From bc26f8568cf356b561a38ec394ef8b4737822413 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:01:08 -0300 Subject: [PATCH 001/423] :robot: Format .jl files (#807) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/discretization/regular.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index 914fd46ba..baa1cca3d 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -104,7 +104,7 @@ function _appendpoles(tg, d, ccw) # connect north pole with triangles north = map(1:(sz[d] - 1)) do j - iᵤ = ntuple(i -> i == d ? j : 1, nd) + iᵤ = ntuple(i -> i == d ? j : 1, nd) iᵥ = ntuple(i -> i == d ? j + 1 : 1, nd) u = cart2corner(tg, iᵤ...) v = cart2corner(tg, iᵥ...) @@ -118,14 +118,14 @@ function _appendpoles(tg, d, ccw) # connect south pole with triangles south = map(1:(sz[d] - 1)) do j - iᵤ = ntuple(i -> i == d ? j : sz[i] + 1, nd) + iᵤ = ntuple(i -> i == d ? j : sz[i] + 1, nd) iᵥ = ntuple(i -> i == d ? j + 1 : sz[i] + 1, nd) u = cart2corner(tg, iᵤ...) v = cart2corner(tg, iᵥ...) connect((s, swap(v, u)...)) end iᵤ = ntuple(i -> i == d ? sz[d] : sz[i] + 1, nd) - iᵥ = ntuple(i -> i == d ? 1 : sz[i] + 1, nd) + iᵥ = ntuple(i -> i == d ? 1 : sz[i] + 1, nd) u = cart2corner(tg, iᵤ...) v = cart2corner(tg, iᵥ...) push!(south, connect((s, swap(v, u)...))) From 315d497152956aed7dea92391a4653b5b29def07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 11:20:19 -0300 Subject: [PATCH 002/423] Fix typo in ext/geometryset.jl --- ext/geometryset.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 37764a441..512d615ba 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -62,7 +62,7 @@ end function vizgset1D!(plot, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) - showfactes1D!(plot, geoms) + showfacets1D!(plot, geoms) end function vizgset1D!(plot, geoms::ObservableVector{<:Ray}, colorant) @@ -77,13 +77,13 @@ function vizgset1D!(plot, geoms::ObservableVector{<:Ray}, colorant) directions = Makie.@lift [asmakie(ray(1) - ray(0)) for ray in $geoms] Makie.arrows!(plot, origins, directions, color=colorant) - showfactes1D!(plot, geoms) + showfacets1D!(plot, geoms) end function vizgset2D!(plot, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) - showfactes2D!(plot, geoms) + showfacets2D!(plot, geoms) end const PolygonLike{Dim,T} = Union{Polygon{Dim,T},MultiPolygon{Dim,T}} @@ -104,7 +104,7 @@ function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) Makie.poly!(plot, polys, color=colors) end - showfactes2D!(plot, geoms) + showfacets2D!(plot, geoms) end function vizgset3D!(plot, geoms, colorant) @@ -112,7 +112,7 @@ function vizgset3D!(plot, geoms, colorant) vizmany!(plot, meshes, colorant) end -function showfactes1D!(plot, geoms) +function showfacets1D!(plot, geoms) showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] pointsize = plot[:pointsize] @@ -125,7 +125,7 @@ function showfactes1D!(plot, geoms) end end -function showfactes2D!(plot, geoms) +function showfacets2D!(plot, geoms) showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] segmentsize = plot[:segmentsize] From 915390fb56f8e97d65cc6e05140192d2afa0789b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 11:20:29 -0300 Subject: [PATCH 003/423] Improve default Vec size in viz --- ext/vector.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/vector.jl b/ext/vector.jl index 369c06d52..03456c3c6 100644 --- a/ext/vector.jl +++ b/ext/vector.jl @@ -7,16 +7,16 @@ function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,T}}}}) where {Dim, color = plot[:color] alpha = plot[:alpha] colorscheme = plot[:colorscheme] + segmentsize = plot[:segmentsize] - if Dim ∉ (2, 3) - error("not implemented") - end + Dim ∈ (2, 3) || error("not implemented") # process color spec into colorant colorant = Makie.@lift process($color, $colorscheme, $alpha) # visualize as built-in arrows - origins = Makie.@lift fill(zero(Makie.Point{Dim,T}), length($vecs)) - directions = Makie.@lift asmakie.($vecs) - Makie.arrows!(plot, origins, directions, color=colorant) + orig = Makie.@lift fill(zero(Makie.Point{Dim,T}), length($vecs)) + dirs = Makie.@lift asmakie.($vecs) + size = Makie.@lift 0.1 * $segmentsize + Makie.arrows!(plot, orig, dirs, color=colorant, arrowsize=size) end From a2bf85d57682ca28d0df7c648c29618bf5e34565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 11:25:56 -0300 Subject: [PATCH 004/423] Improve default Ray size in viz --- ext/geometryset.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 512d615ba..9663ae56e 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -67,15 +67,17 @@ end function vizgset1D!(plot, geoms::ObservableVector{<:Ray}, colorant) rset = plot[:object] + segmentsize = plot[:segmentsize] - if embeddim(rset[]) ∉ (2, 3) - error("not implemented") - end + Dim = embeddim(rset[]) + + Dim ∈ (2, 3) || error("not implemented") # visualize as built-in arrows - origins = Makie.@lift [asmakie(ray(0)) for ray in $geoms] - directions = Makie.@lift [asmakie(ray(1) - ray(0)) for ray in $geoms] - Makie.arrows!(plot, origins, directions, color=colorant) + orig = Makie.@lift [asmakie(ray(0)) for ray in $geoms] + dirs = Makie.@lift [asmakie(ray(1) - ray(0)) for ray in $geoms] + size = Makie.@lift 0.1 * $segmentsize + Makie.arrows!(plot, orig, dirs, color=colorant, arrowsize=size) showfacets1D!(plot, geoms) end From 4f03eb11251135ba5764b772287fbe1182453c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 12:24:37 -0300 Subject: [PATCH 005/423] Implement boundingbox for *Surface types --- src/boundingboxes.jl | 16 +++++++++++++++- test/boundingboxes.jl | 28 ++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index e1088042e..1c8175ad8 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -39,13 +39,27 @@ function boundingbox(r::Ray{Dim,T}) where {Dim,T} Box(Point(l), Point(u)) end -function boundingbox(s::Sphere{Dim,T}) where {Dim,T} +function boundingbox(s::Sphere{Dim}) where {Dim} c = center(s) r = radius(s) r⃗ = Vec(ntuple(i -> r, Dim)) Box(c - r⃗, c + r⃗) end +function boundingbox(c::CylinderSurface) + us = (0, 1/4, 1/2, 3/4) + vs = (0, 1/2, 1) + ps = [c(u, v) for (u, v) in Iterators.product(us, vs)] + boundingbox(ps) +end + +function boundingbox(c::ConeSurface) + us = (0, 1/4, 1/2, 3/4) + vs = (1,) + ps = [c(u, v) for (u, v) in Iterators.product(us, vs)] + boundingbox([ps; apex(c)]) +end + function boundingbox(p::ParaboloidSurface{T}) where {T} v = apex(p) r = radius(p) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index da621f1f5..fb8c33122 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -20,6 +20,13 @@ @test boundingbox(r) == Box(P2(-1, T(-Inf)), P2(T(Inf), 1)) @test @allocated(boundingbox(r)) < 50 + b = Ball(P2(0, 0), T(1)) + @test boundingbox(b) == Box(P2(-1, -1), P2(1, 1)) + @test @allocated(boundingbox(b)) < 50 + b = Ball(P2(1, 1), T(1)) + @test boundingbox(b) == Box(P2(0, 0), P2(2, 2)) + @test @allocated(boundingbox(b)) < 50 + s = Sphere(P2(0, 0), T(1)) @test boundingbox(s) == Box(P2(-1, -1), P2(1, 1)) @test @allocated(boundingbox(s)) < 50 @@ -27,12 +34,21 @@ @test boundingbox(s) == Box(P2(0, 0), P2(2, 2)) @test @allocated(boundingbox(s)) < 50 - b = Ball(P2(0, 0), T(1)) - @test boundingbox(b) == Box(P2(-1, -1), P2(1, 1)) - @test @allocated(boundingbox(b)) < 50 - b = Ball(P2(1, 1), T(1)) - @test boundingbox(b) == Box(P2(0, 0), P2(2, 2)) - @test @allocated(boundingbox(b)) < 50 + c = Cylinder(T(1)) + b = boundingbox(c) + @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + + c = CylinderSurface(T(1)) + b = boundingbox(c) + @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + + c = Cone(Disk(Plane(P3(0,0,0), V3(0,0,1)), T(1)), P3(0,0,1)) + b = boundingbox(c) + @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + + c = ConeSurface(Disk(Plane(P3(0,0,0), V3(0,0,1)), T(1)), P3(0,0,1)) + b = boundingbox(c) + @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) b = Box(P2(-3, -1), P2(0.5, 0.5)) s = Sphere(P2(0, 0), T(2)) From a0b0685b0b5d4a8ae13d8589ba689eba024ffd94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:33:26 -0300 Subject: [PATCH 006/423] :robot: Format .jl files (#808) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/boundingboxes.jl | 6 +++--- test/boundingboxes.jl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 1c8175ad8..61a4036b1 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -47,14 +47,14 @@ function boundingbox(s::Sphere{Dim}) where {Dim} end function boundingbox(c::CylinderSurface) - us = (0, 1/4, 1/2, 3/4) - vs = (0, 1/2, 1) + us = (0, 1 / 4, 1 / 2, 3 / 4) + vs = (0, 1 / 2, 1) ps = [c(u, v) for (u, v) in Iterators.product(us, vs)] boundingbox(ps) end function boundingbox(c::ConeSurface) - us = (0, 1/4, 1/2, 3/4) + us = (0, 1 / 4, 1 / 2, 3 / 4) vs = (1,) ps = [c(u, v) for (u, v) in Iterators.product(us, vs)] boundingbox([ps; apex(c)]) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index fb8c33122..6981115de 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -42,11 +42,11 @@ b = boundingbox(c) @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) - c = Cone(Disk(Plane(P3(0,0,0), V3(0,0,1)), T(1)), P3(0,0,1)) + c = Cone(Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)), P3(0, 0, 1)) b = boundingbox(c) @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) - c = ConeSurface(Disk(Plane(P3(0,0,0), V3(0,0,1)), T(1)), P3(0,0,1)) + c = ConeSurface(Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)), P3(0, 0, 1)) b = boundingbox(c) @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) From 59e3e5abeab519daa4bf73c1f59d73c848a2f2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 12:34:36 -0300 Subject: [PATCH 007/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 18616e8bd..4869ca4be 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.40.12" +version = "0.41.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 4572f0cd34c532190fb61a837379eb4ee2386922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Mar 2024 12:44:33 -0300 Subject: [PATCH 008/423] Update docs --- docs/src/algorithms/clamping.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/algorithms/clamping.md b/docs/src/algorithms/clamping.md index 97f52e53e..c3c5c51d4 100644 --- a/docs/src/algorithms/clamping.md +++ b/docs/src/algorithms/clamping.md @@ -3,8 +3,8 @@ Meshes adds methods to Julia's built-in `clamp` function. The additional methods clamp points to the edges of a box in any number of dimensions. The target points and boxes must have the same number of dimensions and the same numeric type. ```@docs -clamp(point::Point{Dim,T}, box::Box{Dim,T}) where {Dim,T} -clamp(points::PointSet{Dim,T}, box::Box{Dim,T}) where {Dim,T} +clamp(::Point, ::Box) +clamp(::PointSet, ::Box) ``` ```@example clamping @@ -28,4 +28,4 @@ ax = Mke.Axis(fig[1,2], title="clamped", aspect=1, limits=(0,1,0,1)) viz!(ax, box) viz!(ax, clamped, color=:black, pointsize=6) fig -``` \ No newline at end of file +``` From 509bb5b577f93f480f2af8257c4139c786ff20bd Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 26 Mar 2024 09:47:02 -0300 Subject: [PATCH 009/423] Add 'isperiodic' for 'Grid' & Remove 'isopen' & Remove 'isclosed(::GridTopology)' method (#811) --- src/predicates/isclosed.jl | 8 -------- src/predicates/isperiodic.jl | 6 +++--- src/topologies/grid.jl | 8 +++++++- test/predicates.jl | 3 +++ 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/predicates/isclosed.jl b/src/predicates/isclosed.jl index 4f55ed21f..fd683a1e7 100644 --- a/src/predicates/isclosed.jl +++ b/src/predicates/isclosed.jl @@ -16,11 +16,3 @@ isclosed(::Type{<:Segment}) = false isclosed(::Type{<:Rope}) = false isclosed(::Type{<:Ring}) = true - -""" - isclosed(topology) - -Tells whether or not the `topology` is closed -along each parametric dimension. -""" -isclosed(t::GridTopology) = .!isopen(t) diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index d0eeffd69..f2fee0a90 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -45,9 +45,9 @@ isperiodic(::Type{<:Quadrangle}) = (false, false) isperiodic(::Type{<:Hexahedron}) = (false, false, false) """ - isperiodic(topology) + isperiodic(grid) -Tells whether or not the `topology` is periodic +Tells whether or not the `grid` is periodic along each parametric dimension. """ -isperiodic(t::GridTopology) = isclosed(t) +isperiodic(g::Grid) = isperiodic(topology(g)) diff --git a/src/topologies/grid.jl b/src/topologies/grid.jl index c82d43130..e5db134a5 100644 --- a/src/topologies/grid.jl +++ b/src/topologies/grid.jl @@ -36,7 +36,13 @@ paramdim(::GridTopology{D}) where {D} = D Base.size(t::GridTopology) = t.dims -isopen(t::GridTopology) = t.open +""" + isperiodic(topology) + +Tells whether or not the `topology` is periodic +along each parametric dimension. +""" +isperiodic(t::GridTopology) = .!t.open """ elem2cart(t, e) diff --git a/test/predicates.jl b/test/predicates.jl index ae09886de..28c1ed229 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -151,6 +151,9 @@ @test isperiodic(Segment) == (false,) @test isperiodic(Quadrangle) == (false, false) @test isperiodic(Hexahedron) == (false, false, false) + + @test isperiodic(CartesianGrid{T}(10, 10)) == (false, false) + @test isperiodic(CartesianGrid{T}(10, 10, 10)) == (false, false, false) end @testset "in" begin From 0ed2202ec3c1bca16c4d151409a482e9e521ddda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 26 Mar 2024 09:47:27 -0300 Subject: [PATCH 010/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4869ca4be..aaa441455 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.41.0" +version = "0.41.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From b3e64401ba4f114c7a9b70736198716a8d04ca23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 28 Mar 2024 09:07:31 -0300 Subject: [PATCH 011/423] Rename colorscheme --> colormap in viz --- ext/MeshesMakieExt.jl | 2 +- ext/colors.jl | 2 +- ext/geometryset.jl | 8 ++++---- ext/grid/cartesian.jl | 8 ++++---- ext/grid/rectilinear.jl | 4 ++-- ext/grid/structured.jl | 4 ++-- ext/grid/transformed.jl | 4 ++-- ext/simplemesh.jl | 8 ++++---- ext/subcartesiangrid.jl | 4 ++-- ext/utils.jl | 4 ++-- ext/vector.jl | 4 ++-- src/viz.jl | 8 ++++---- 12 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 907ad65d4..1fd325d44 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -23,7 +23,7 @@ Makie.@recipe(Viz, object) do scene Makie.Attributes( color=:slategray3, alpha=nothing, - colorscheme=nothing, + colormap=nothing, pointsize=4, segmentsize=1.5, showfacets=false, diff --git a/ext/colors.jl b/ext/colors.jl index e67714fa3..3da1ab75e 100644 --- a/ext/colors.jl +++ b/ext/colors.jl @@ -16,7 +16,7 @@ ascolorscheme(name::Symbol) = cgrad(name) ascolorscheme(name::AbstractString) = ascolorscheme(Symbol(name)) ascolorscheme(scheme) = scheme -# default colorscheme for vector of values +# default color scheme for vector of values defaultscheme(values) = cgrad(:viridis) # add transparency to colors diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 9663ae56e..bf5a4f285 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -6,10 +6,10 @@ function Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) gset = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # get geometries geoms = Makie.@lift parent($gset) @@ -38,11 +38,11 @@ function Makie.plot!(plot::Viz{<:Tuple{PointSet}}) pset = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] pointsize = plot[:pointsize] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # get geometries and coordinates geoms = Makie.@lift parent($pset) diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index c8c00ee9d..7663af3b0 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -6,13 +6,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) @@ -64,13 +64,13 @@ function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 8ed163014..c592c9487 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -6,13 +6,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index f456a8513..9310abf96 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -6,13 +6,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 992e1c9c7..500fde86f 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -25,11 +25,11 @@ function transformedgrid!(plot, fallback) if isoptimized(trans[]) color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] - viz!(plot, grid; color, alpha, colorscheme, segmentsize, showfacets, facetcolor) + viz!(plot, grid; color, alpha, colormap, segmentsize, showfacets, facetcolor) makietransform!(plot, trans[]) else fallback(tgrid) diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index f059779a3..6817225a1 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -20,11 +20,11 @@ function vizmesh1D!(plot) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # retrieve coordinates of segments coords = Makie.@lift let @@ -51,13 +51,13 @@ function vizmesh2D!(plot) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # retrieve triangle mesh parameters tparams = Makie.@lift let diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index d0686a914..26a0c5aa3 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -8,10 +8,10 @@ function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) subgrid = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # retrieve grid paramaters gparams = Makie.@lift let diff --git a/ext/utils.jl b/ext/utils.jl index 78ceb425e..6ae4bc473 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -17,7 +17,7 @@ concat(mesh₁::Mesh, mesh₂::Mesh) = merge(mesh₁, mesh₂) function vizmany!(plot, objs, color) alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] pointsize = plot[:pointsize] segmentsize = plot[:segmentsize] @@ -25,5 +25,5 @@ function vizmany!(plot, objs, color) colors = Makie.@lift mayberepeat($color, $objs) alphas = Makie.@lift mayberepeat($alpha, $objs) - viz!(plot, object, color=colors, alpha=alphas, colorscheme=colorscheme, pointsize=pointsize, segmentsize=segmentsize) + viz!(plot, object, color=colors, alpha=alphas, colormap=colormap, pointsize=pointsize, segmentsize=segmentsize) end diff --git a/ext/vector.jl b/ext/vector.jl index 03456c3c6..ded8dfe79 100644 --- a/ext/vector.jl +++ b/ext/vector.jl @@ -6,13 +6,13 @@ function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,T}}}}) where {Dim, vecs = plot[:object] color = plot[:color] alpha = plot[:alpha] - colorscheme = plot[:colorscheme] + colormap = plot[:colormap] segmentsize = plot[:segmentsize] Dim ∈ (2, 3) || error("not implemented") # process color spec into colorant - colorant = Makie.@lift process($color, $colorscheme, $alpha) + colorant = Makie.@lift process($color, $colormap, $alpha) # visualize as built-in arrows orig = Makie.@lift fill(zero(Makie.Point{Dim,T}), length($vecs)) diff --git a/src/viz.jl b/src/viz.jl index 61db03f0f..35d9ca8ed 100644 --- a/src/viz.jl +++ b/src/viz.jl @@ -11,7 +11,7 @@ Visualize Meshes.jl `object` with various `options`. * `color` - color of geometries * `alpha` - transparency in [0,1] -* `colorscheme` - color scheme from ColorSchemes.jl +* `colormap` - color scheme/map from ColorSchemes.jl * `pointsize` - size of points in point set * `segmentsize` - size (or width) of segments * `showfacets` - enable visualization of facets @@ -63,10 +63,10 @@ scene with `options` forwarded to [`viz`](@ref). function viz! end """ - ascolors(values, colorscheme) + ascolors(values, colormap) Convert vector of `values` to Colors.jl, -using `colorscheme` from ColorSchemes.jl. +using `colormap` from ColorSchemes.jl. ### Notes @@ -80,7 +80,7 @@ function ascolors end """ defaultscheme(values) -Return default colorscheme for `values`. +Return default colormap for `values`. ### Notes From 2ccda6b2b848e604c048a3b848da99065ec8d4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 28 Mar 2024 10:21:44 -0300 Subject: [PATCH 012/423] Add RegularRefinement --- src/Meshes.jl | 1 + src/refinement.jl | 1 + src/refinement/regular.jl | 103 ++++++++++++++++++++++++++++++++++++++ test/refinement.jl | 24 +++++++++ 4 files changed, 129 insertions(+) create mode 100644 src/refinement/regular.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 1643fd4cd..00c064cb5 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -482,6 +482,7 @@ export RefinementMethod, TriRefinement, QuadRefinement, + RegularRefinement, CatmullClark, TriSubdivision, refine, diff --git a/src/refinement.jl b/src/refinement.jl index 954f9b262..271a4e284 100644 --- a/src/refinement.jl +++ b/src/refinement.jl @@ -22,5 +22,6 @@ function refine end include("refinement/tri.jl") include("refinement/quad.jl") +include("refinement/regular.jl") include("refinement/catmullclark.jl") include("refinement/trisubdivision.jl") diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl new file mode 100644 index 000000000..b298c29ae --- /dev/null +++ b/src/refinement/regular.jl @@ -0,0 +1,103 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + RegularRefinement(f₁, f₂, ..., fₙ) + +Refine each dimension of the grid by given factors `f₁`, `f₂`, ..., `fₙ`. + +## Examples + +```julia +refine(grid2D, RegularRefinement(2, 3)) +refine(grid3D, RegularRefinement(2, 3, 1)) +``` +""" +struct RegularRefinement{N} <: RefinementMethod + factors::Dims{N} +end + +RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors) + +function refine(grid::CartesianGrid{Dim}, method::RegularRefinement) where {Dim} + factors = fitdims(method.factors, Dim) + CartesianGrid(minimum(grid), maximum(grid), dims = size(grid) .* factors) +end + +function refine(grid::RectilinearGrid{Dim}, method::RegularRefinement) where {Dim} + factors = fitdims(method.factors, Dim) + xyzₛ = xyz(grid) + xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), Dim) + RectilinearGrid(xyzₜ) +end + +function refine(grid::StructuredGrid{Dim}, method::RegularRefinement) where {Dim} + factors = fitdims(method.factors, Dim) + XYZ′ = _XYZ(grid, factors) + StructuredGrid(XYZ′) +end + +function _refinedims(x, f) + x′ = mapreduce(vcat, 1:(length(x) - 1)) do i + range(x[i], x[i + 1], f + 1)[begin:(end - 1)] + end + push!(x′, last(x)) + x′ +end + +function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) + fᵢ, fⱼ = factors + sᵢ, sⱼ = size(grid) + us = 0:(1 / fᵢ):1 + vs = 0:(1 / fⱼ):1 + catᵢ(A...) = cat(A..., dims=Val(1)) + catⱼ(A...) = cat(A..., dims=Val(2)) + + mat(quad) = [coordinates(quad(u, v)) for u in us, v in vs] + M = [mat(grid[i, j]) for i in 1:sᵢ, j in 1:sⱼ] + + C = mapreduce(catⱼ, 1:sⱼ) do j + Mⱼ = mapreduce(catᵢ, 1:sᵢ) do i + Mᵢⱼ = M[i, j] + i == sᵢ ? Mᵢⱼ : Mᵢⱼ[begin:(end - 1), :] + end + j == sⱼ ? Mⱼ : Mⱼ[:, begin:(end - 1)] + end + + X = getindex.(C, 1) + Y = getindex.(C, 2) + + (X, Y) +end + +function _XYZ(grid::StructuredGrid{3}, factors::Dims{3}) + fᵢ, fⱼ, fₖ = factors + sᵢ, sⱼ, sₖ = size(grid) + us = 0:(1 / fᵢ):1 + vs = 0:(1 / fⱼ):1 + ws = 0:(1 / fₖ):1 + catᵢ(A...) = cat(A..., dims=Val(1)) + catⱼ(A...) = cat(A..., dims=Val(2)) + catₖ(A...) = cat(A..., dims=Val(3)) + + mat(hex) = [coordinates(hex(u, v, w)) for u in us, v in vs, w in ws] + M = [mat(grid[i, j, k]) for i in 1:sᵢ, j in 1:sⱼ, k in 1:sₖ] + + C = mapreduce(catₖ, 1:sₖ) do k + Mₖ = mapreduce(catⱼ, 1:sⱼ) do j + Mⱼₖ = mapreduce(catᵢ, 1:sᵢ) do i + Mᵢⱼₖ = M[i, j, k] + i == sᵢ ? Mᵢⱼₖ : Mᵢⱼₖ[begin:(end - 1), :, :] + end + j == sⱼ ? Mⱼₖ : Mⱼₖ[:, begin:(end - 1), :] + end + k == sₖ ? Mₖ : Mₖ[:, :, begin:(end - 1)] + end + + X = getindex.(C, 1) + Y = getindex.(C, 2) + Z = getindex.(C, 3) + + (X, Y, Z) +end diff --git a/test/refinement.jl b/test/refinement.jl index e72e89a41..f32baaae9 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -30,6 +30,30 @@ end end + @testset "RegularRefinement" begin + # 2D grids + grid = CartesianGrid(P2(0, 0), P2(10, 10), dims=(10, 10)) + tgrid = CartesianGrid(P2(0, 0), P2(10, 10), dims=(20, 20)) + @test refine(grid, RegularRefinement(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test refine(rgrid, RegularRefinement(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test refine(sgrid, RegularRefinement(2)) == tsgrid + + # 3D grids + grid = CartesianGrid{T}(3, 3, 3) + tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(6, 6, 6)) + @test refine(grid, RegularRefinement(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test refine(rgrid, RegularRefinement(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test refine(sgrid, RegularRefinement(2)) == tsgrid + end + @testset "CatmullClark" begin points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) From 1a5c7eb20fb3e2a65115d752b44e538e58b02449 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:26:46 -0300 Subject: [PATCH 013/423] :robot: Format .jl files (#815) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/refinement/regular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index b298c29ae..7325efca8 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -22,7 +22,7 @@ RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors) function refine(grid::CartesianGrid{Dim}, method::RegularRefinement) where {Dim} factors = fitdims(method.factors, Dim) - CartesianGrid(minimum(grid), maximum(grid), dims = size(grid) .* factors) + CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end function refine(grid::RectilinearGrid{Dim}, method::RegularRefinement) where {Dim} From 0afd192d898d565b1bcecc9826ac54fdf0491c6a Mon Sep 17 00:00:00 2001 From: cserteGT3 Date: Thu, 28 Mar 2024 14:52:28 +0100 Subject: [PATCH 014/423] add functions for FrustumSurface discretization (#814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * functions for FrustumSurface discretization * Minor adjustments --------- Co-authored-by: Júlio Hoffimann --- src/discretization.jl | 2 ++ src/discretization/regular.jl | 2 ++ src/predicates/isparametrized.jl | 2 ++ src/predicates/isperiodic.jl | 2 ++ src/primitives/frustumsurface.jl | 30 ++++++++++++++++++++++++++++++ src/sampling/regular.jl | 4 ++++ test/primitives.jl | 1 + 7 files changed, 43 insertions(+) diff --git a/src/discretization.jl b/src/discretization.jl index 97944f527..4e49a2a65 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -121,6 +121,8 @@ discretize(cylsurf::CylinderSurface) = discretize(cylsurf, RegularDiscretization discretize(consurf::ConeSurface) = discretize(consurf, RegularDiscretization(50, 2)) +discretize(frustsurf::FrustumSurface) = discretize(frustsurf, RegularDiscretization(50, 2)) + discretize(parsurf::ParaboloidSurface) = discretize(parsurf, RegularDiscretization(50)) discretize(multi::Multi) = mapreduce(discretize, merge, parent(multi)) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index baa1cca3d..11c9f89c5 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -61,6 +61,8 @@ appendtopo(::CylinderSurface, tg) = _appendpoles(tg, 1, false) appendtopo(::ConeSurface, tg) = _appendpoles(tg, 1, false) +appendtopo(::FrustumSurface, tg) = _appendpoles(tg, 1, false) + function _appendcenter(tg) # auxiliary variables _, ny = size(tg) diff --git a/src/predicates/isparametrized.jl b/src/predicates/isparametrized.jl index 6a49b889d..b848fdb2d 100644 --- a/src/predicates/isparametrized.jl +++ b/src/predicates/isparametrized.jl @@ -44,6 +44,8 @@ isparametrized(::Type{<:CylinderSurface}) = true isparametrized(::Type{<:ConeSurface}) = true +isparametrized(::Type{<:FrustumSurface}) = true + isparametrized(::Type{<:ParaboloidSurface}) = true isparametrized(::Type{<:Torus}) = true diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index f2fee0a90..e0622d887 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -34,6 +34,8 @@ isperiodic(::Type{<:CylinderSurface}) = (true, false) isperiodic(::Type{<:ConeSurface}) = (true, false) +isperiodic(::Type{<:FrustumSurface}) = (true, false) + isperiodic(::Type{<:ParaboloidSurface}) = (false, true) isperiodic(::Type{<:Torus}) = (true, true) diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 9e7d7d9bc..e43279900 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -25,12 +25,42 @@ end FrustumSurface(bot::Disk{T}, top::Disk{T}) where {T} = FrustumSurface{T}(bot, top) +paramdim(::Type{<:FrustumSurface}) = 2 + bottom(f::FrustumSurface) = f.bot top(f::FrustumSurface) = f.top height(f::FrustumSurface) = norm(center(bottom(f)) - center(top(f))) +axis(f::FrustumSurface) = Line(center(bottom(f)), center(top(f))) + +function (f::FrustumSurface{T})(φ, z) where {T} + if (φ < 0 || φ > 1) || (z < 0 || z > 1) + throw(DomainError((φ, z), "f(φ, z) is not defined for φ, z outside [0, 1]².")) + end + rb = radius(bottom(f)) + rt = radius(top(f)) + a = axis(f) + d = a(1) - a(0) + l = norm(d) + + # rotation to align z axis with cylinder axis + Q = rotation_between(d, Vec{3,T}(0, 0, 1)) + + # scale coordinates + φₛ = 2T(π) * φ + zₛ = z * l + + # local coordinates, that will be transformed with rotation and position of the FrustumSurface + x = cos(φₛ) * (rb * (l - zₛ) + rt * zₛ) / l + y = sin(φₛ) * (rb * (l - zₛ) + rt * zₛ) / l + z = zₛ + p = Vec{3,T}(x, y, z) + + center(bottom(f)) + Q' * p +end + function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{FrustumSurface{T}}) where {T} bottom = rand(rng, Disk{T}) ax = normal(plane(bottom)) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index afb8b25d4..1c7188ce9 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -64,6 +64,10 @@ firstoffset(::ConeSurface) = (n -> zero(n), n -> inv(n)) lastoffset(::ConeSurface) = (n -> inv(n), n -> zero(n)) extrapoints(c::ConeSurface) = (apex(c), base(c)(0, 0)) +firstoffset(::FrustumSurface) = (n -> zero(n), n -> zero(n)) +lastoffset(::FrustumSurface) = (n -> inv(n), n -> zero(n)) +extrapoints(c::FrustumSurface) = (bottom(c)(0, 0), top(c)(0, 0)) + # -------------- # SPECIAL CASES # -------------- diff --git a/test/primitives.jl b/test/primitives.jl index 439eaa129..f2ea35741 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1132,6 +1132,7 @@ dt = Disk(pt, T(2)) f = FrustumSurface(db, dt) @test embeddim(f) == 3 + @test paramdim(f) == 2 @test coordtype(f) == T @test isnothing(boundary(f)) From 8167e36d9d39a589f45e6cb8424d557554c196d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 28 Mar 2024 11:48:15 -0300 Subject: [PATCH 015/423] Add missing Frustum methods --- docs/src/algorithms/refinement.md | 22 +++++++++++ docs/src/geometries/primitives.md | 61 +++++++++---------------------- src/boundary.jl | 18 ++++----- src/primitives.jl | 2 +- src/primitives/frustum.jl | 6 ++- 5 files changed, 54 insertions(+), 55 deletions(-) diff --git a/docs/src/algorithms/refinement.md b/docs/src/algorithms/refinement.md index 616754fee..7261a76f5 100644 --- a/docs/src/algorithms/refinement.md +++ b/docs/src/algorithms/refinement.md @@ -54,6 +54,28 @@ viz(fig[2,2], ref3, showfacets = true) fig ``` +## RegularRefinement + +```@docs +RegularRefinement +``` + +```@example refinement +grid = CartesianGrid(10, 10) + +# refine three times +ref1 = refine(grid, RegularRefinement(2, 2)) +ref2 = refine(ref1, RegularRefinement(3, 2)) +ref3 = refine(ref2, RegularRefinement(2, 3)) + +fig = Mke.Figure(size = (800, 800)) +viz(fig[1,1], grid, showfacets = true) +viz(fig[1,2], ref1, showfacets = true) +viz(fig[2,1], ref2, showfacets = true) +viz(fig[2,2], ref3, showfacets = true) +fig +``` + ## Catmull-Clark ```@docs diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 8f6420542..bc180a2d0 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -68,80 +68,58 @@ Box Box((0.,0.,0.), (1.,1.,1.)) |> viz ``` -### Ball +### Ball/Sphere ```@docs Ball -``` - -```@example primitives -Ball((0.,0.,0.), 1.) |> viz -``` - -### Sphere - -```@docs Sphere ``` ```@example primitives -Sphere((0.,0.,0.), 1.) |> viz +Ball((0.,0.,0.), 1.) |> viz ``` -### Disk +### Disk/Circle ```@docs Disk -``` - -### Circle - -```@docs Circle ``` -### Cylinder +### Cylinder/CylinderSurface ```@docs Cylinder -``` - -```@example primitives -Cylinder(1.0) |> viz -``` - -### CylinderSurface - -```@docs CylinderSurface ``` ```@example primitives -CylinderSurface(1.0) |> viz +Cylinder(1.0) |> viz ``` -### Cone +### Cone/ConeSurface ```@docs Cone +ConeSurface ``` -### ConeSurface - -```@docs -ConeSurface +```@example primitives +Cone(Disk(Plane((0,0,0), (0,0,1)), 1), (0,0,1)) |> viz ``` -### Frustum +### Frustum/FrustumSurface ```@docs Frustum +FrustumSurface ``` -### FrustumSurface - -```@docs -FrustumSurface +```@example primitives +Frustum( + Disk(Plane((0,0,0), (0,0,1)), 2), + Disk(Plane((0,0,10), (0,0,1)), 1) +) |> viz ``` ### Torus @@ -161,10 +139,5 @@ ParaboloidSurface ``` ```@example primitives -a = Point3(5, 2, 4) -r = 1.0 -f = 0.25 -par = ParaboloidSurface(a, r, f) -disk = Disk(Plane(a, Vec(0, 0, 1)), r) -viz([par, disk], color = [:green, :gray]) +ParaboloidSurface((5., 2., 4.), 1.0, 0.25) |> viz ``` diff --git a/src/boundary.jl b/src/boundary.jl index 1d7826f45..8bb4af124 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -58,25 +58,20 @@ boundary(d::Disk) = Circle(plane(d), radius(d)) boundary(::Circle) = nothing -boundary(c::Cone) = ConeSurface(base(c), apex(c)) - -boundary(::ConeSurface) = nothing - boundary(c::Cylinder) = CylinderSurface(bottom(c), top(c), radius(c)) boundary(::CylinderSurface) = nothing -boundary(::ParaboloidSurface) = nothing +boundary(c::Cone) = ConeSurface(base(c), apex(c)) -function boundary(p::Pyramid) - indices = [(4, 3, 2, 1), (5, 1, 2), (5, 4, 1), (5, 3, 4), (5, 2, 3)] - SimpleMesh(pointify(p), connect.(indices)) -end +boundary(::ConeSurface) = nothing boundary(f::Frustum) = FrustumSurface(bottom(f), top(f)) boundary(::FrustumSurface) = nothing +boundary(::ParaboloidSurface) = nothing + boundary(::Torus) = nothing boundary(s::Segment) = Multi(pointify(s)) @@ -100,6 +95,11 @@ function boundary(h::Hexahedron) SimpleMesh(pointify(h), connect.(indices)) end +function boundary(p::Pyramid) + indices = [(4, 3, 2, 1), (5, 1, 2), (5, 4, 1), (5, 3, 4), (5, 2, 3)] + SimpleMesh(pointify(p), connect.(indices)) +end + function boundary(m::Multi) bounds = [boundary(geom) for geom in parent(m)] valid = filter(!isnothing, bounds) diff --git a/src/primitives.jl b/src/primitives.jl index 5953b80ee..dc196dd44 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -36,9 +36,9 @@ include("primitives/disk.jl") include("primitives/circle.jl") include("primitives/cylinder.jl") include("primitives/cylindersurface.jl") -include("primitives/paraboloidsurface.jl") include("primitives/cone.jl") include("primitives/conesurface.jl") include("primitives/frustum.jl") include("primitives/frustumsurface.jl") +include("primitives/paraboloidsurface.jl") include("primitives/torus.jl") diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index 64cd06dc1..6d7224a30 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -25,11 +25,15 @@ end Frustum(bot::Disk{T}, top::Disk{T}) where {T} = Frustum{T}(bot, top) +paramdim(::Type{<:Frustum}) = 3 + bottom(f::Frustum) = f.bot top(f::Frustum) = f.top -height(f::Frustum) = norm(center(bottom(f)) - center(top(f))) +height(f::Frustum) = height(boundary(f)) + +axis(f::Frustum) = axis(boundary(f)) function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Frustum{T}}) where {T} bottom = rand(rng, Disk{T}) From 36ef234eb255b06c2141d32964a10ed8f5766cd0 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 28 Mar 2024 18:04:09 -0300 Subject: [PATCH 016/423] Viz: Add 'colorrange' option (#817) --- ext/MeshesMakieExt.jl | 1 + ext/colors.jl | 15 ++++++++------- ext/geometryset.jl | 6 ++++-- ext/grid/cartesian.jl | 6 ++++-- ext/grid/rectilinear.jl | 3 ++- ext/grid/structured.jl | 3 ++- ext/simplemesh.jl | 6 ++++-- ext/subcartesiangrid.jl | 3 ++- ext/vector.jl | 3 ++- src/viz.jl | 1 + 10 files changed, 30 insertions(+), 17 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 1fd325d44..f7c9ea9ae 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -24,6 +24,7 @@ Makie.@recipe(Viz, object) do scene color=:slategray3, alpha=nothing, colormap=nothing, + colorrange=nothing, pointsize=4, segmentsize=1.5, showfacets=false, diff --git a/ext/colors.jl b/ext/colors.jl index 3da1ab75e..1732f0a35 100644 --- a/ext/colors.jl +++ b/ext/colors.jl @@ -6,10 +6,11 @@ const V{T} = AbstractVector{<:T} # convert value to colorant, optionally using color scheme object -ascolors(values::V{Symbol}, scheme) = ascolors(string.(values), scheme) -ascolors(values::V{AbstractString}, scheme) = parse.(Ref(Colorant), values) -ascolors(values::V{Number}, scheme) = get(scheme, values, :extrema) -ascolors(values::V{Colorant}, scheme) = values +ascolors(values::V{Symbol}, scheme, colorrange) = ascolors(string.(values), scheme, colorrange) +ascolors(values::V{AbstractString}, scheme, colorrange) = parse.(Ref(Colorant), values) +ascolors(values::V{Number}, scheme, colorrange) = + isnothing(colorrange) ? get(scheme, values, :extrema) : get(scheme, values, colorrange) +ascolors(values::V{Colorant}, scheme, colorrange) = values # convert color scheme name to color scheme object ascolorscheme(name::Symbol) = cgrad(name) @@ -28,7 +29,7 @@ setalpha(colors, ::Nothing) = colors # -------------------------------- # convert user input to colors -function process(values::V, scheme, alphas) +function process(values::V, scheme, colorrange, alphas) # find invalid and valid indices isinvalid(v) = ismissing(v) || (v isa Number && isnan(v)) iinds = findall(isinvalid, values) @@ -40,7 +41,7 @@ function process(values::V, scheme, alphas) # valid values are assigned colors from scheme vals = coalesce.(values[vinds]) vscheme = isnothing(scheme) ? defaultscheme(vals) : ascolorscheme(scheme) - vcolors = setalpha(ascolors(vals, vscheme), alphas) + vcolors = setalpha(ascolors(vals, vscheme, colorrange), alphas) # build final vector of colors colors = Vector{Colorant}(undef, length(values)) @@ -50,4 +51,4 @@ function process(values::V, scheme, alphas) colors end -process(value, scheme, alphas) = process([value], scheme, alphas) |> first +process(value, scheme, colorrange, alphas) = process([value], scheme, colorrange, alphas) |> first diff --git a/ext/geometryset.jl b/ext/geometryset.jl index bf5a4f285..6fd7284e6 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -7,9 +7,10 @@ function Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # get geometries geoms = Makie.@lift parent($gset) @@ -39,10 +40,11 @@ function Makie.plot!(plot::Viz{<:Tuple{PointSet}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] pointsize = plot[:pointsize] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # get geometries and coordinates geoms = Makie.@lift parent($pset) diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index 7663af3b0..db163241d 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -7,12 +7,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) @@ -65,12 +66,13 @@ function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index c592c9487..277173597 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -7,12 +7,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 9310abf96..6adf43b36 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -7,12 +7,13 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # number of vertices and colors nv = Makie.@lift nvertices($grid) diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index 6817225a1..57e8c4e77 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -21,10 +21,11 @@ function vizmesh1D!(plot) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # retrieve coordinates of segments coords = Makie.@lift let @@ -52,12 +53,13 @@ function vizmesh2D!(plot) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] showfacets = plot[:showfacets] facetcolor = plot[:facetcolor] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # retrieve triangle mesh parameters tparams = Makie.@lift let diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index 26a0c5aa3..361d2d320 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -9,9 +9,10 @@ function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # retrieve grid paramaters gparams = Makie.@lift let diff --git a/ext/vector.jl b/ext/vector.jl index ded8dfe79..3f13a20b1 100644 --- a/ext/vector.jl +++ b/ext/vector.jl @@ -7,12 +7,13 @@ function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,T}}}}) where {Dim, color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + colorrange = plot[:colorrange] segmentsize = plot[:segmentsize] Dim ∈ (2, 3) || error("not implemented") # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $alpha) + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # visualize as built-in arrows orig = Makie.@lift fill(zero(Makie.Point{Dim,T}), length($vecs)) diff --git a/src/viz.jl b/src/viz.jl index 35d9ca8ed..ee2954da9 100644 --- a/src/viz.jl +++ b/src/viz.jl @@ -12,6 +12,7 @@ Visualize Meshes.jl `object` with various `options`. * `color` - color of geometries * `alpha` - transparency in [0,1] * `colormap` - color scheme/map from ColorSchemes.jl +* `colorrange` - minimum and maximum color values * `pointsize` - size of points in point set * `segmentsize` - size (or width) of segments * `showfacets` - enable visualization of facets From 6df796a1f581145efbd40bea35284b77600943bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 30 Mar 2024 10:59:49 -0300 Subject: [PATCH 017/423] Add RegularCoarsening --- src/Meshes.jl | 6 ++++++ src/coarsening.jl | 23 ++++++++++++++++++++ src/coarsening/regular.jl | 44 +++++++++++++++++++++++++++++++++++++++ test/coarsening.jl | 29 ++++++++++++++++++++++++++ test/runtests.jl | 1 + 5 files changed, 103 insertions(+) create mode 100644 src/coarsening.jl create mode 100644 src/coarsening/regular.jl create mode 100644 test/coarsening.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 00c064cb5..a200f9fa5 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -96,6 +96,7 @@ include("sampling.jl") include("pointification.jl") include("discretization.jl") include("refinement.jl") +include("coarsening.jl") # transforms include("transforms.jl") @@ -487,6 +488,11 @@ export TriSubdivision, refine, + # coarsening + CoarseningMethod, + RegularCoarsening, + coarsen, + # transforms GeometricTransform, CoordinateTransform, diff --git a/src/coarsening.jl b/src/coarsening.jl new file mode 100644 index 000000000..899eec22b --- /dev/null +++ b/src/coarsening.jl @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + CoarseningMethod + +A method for coarsening meshes. +""" +abstract type CoarseningMethod end + +""" + coarsen(mesh, method) + +Coarsen `mesh` with coarsening `method`. +""" +function coarsen end + +# ---------------- +# IMPLEMENTATIONS +# ---------------- + +include("coarsening/regular.jl") diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl new file mode 100644 index 000000000..c0760f8d0 --- /dev/null +++ b/src/coarsening/regular.jl @@ -0,0 +1,44 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + RegularCoarsening(f₁, f₂, ..., fₙ) + +Coarsen each dimension of the grid by given factors `f₁`, `f₂`, ..., `fₙ`. + +## Examples + +```julia +coarsen(grid2D, RegularCoarsening(2, 3)) +coarsen(grid3D, RegularCoarsening(2, 3, 1)) +``` +""" +struct RegularCoarsening{N} <: CoarseningMethod + factors::Dims{N} +end + +RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) + +function coarsen(grid::CartesianGrid{Dim}, method::RegularCoarsening) where {Dim} + factors = fitdims(method.factors, Dim) + CartesianGrid(minimum(grid), maximum(grid), dims = size(grid) .÷ factors) +end + +function coarsen(grid::RectilinearGrid{Dim}, method::RegularCoarsening) where {Dim} + factors = fitdims(method.factors, Dim) + dims = size(grid) .+ .!isperiodic(grid) + rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) + xyzₛ = xyz(grid) + xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], Dim) + RectilinearGrid(xyzₜ) +end + +function coarsen(grid::StructuredGrid{Dim}, method::RegularCoarsening) where {Dim} + factors = fitdims(method.factors, Dim) + dims = size(grid) .+ .!isperiodic(grid) + rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) + XYZₛ = XYZ(grid) + XYZₜ = ntuple(i -> XYZₛ[i][rngs...], Dim) + StructuredGrid(XYZₜ) +end diff --git a/test/coarsening.jl b/test/coarsening.jl new file mode 100644 index 000000000..11e6047ca --- /dev/null +++ b/test/coarsening.jl @@ -0,0 +1,29 @@ +@testset "Coarsening" begin + @testset "RegularCoarsening" begin + # 2D grids + grid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(10, 10)) + @test coarsen(grid, RegularCoarsening(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test coarsen(rgrid, RegularCoarsening(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid + + grid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(10, 5)) + @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid + + # 3D grids + grid = CartesianGrid{T}(100, 100, 100) + tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(50, 25, 20)) + @test coarsen(grid, RegularCoarsening(2, 4, 5)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test coarsen(rgrid, RegularCoarsening(2, 4, 5)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 1fc854854..fb092b591 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -107,6 +107,7 @@ testfiles = [ "pointification.jl", "discretization.jl", "refinement.jl", + "coarsening.jl", "transforms.jl", "distances.jl", "supportfun.jl", From 849874e235249e2f14889b8a0d757476be640f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Apr 2024 09:12:42 -0300 Subject: [PATCH 018/423] Add coarsening.md docs --- docs/make.jl | 1 + docs/src/algorithms/coarsening.md | 33 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 docs/src/algorithms/coarsening.md diff --git a/docs/make.jl b/docs/make.jl index 2fea7d09e..86830fe18 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -48,6 +48,7 @@ makedocs( "algorithms/partitioning.md", "algorithms/discretization.md", "algorithms/refinement.md", + "algorithms/coarsening.md", "algorithms/simplification.md", "algorithms/intersection.md", "algorithms/clipping.md", diff --git a/docs/src/algorithms/coarsening.md b/docs/src/algorithms/coarsening.md new file mode 100644 index 000000000..8cd4f6dc5 --- /dev/null +++ b/docs/src/algorithms/coarsening.md @@ -0,0 +1,33 @@ +# Coarsening + +```@example coarsening +using Meshes # hide +import CairoMakie as Mke # hide +``` + +```@docs +coarsen +CoarseningMethod +``` + +## RegularCoarsening + +```@docs +RegularCoarsening +``` + +```@example coarsening +grid = CartesianGrid(100, 100) + +# refine three times +cor1 = coarsen(grid, RegularCoarsening(2, 2)) +cor2 = coarsen(cor1, RegularCoarsening(3, 2)) +cor3 = coarsen(cor2, RegularCoarsening(2, 3)) + +fig = Mke.Figure(size = (800, 800)) +viz(fig[1,1], grid, showfacets = true) +viz(fig[1,2], cor1, showfacets = true) +viz(fig[2,1], cor2, showfacets = true) +viz(fig[2,2], cor3, showfacets = true) +fig +``` From ca670c7bb795bae89fdb27c9de9879c5bb6d96a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:45:06 -0300 Subject: [PATCH 019/423] :robot: Format .jl files (#818) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/coarsening/regular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index c0760f8d0..190c189a1 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -22,7 +22,7 @@ RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) function coarsen(grid::CartesianGrid{Dim}, method::RegularCoarsening) where {Dim} factors = fitdims(method.factors, Dim) - CartesianGrid(minimum(grid), maximum(grid), dims = size(grid) .÷ factors) + CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end function coarsen(grid::RectilinearGrid{Dim}, method::RegularCoarsening) where {Dim} From 3ebd5e9e76ad3e0ef8e4b1f9c7e1db5c67f7f108 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 1 Apr 2024 08:49:31 -0300 Subject: [PATCH 020/423] Fix the implementation of the `Meshes.XYZ` function (#820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the implementation of the 'Meshes.XYZ' function * Update src/mesh/rectilineargrid.jl * Rename auxiliary function * Apply suggestions from code review Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Apply suggestions * Apply suggestions --------- Co-authored-by: Júlio Hoffimann --- src/mesh/cartesiangrid.jl | 2 +- src/mesh/rectilineargrid.jl | 15 +-------------- src/utils.jl | 19 +++++++++++++++++++ test/mesh.jl | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 0904592a5..652dd533d 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -125,7 +125,7 @@ function xyz(g::CartesianGrid{Dim}) where {Dim} end end -XYZ(g::CartesianGrid) = XYZ(convert(RectilinearGrid, g)) +XYZ(g::CartesianGrid) = XYZ(xyz(g)) function centroid(g::CartesianGrid, ind::Int) ijk = elem2cart(topology(g), ind) diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 41391c3af..29227c5b3 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -35,20 +35,7 @@ vertex(g::RectilinearGrid{Dim}, ijk::Dims{Dim}) where {Dim} = Point(getindex.(g. xyz(g::RectilinearGrid) = g.xyz -@generated function XYZ(g::RectilinearGrid{Dim,T}) where {Dim,T} - exprs = ntuple(Dim) do d - quote - a = g.xyz[$d] - N = length(a) - A = Array{T,Dim}(undef, @ntuple($Dim, i -> N)) - @nloops $Dim i A begin - @nref($Dim, A, i) = a[$(Symbol(:i_, d))] - end - A - end - end - Expr(:tuple, exprs...) -end +XYZ(g::RectilinearGrid) = XYZ(xyz(g)) function centroid(g::RectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) diff --git a/src/utils.jl b/src/utils.jl index dc4c7eaed..de171601f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -135,3 +135,22 @@ function intersectparameters(a::Point{Dim,T}, b::Point{Dim,T}, c::Point{Dim,T}, λ₁, λ₂, r, rₐ end + +""" + XYZ(xyz) + +Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. +""" +@generated function XYZ(xyz::NTuple{Dim,<:AbstractVector{T}}) where {Dim,T} + exprs = ntuple(Dim) do d + quote + a = xyz[$d] + A = Array{T,Dim}(undef, length.(xyz)) + @nloops $Dim i A begin + @nref($Dim, A, i) = a[$(Symbol(:i_, d))] + end + A + end + end + Expr(:tuple, exprs...) +end diff --git a/test/mesh.jl b/test/mesh.jl index e842e00a1..db02a8c49 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -268,6 +268,15 @@ # conversion cg = CartesianGrid{T}(10, 10) rg = convert(RectilinearGrid, cg) + @test size(rg) == size(cg) + @test nvertices(rg) == nvertices(cg) + @test nelements(rg) == nelements(cg) + @test topology(rg) == topology(cg) + @test vertices(rg) == vertices(cg) + + cg = CartesianGrid{T}(10, 20, 30) + rg = convert(RectilinearGrid, cg) + @test size(rg) == size(cg) @test nvertices(rg) == nvertices(cg) @test nelements(rg) == nelements(cg) @test topology(rg) == topology(cg) @@ -373,6 +382,15 @@ # conversion cg = CartesianGrid{T}(10, 10) sg = convert(StructuredGrid, cg) + @test size(sg) == size(cg) + @test nvertices(sg) == nvertices(cg) + @test nelements(sg) == nelements(cg) + @test topology(sg) == topology(cg) + @test vertices(sg) == vertices(cg) + + cg = CartesianGrid{T}(10, 20, 30) + sg = convert(StructuredGrid, cg) + @test size(sg) == size(cg) @test nvertices(sg) == nvertices(cg) @test nelements(sg) == nelements(cg) @test topology(sg) == topology(cg) @@ -380,6 +398,15 @@ rg = RectilinearGrid(T.(0:10), T.(0:10)) sg = convert(StructuredGrid, rg) + @test size(sg) == size(rg) + @test nvertices(sg) == nvertices(rg) + @test nelements(sg) == nelements(rg) + @test topology(sg) == topology(rg) + @test vertices(sg) == vertices(rg) + + rg = RectilinearGrid(T.(0:10), T.(0:20), T.(0:30)) + sg = convert(StructuredGrid, rg) + @test size(sg) == size(rg) @test nvertices(sg) == nvertices(rg) @test nelements(sg) == nelements(rg) @test topology(sg) == topology(rg) From 06ccaddc997c60a491cf2087b982e8eb836cd47b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:24:13 -0300 Subject: [PATCH 021/423] Fix `Affine/Rotate` transform with `Grid`s (#824) * Fix 'Affine' transform with 'Grid's * Apply suggestions --- src/transforms/affine.jl | 6 +++++ src/transforms/rotate.jl | 4 +++ test/transforms.jl | 57 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 45168d11f..f4c3c351a 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -61,6 +61,12 @@ applycoord(t::Affine, b::Box{2}) = applycoord(t, convert(Quadrangle, b)) applycoord(t::Affine, b::Box{3}) = applycoord(t, convert(Hexahedron, b)) +applycoord(t::Affine, g::CartesianGrid) = TransformedGrid(g, t) + +applycoord(t::Affine, g::RectilinearGrid) = TransformedGrid(g, t) + +applycoord(t::Affine, g::StructuredGrid) = TransformedGrid(g, t) + # ----------------- # HELPER FUNCTIONS # ----------------- diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 88c5a6818..45414ca48 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -72,3 +72,7 @@ applycoord(t::Rotate, b::Box{2}) = applycoord(t, convert(Quadrangle, b)) applycoord(t::Rotate, b::Box{3}) = applycoord(t, convert(Hexahedron, b)) applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) + +applycoord(t::Rotate, g::RectilinearGrid) = TransformedGrid(g, t) + +applycoord(t::Rotate, g::StructuredGrid) = TransformedGrid(g, t) diff --git a/test/transforms.jl b/test/transforms.jl index 04ba70ce2..bb89bde4b 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -174,7 +174,29 @@ f = Rotate(Angle2d(T(π / 2))) d = CartesianGrid{T}(10, 10) r, c = TB.apply(f, d) - @test r isa TransformedMesh + @test r isa TransformedGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Rotate(Angle2d(T(π / 2))) + d = convert(RectilinearGrid, CartesianGrid{T}(10, 10)) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Rotate(Angle2d(T(π / 2))) + d = convert(StructuredGrid, CartesianGrid{T}(10, 10)) + r, c = TB.apply(f, d) + @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -503,6 +525,39 @@ @test all(r .≈ [f(t), f(t)]) @test all(TB.revert(f, r, c) .≈ d) + # -------------- + # CARTESIANGRID + # -------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = CartesianGrid{T}(10, 10) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = convert(RectilinearGrid, CartesianGrid{T}(10, 10)) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = convert(StructuredGrid, CartesianGrid{T}(10, 10)) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # ---------- # TRANSFORM # ---------- From bb98acc23fb801fc9d7bf71011a1dcc04ed84336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 05:41:32 -0300 Subject: [PATCH 022/423] Bump julia-actions/setup-julia from 1 to 2 (#825) Bumps [julia-actions/setup-julia](https://github.com/julia-actions/setup-julia) from 1 to 2. - [Release notes](https://github.com/julia-actions/setup-julia/releases) - [Commits](https://github.com/julia-actions/setup-julia/compare/v1...v2) --- updated-dependencies: - dependency-name: julia-actions/setup-julia dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 27e782248..6902eb325 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -32,7 +32,7 @@ jobs: - x64 steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@v2 with: version: '1' - run: | From 5218289cc2b04c4a4067433af55b32ff0506a35e Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 8 Apr 2024 05:51:28 -0300 Subject: [PATCH 023/423] Use Colorfy.jl in `viz` (#822) * Use Colorfy.jl in 'viz' * Apply suggestions * Update code * Remove unused code * Add compat --- Project.toml | 2 ++ ext/MeshesMakieExt.jl | 7 +----- ext/colors.jl | 55 ++++++------------------------------------- src/viz.jl | 29 ----------------------- 4 files changed, 10 insertions(+), 83 deletions(-) diff --git a/Project.toml b/Project.toml index aaa441455..3792d09d2 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.41.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" CircularArrays = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" +Colorfy = "03fe91ce-8ec6-4610-8e8d-e7491ccca690" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" @@ -27,6 +28,7 @@ MeshesMakieExt = "Makie" [compat] Bessels = "0.2" CircularArrays = "1.3" +Colorfy = "0.1" Distances = "0.10" LinearAlgebra = "1.9" Makie = "0.20" diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index f7c9ea9ae..c2c3515fd 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -7,16 +7,11 @@ module MeshesMakieExt using Meshes using Rotations using LinearAlgebra - -using Makie: cgrad -using Makie: coloralpha -using Makie.Colors: Colorant +using Colorfy import TransformsBase as TB import Meshes: viz, viz! -import Meshes: ascolors -import Meshes: defaultscheme import Makie Makie.@recipe(Viz, object) do scene diff --git a/ext/colors.jl b/ext/colors.jl index 1732f0a35..1d01c0221 100644 --- a/ext/colors.jl +++ b/ext/colors.jl @@ -2,53 +2,12 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -# type alias to reduce typing -const V{T} = AbstractVector{<:T} - -# convert value to colorant, optionally using color scheme object -ascolors(values::V{Symbol}, scheme, colorrange) = ascolors(string.(values), scheme, colorrange) -ascolors(values::V{AbstractString}, scheme, colorrange) = parse.(Ref(Colorant), values) -ascolors(values::V{Number}, scheme, colorrange) = - isnothing(colorrange) ? get(scheme, values, :extrema) : get(scheme, values, colorrange) -ascolors(values::V{Colorant}, scheme, colorrange) = values - -# convert color scheme name to color scheme object -ascolorscheme(name::Symbol) = cgrad(name) -ascolorscheme(name::AbstractString) = ascolorscheme(Symbol(name)) -ascolorscheme(scheme) = scheme - -# default color scheme for vector of values -defaultscheme(values) = cgrad(:viridis) - -# add transparency to colors -setalpha(colors, alphas) = coloralpha.(colors, alphas) -setalpha(colors, ::Nothing) = colors - -# -------------------------------- -# PROCESS COLORS PROVIDED BY USER -# -------------------------------- - -# convert user input to colors -function process(values::V, scheme, colorrange, alphas) - # find invalid and valid indices - isinvalid(v) = ismissing(v) || (v isa Number && isnan(v)) - iinds = findall(isinvalid, values) - vinds = setdiff(1:length(values), iinds) - - # invalid values are assigned full transparency - icolors = parse(Colorant, "rgba(0,0,0,0)") - - # valid values are assigned colors from scheme - vals = coalesce.(values[vinds]) - vscheme = isnothing(scheme) ? defaultscheme(vals) : ascolorscheme(scheme) - vcolors = setalpha(ascolors(vals, vscheme, colorrange), alphas) - - # build final vector of colors - colors = Vector{Colorant}(undef, length(values)) - colors[iinds] .= icolors - colors[vinds] .= vcolors - - colors +# preprocess colors provided by user +function process(values::AbstractVector, colorscheme, colorrange, alphas) + valphas = isnothing(alphas) ? Colorfy.defaultalphas(values) : alphas + vcolorscheme = isnothing(colorscheme) ? Colorfy.defaultcolorscheme(values) : colorscheme + vcolorrange = isnothing(colorrange) ? Colorfy.defaultcolorrange(values) : colorrange + colorfy(values, alphas=valphas, colorscheme=vcolorscheme, colorrange=vcolorrange) end -process(value, scheme, colorrange, alphas) = process([value], scheme, colorrange, alphas) |> first +process(value, colorscheme, colorrange, alphas) = process([value], colorscheme, colorrange, alphas) |> first diff --git a/src/viz.jl b/src/viz.jl index ee2954da9..a6c3ddf0d 100644 --- a/src/viz.jl +++ b/src/viz.jl @@ -62,32 +62,3 @@ Visualize Meshes.jl `object` in an existing scene with `options` forwarded to [`viz`](@ref). """ function viz! end - -""" - ascolors(values, colormap) - -Convert vector of `values` to Colors.jl, -using `colormap` from ColorSchemes.jl. - -### Notes - -This function is intended for developers -who may be interested in the visualization -of custom Julia objects in the `color` -argument of the [`viz`](@ref) function. -""" -function ascolors end - -""" - defaultscheme(values) - -Return default colormap for `values`. - -### Notes - -This function is intended for developers -who may be interested in the automatic -selection of colorschemes from ColorSchemes.jl -for custom Julia objects. -""" -function defaultscheme end From 761ad27426745986a4471695778221394ed92b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 8 Apr 2024 08:59:04 -0300 Subject: [PATCH 024/423] Rename viz options for clarity --- docs/src/algorithms/coarsening.md | 8 +++--- docs/src/algorithms/discretization.md | 10 +++---- docs/src/algorithms/refinement.md | 40 +++++++++++++-------------- docs/src/domains/meshes.md | 8 +++--- docs/src/index.md | 4 +-- docs/src/visualization.md | 2 +- ext/MeshesMakieExt.jl | 8 ++++-- ext/geometryset.jl | 26 ++++++++--------- ext/grid/cartesian.jl | 20 +++++++------- ext/grid/rectilinear.jl | 8 +++--- ext/grid/structured.jl | 8 +++--- ext/grid/transformed.jl | 6 ++-- ext/simplemesh.jl | 8 +++--- src/viz.jl | 24 ++++++++-------- test/discretization.jl | 4 +-- test/refinement.jl | 36 ++++++++++++------------ 16 files changed, 112 insertions(+), 108 deletions(-) diff --git a/docs/src/algorithms/coarsening.md b/docs/src/algorithms/coarsening.md index 8cd4f6dc5..3f3ab1e32 100644 --- a/docs/src/algorithms/coarsening.md +++ b/docs/src/algorithms/coarsening.md @@ -25,9 +25,9 @@ cor2 = coarsen(cor1, RegularCoarsening(3, 2)) cor3 = coarsen(cor2, RegularCoarsening(2, 3)) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], grid, showfacets = true) -viz(fig[1,2], cor1, showfacets = true) -viz(fig[2,1], cor2, showfacets = true) -viz(fig[2,2], cor3, showfacets = true) +viz(fig[1,1], grid, showsegments = true) +viz(fig[1,2], cor1, showsegments = true) +viz(fig[2,1], cor2, showsegments = true) +viz(fig[2,2], cor3, showsegments = true) fig ``` diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index 31c538cf3..57e5f15a5 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -27,7 +27,7 @@ mesh = discretize(hexagon, FanTriangulation()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], hexagon) -viz(fig[1,2], mesh, showfacets = true) +viz(fig[1,2], mesh, showsegments = true) fig ``` @@ -43,7 +43,7 @@ sphere = Sphere((0.,0.,0.), 1.) mesh = discretize(sphere, RegularDiscretization(10,10)) fig = Mke.Figure(size = (400, 400)) -viz(fig[1,1], mesh, showfacets = true) +viz(fig[1,1], mesh, showsegments = true) fig ``` @@ -100,7 +100,7 @@ mesh = discretize(polyarea, Dehn1899()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) -viz(fig[1,2], mesh, showfacets = true) +viz(fig[1,2], mesh, showsegments = true) fig ``` @@ -115,7 +115,7 @@ mesh = discretize(polyarea, FIST()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) -viz(fig[1,2], mesh, showfacets = true) +viz(fig[1,2], mesh, showsegments = true) fig ``` @@ -141,6 +141,6 @@ mesh = discretize(polyarea, FIST()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) -viz(fig[1,2], mesh, showfacets = true) +viz(fig[1,2], mesh, showsegments = true) fig ``` diff --git a/docs/src/algorithms/refinement.md b/docs/src/algorithms/refinement.md index 7261a76f5..883873060 100644 --- a/docs/src/algorithms/refinement.md +++ b/docs/src/algorithms/refinement.md @@ -25,10 +25,10 @@ ref2 = refine(ref1, TriRefinement()) ref3 = refine(ref2, TriRefinement()) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], grid, showfacets = true) -viz(fig[1,2], ref1, showfacets = true) -viz(fig[2,1], ref2, showfacets = true) -viz(fig[2,2], ref3, showfacets = true) +viz(fig[1,1], grid, showsegments = true) +viz(fig[1,2], ref1, showsegments = true) +viz(fig[2,1], ref2, showsegments = true) +viz(fig[2,2], ref3, showsegments = true) fig ``` @@ -47,10 +47,10 @@ ref2 = refine(ref1, QuadRefinement()) ref3 = refine(ref2, QuadRefinement()) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], grid, showfacets = true) -viz(fig[1,2], ref1, showfacets = true) -viz(fig[2,1], ref2, showfacets = true) -viz(fig[2,2], ref3, showfacets = true) +viz(fig[1,1], grid, showsegments = true) +viz(fig[1,2], ref1, showsegments = true) +viz(fig[2,1], ref2, showsegments = true) +viz(fig[2,2], ref3, showsegments = true) fig ``` @@ -69,10 +69,10 @@ ref2 = refine(ref1, RegularRefinement(3, 2)) ref3 = refine(ref2, RegularRefinement(2, 3)) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], grid, showfacets = true) -viz(fig[1,2], ref1, showfacets = true) -viz(fig[2,1], ref2, showfacets = true) -viz(fig[2,2], ref3, showfacets = true) +viz(fig[1,1], grid, showsegments = true) +viz(fig[1,2], ref1, showsegments = true) +viz(fig[2,1], ref2, showsegments = true) +viz(fig[2,2], ref3, showsegments = true) fig ``` @@ -94,10 +94,10 @@ ref2 = refine(ref1, CatmullClark()) ref3 = refine(ref2, CatmullClark()) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], mesh, showfacets = true) -viz(fig[1,2], ref1, showfacets = true) -viz(fig[2,1], ref2, showfacets = true) -viz(fig[2,2], ref3, showfacets = true) +viz(fig[1,1], mesh, showsegments = true) +viz(fig[1,2], ref1, showsegments = true) +viz(fig[2,1], ref2, showsegments = true) +viz(fig[2,2], ref3, showsegments = true) fig ``` @@ -116,9 +116,9 @@ ref2 = refine(ref1, TriSubdivision()) ref3 = refine(ref2, TriSubdivision()) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], grid, showfacets = true) -viz(fig[1,2], ref1, showfacets = true) -viz(fig[2,1], ref2, showfacets = true) -viz(fig[2,2], ref3, showfacets = true) +viz(fig[1,1], grid, showsegments = true) +viz(fig[1,2], ref1, showsegments = true) +viz(fig[2,1], ref2, showsegments = true) +viz(fig[2,2], ref3, showsegments = true) fig ``` diff --git a/docs/src/domains/meshes.md b/docs/src/domains/meshes.md index b1c4a2053..1511565ba 100644 --- a/docs/src/domains/meshes.md +++ b/docs/src/domains/meshes.md @@ -23,7 +23,7 @@ CartesianGrid # 3D Cartesian grid grid = CartesianGrid(10, 10, 10) -viz(grid, showfacets = true) +viz(grid, showsegments = true) ``` ```@docs @@ -36,7 +36,7 @@ x = 0.0:0.2:1.0 y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) -viz(grid, showfacets = true) +viz(grid, showsegments = true) ``` ```@docs @@ -49,7 +49,7 @@ X = [i/20 * cos(3π/2 * (j-1) / (30-1)) for i in 1:20, j in 1:30] Y = [i/20 * sin(3π/2 * (j-1) / (30-1)) for i in 1:20, j in 1:30] grid = StructuredGrid(X, Y) -viz(grid, showfacets = true) +viz(grid, showsegments = true) ``` ```@docs @@ -66,7 +66,7 @@ connec = connect.([(1,2,6,5),(2,4,6),(4,3,5,6),(3,1,5)], Ngon) # 2D mesh made of N-gon elements mesh = SimpleMesh(points, connec) -viz(mesh, showfacets = true) +viz(mesh, showsegments = true) ``` ## Connectivities diff --git a/docs/src/index.md b/docs/src/index.md index 1189e265b..25971ca7a 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -296,7 +296,7 @@ Domain ```@example overview g = CartesianGrid(100, 100) -viz(g, showfacets = true) +viz(g, showsegments = true) ``` No memory is allocated: @@ -323,7 +323,7 @@ mesh = SimpleMesh(points, [tris; quads]) ``` ```@example overview -viz(mesh, showfacets = true) +viz(mesh, showsegments = true) ``` The actual geometries of the elements are materialized in a lazy fashion diff --git a/docs/src/visualization.md b/docs/src/visualization.md index a47788817..d69ad4d6e 100644 --- a/docs/src/visualization.md +++ b/docs/src/visualization.md @@ -39,5 +39,5 @@ such as [`Mesh`](@ref) and show facets efficiently: ```@example viz grid = CartesianGrid(10, 10, 10) -viz(grid, showfacets = true, facetcolor = :teal) +viz(grid, showsegments = true, segmentcolor = :teal) ``` \ No newline at end of file diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index c2c3515fd..120c67da8 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -20,10 +20,12 @@ Makie.@recipe(Viz, object) do scene alpha=nothing, colormap=nothing, colorrange=nothing, - pointsize=4, + showsegments=false, + segmentcolor=:gray30, segmentsize=1.5, - showfacets=false, - facetcolor=:gray30 + showpoints=false, + pointcolor=:gray30, + pointsize=4, ) end diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 6fd7284e6..d0482fd5b 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -93,17 +93,17 @@ end const PolygonLike{Dim,T} = Union{Polygon{Dim,T},MultiPolygon{Dim,T}} function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # repeat colors if necessary colors = Makie.@lift mayberepeat($colorant, $geoms) # visualize as built-in poly polys = Makie.@lift asmakie($geoms) - if showfacets[] - Makie.poly!(plot, polys, color=colors, strokecolor=facetcolor, strokewidth=segmentsize) + if showsegments[] + Makie.poly!(plot, polys, color=colors, strokecolor=segmentcolor, strokewidth=segmentsize) else Makie.poly!(plot, polys, color=colors) end @@ -117,28 +117,28 @@ function vizgset3D!(plot, geoms, colorant) end function showfacets1D!(plot, geoms) - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] + showpoints = plot[:showpoints] + pointcolor = plot[:pointcolor] pointsize = plot[:pointsize] - if showfacets[] + if showpoints[] # all boundaries are points or multipoints bounds = Makie.@lift filter(!isnothing, boundary.($geoms)) bset = Makie.@lift GeometrySet($bounds) - viz!(plot, bset, color=facetcolor, pointsize=pointsize) + viz!(plot, bset, color=pointcolor, pointsize=pointsize) end end function showfacets2D!(plot, geoms) - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - if showfacets[] - # all boundaries are geometries + if showsegments[] + # all boundaries are 1D geometries bounds = Makie.@lift filter(!isnothing, boundary.($geoms)) bset = Makie.@lift GeometrySet($bounds) - viz!(plot, bset, color=facetcolor, segmentsize=segmentsize) + viz!(plot, bset, color=segmentcolor, segmentsize=segmentsize) end end diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index db163241d..ba026159a 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -8,9 +8,9 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -30,10 +30,10 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) bbox = Makie.@lift boundingbox($grid) viz!(plot, bbox, color=colorant) - if showfacets[] + if showsegments[] tup = Makie.@lift xysegments(Meshes.xyz($grid)...) x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end else if nc[] == nv[] @@ -46,10 +46,10 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) Makie.image!(plot, C, interpolate=false) end - if showfacets[] + if showsegments[] tup = Makie.@lift xysegments(0:$sz[1], 0:$sz[2]) x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end # adjust spacing and origin @@ -67,9 +67,9 @@ function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -101,9 +101,9 @@ function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) end end - if showfacets[] + if showsegments[] tup = Makie.@lift xyzsegments($xyz...) x, y, z = Makie.@lift($tup[1]), Makie.@lift($tup[2]), Makie.@lift($tup[3]) - Makie.lines!(plot, x, y, z, color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, x, y, z, color=segmentcolor, linewidth=segmentsize) end end diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 277173597..963c8fd86 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -8,9 +8,9 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -42,9 +42,9 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) end end - if showfacets[] + if showsegments[] tup = Makie.@lift xysegments($xs, $ys) x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end end diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 6adf43b36..444e84c7c 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -8,9 +8,9 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -30,10 +30,10 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) C = Makie.@lift reshape($colorant, $sz) Makie.surface!(plot, X, Y, color=C) - if showfacets[] + if showsegments[] tup = Makie.@lift structuredsegments($grid) x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end else vizmesh2D!(plot) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 500fde86f..389555d0a 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -26,10 +26,10 @@ function transformedgrid!(plot, fallback) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] - viz!(plot, grid; color, alpha, colormap, segmentsize, showfacets, facetcolor) + viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans[]) else fallback(tgrid) diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index 57e8c4e77..9ad59da10 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -54,9 +54,9 @@ function vizmesh2D!(plot) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - showfacets = plot[:showfacets] - facetcolor = plot[:facetcolor] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -144,7 +144,7 @@ function vizmesh2D!(plot) Makie.mesh!(plot, tcoords, tmatrix, color=tcolors, shading=tshading) - if showfacets[] + if showsegments[] # retrieve coordinates parameters xparams = Makie.@lift let # relevant settings @@ -182,7 +182,7 @@ function vizmesh2D!(plot) Makie.@lift $xparams[i] end - Makie.lines!(plot, xyz..., color=facetcolor, linewidth=segmentsize) + Makie.lines!(plot, xyz..., color=segmentcolor, linewidth=segmentsize) end end diff --git a/src/viz.jl b/src/viz.jl index a6c3ddf0d..bc7fc9fa5 100644 --- a/src/viz.jl +++ b/src/viz.jl @@ -9,14 +9,16 @@ Visualize Meshes.jl `object` with various `options`. ## Available options -* `color` - color of geometries -* `alpha` - transparency in [0,1] -* `colormap` - color scheme/map from ColorSchemes.jl -* `colorrange` - minimum and maximum color values -* `pointsize` - size of points in point set -* `segmentsize` - size (or width) of segments -* `showfacets` - enable visualization of facets -* `facetcolor` - color of facets (e.g. edges) +* `color` - color of geometries +* `alpha` - transparency in [0,1] +* `colormap` - color scheme/map from ColorSchemes.jl +* `colorrange` - minimum and maximum color values +* `showsegments` - visualize segments +* `segmentcolor` - color of segments +* `segmentsize` - width of segments +* `showpoints` - visualize points +* `pointcolor` - color of points +* `pointsize` - size of points The option `color` can be a single scalar or a vector of scalars. For [`Mesh`](@ref) subtypes, the length of @@ -36,11 +38,11 @@ viz(mesh, color = 1:nelements(mesh)) ``` Different strategies to show the boundary of -geometries (showfacets vs. boundary): +geometries (showsegments vs. boundary): ``` -# visualize boundary as facets -viz(polygon, showfacets = true) +# visualize boundary with showsegments +viz(polygon, showsegments = true) # visualize boundary with separate call viz(polygon) diff --git a/test/discretization.jl b/test/discretization.jl index 602d8a13e..4d39e7f5f 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -492,8 +492,8 @@ grid = CartesianGrid{T}(3, 3) mesh = simplexify(grid) fig = Mke.Figure(size=(600, 300)) - viz(fig[1, 1], grid, showfacets=true) - viz(fig[1, 2], mesh, showfacets=true) + viz(fig[1, 1], grid, showsegments=true) + viz(fig[1, 2], mesh, showsegments=true) @test_reference "data/triangulate-$T.png" fig end diff --git a/test/refinement.jl b/test/refinement.jl index f32baaae9..deec9956d 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -6,9 +6,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], grid, showfacets=true) - viz(fig[1, 2], ref1, showfacets=true) - viz(fig[1, 3], ref2, showfacets=true) + viz(fig[1, 1], grid, showsegments=true) + viz(fig[1, 2], ref1, showsegments=true) + viz(fig[1, 3], ref2, showsegments=true) @test_reference "data/trirefine-$T.png" fig end end @@ -23,9 +23,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showfacets=true) - viz(fig[1, 2], ref2, showfacets=true) - viz(fig[1, 3], ref3, showfacets=true) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/quadrefine-$T.png" fig end end @@ -64,9 +64,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showfacets=true) - viz(fig[1, 2], ref2, showfacets=true) - viz(fig[1, 3], ref3, showfacets=true) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/catmullclark-1-$T.png" fig end @@ -79,9 +79,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showfacets=true) - viz(fig[1, 2], ref2, showfacets=true) - viz(fig[1, 3], ref3, showfacets=true) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/catmullclark-2-$T.png" fig end @@ -94,9 +94,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showfacets=true) - viz(fig[1, 2], ref2, showfacets=true) - viz(fig[1, 3], ref3, showfacets=true) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/catmullclark-3-$T.png" fig end end @@ -111,9 +111,9 @@ if visualtests fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showfacets=true) - viz(fig[1, 2], ref2, showfacets=true) - viz(fig[1, 3], ref3, showfacets=true) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/trisubdivision-$T.png" fig end end From e469eab28de79c2a203d8ac81b134c736a45c99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 8 Apr 2024 09:09:38 -0300 Subject: [PATCH 025/423] Update docs --- docs/src/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 25971ca7a..6050fc772 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -294,9 +294,9 @@ Domain ``` ```@example overview -g = CartesianGrid(100, 100) +grid = CartesianGrid(100, 100) -viz(g, showsegments = true) +viz(grid, showsegments = true) ``` No memory is allocated: @@ -308,7 +308,7 @@ No memory is allocated: but we can still loop over the elements, which are quadrangles in 2D: ```@example overview -collect(elements(g)) +collect(grid) ``` We can construct a general unstructured mesh with a global vector of points @@ -330,5 +330,5 @@ The actual geometries of the elements are materialized in a lazy fashion like with the Cartesian grid: ```@example overview -collect(elements(mesh)) +collect(mesh) ``` \ No newline at end of file From c8fe241f5271e2cf39b9fe86f03acfe8657e891f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 09:10:33 -0300 Subject: [PATCH 026/423] :robot: Format .jl files (#826) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- ext/MeshesMakieExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 120c67da8..55ef91f2c 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -25,7 +25,7 @@ Makie.@recipe(Viz, object) do scene segmentsize=1.5, showpoints=false, pointcolor=:gray30, - pointsize=4, + pointsize=4 ) end From cc193e4e3c0f379b18daab60cb36c03451afbe2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 8 Apr 2024 09:11:33 -0300 Subject: [PATCH 027/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3792d09d2..a339a2d3b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.41.1" +version = "0.42.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 55f175eca962e486b0fac2cec5bcc6b5224b4e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Apr 2024 08:50:00 -0300 Subject: [PATCH 028/423] Add Ellipsoid primitive --- src/Meshes.jl | 1 + src/boundary.jl | 2 ++ src/discretization.jl | 2 ++ src/discretization/regular.jl | 3 +++ src/measures.jl | 2 ++ src/predicates/isparametrized.jl | 2 ++ src/predicates/isperiodic.jl | 2 ++ src/primitives.jl | 1 + src/primitives/ellipsoid.jl | 43 ++++++++++++++++++++++++++++++++ src/sampling/regular.jl | 4 +++ test/discretization.jl | 7 ++++++ test/predicates.jl | 2 ++ test/primitives.jl | 27 ++++++++++++++++++++ test/sampling.jl | 14 +++++++++++ 14 files changed, 112 insertions(+) create mode 100644 src/primitives/ellipsoid.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index a200f9fa5..478fe0ea9 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -147,6 +147,7 @@ export Box, Ball, Sphere, + Ellipsoid, Disk, Circle, Cylinder, diff --git a/src/boundary.jl b/src/boundary.jl index 8bb4af124..75c637560 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -54,6 +54,8 @@ boundary(b::Ball) = Sphere(center(b), radius(b)) boundary(::Sphere) = nothing +boundary(::Ellipsoid) = nothing + boundary(d::Disk) = Circle(plane(d), radius(d)) boundary(::Circle) = nothing diff --git a/src/discretization.jl b/src/discretization.jl index 4e49a2a65..4e7089714 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -115,6 +115,8 @@ discretize(disk::Disk) = discretize(disk, RegularDiscretization(50)) discretize(sphere::Sphere{3}) = discretize(sphere, RegularDiscretization(50)) +discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(50)) + discretize(torus::Torus) = discretize(torus, RegularDiscretization(50)) discretize(cylsurf::CylinderSurface) = discretize(cylsurf, RegularDiscretization(50, 2)) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index 11c9f89c5..68f5be1ff 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -44,6 +44,7 @@ end perdims(g) = isperiodic(g) perdims(::Sphere{3}) = (false, true) +perdims(::Ellipsoid) = (false, true) # ------------------------ # append to grid topology @@ -57,6 +58,8 @@ appendtopo(::Disk, tg) = _appendcenter(tg) appendtopo(::Sphere{3}, tg) = _appendpoles(tg, 2, true) +appendtopo(::Ellipsoid, tg) = _appendpoles(tg, 2, true) + appendtopo(::CylinderSurface, tg) = _appendpoles(tg, 1, false) appendtopo(::ConeSurface, tg) = _appendpoles(tg, 1, false) diff --git a/src/measures.jl b/src/measures.jl index e670dbda4..c71531991 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -156,3 +156,5 @@ perimeter(::Line{Dim,T}) where {Dim,T} = zero(T) perimeter(::Plane{T}) where {T} = zero(T) perimeter(::Sphere{Dim,T}) where {Dim,T} = zero(T) + +perimeter(::Ellipsoid{T}) where {T} = zero(T) diff --git a/src/predicates/isparametrized.jl b/src/predicates/isparametrized.jl index b848fdb2d..863512177 100644 --- a/src/predicates/isparametrized.jl +++ b/src/predicates/isparametrized.jl @@ -34,6 +34,8 @@ isparametrized(::Type{<:Ball}) = true isparametrized(::Type{<:Sphere}) = true +isparametrized(::Type{<:Ellipsoid}) = true + isparametrized(::Type{<:Disk}) = true isparametrized(::Type{<:Circle}) = true diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index e0622d887..d335c4877 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -26,6 +26,8 @@ isperiodic(::Type{<:Ball{Dim}}) where {Dim} = ntuple(i -> i != 1, Dim) isperiodic(::Type{<:Sphere{Dim}}) where {Dim} = ntuple(i -> true, Dim - 1) +isperiodic(::Type{<:Ellipsoid}) = (true, true) + isperiodic(::Type{<:Disk}) = (false, true) isperiodic(::Type{<:Circle}) = (true,) diff --git a/src/primitives.jl b/src/primitives.jl index dc196dd44..ee8d1eae3 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -32,6 +32,7 @@ include("primitives/plane.jl") include("primitives/box.jl") include("primitives/ball.jl") include("primitives/sphere.jl") +include("primitives/ellipsoid.jl") include("primitives/disk.jl") include("primitives/circle.jl") include("primitives/cylinder.jl") diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl new file mode 100644 index 000000000..3c597d133 --- /dev/null +++ b/src/primitives/ellipsoid.jl @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + Ellipsoid(radii, center=(0, 0, 0), rotation=I) + +A 3D ellipsoid with given `radii`, `center` and `rotation`. +""" +struct Ellipsoid{T,R} <: Primitive{3,T} + radii::NTuple{3,T} + center::Point{3,T} + rotation::R +end + +Ellipsoid(radii::NTuple{3,T}, center = (T(0), T(0), T(0)), rotation::R = I) where {T,R} = + Ellipsoid{T,R}(radii, center, rotation) + +paramdim(::Type{<:Ellipsoid}) = 2 + +radii(e::Ellipsoid) = e.radii + +center(e::Ellipsoid) = e.center + +rotation(e::Ellipsoid) = e.rotation + +function (e::Ellipsoid{T})(θ, φ) where {T} + if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) + throw(DomainError((θ, φ), "e(θ, φ) is not defined for θ, φ outside [0, 1]².")) + end + r = e.radii + c = e.center + R = e.rotation + sθ, cθ = sincospi(T(θ)) + sφ, cφ = sincospi(2 * T(φ)) + x = r[1] * sθ * cφ + y = r[2] * sθ * sφ + z = r[3] * cθ + c + R * Vec(x, y, z) +end + +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ellipsoid{T}}) where {T} = + Ellipsoid((rand(rng, T), rand(rng, T), rand(rng, T)), rand(rng, Point{3,T}), rand(rng, QuatRotation)) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 1c7188ce9..4ebe1b99c 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -56,6 +56,10 @@ firstoffset(::Sphere{3}) = (n -> inv(n + 1), n -> zero(n)) lastoffset(::Sphere{3}) = (n -> inv(n + 1), n -> inv(n)) extrapoints(s::Sphere{3}) = (s(0, 0), s(1, 0)) +firstoffset(::Ellipsoid) = (n -> inv(n + 1), n -> zero(n)) +lastoffset(::Ellipsoid) = (n -> inv(n + 1), n -> inv(n)) +extrapoints(e::Ellipsoid) = (e(0, 0), e(1, 0)) + firstoffset(::CylinderSurface) = (n -> zero(n), n -> zero(n)) lastoffset(::CylinderSurface) = (n -> inv(n), n -> zero(n)) extrapoints(c::CylinderSurface) = (bottom(c)(0, 0), top(c)(0, 0)) diff --git a/test/discretization.jl b/test/discretization.jl index 4d39e7f5f..4ebc223ba 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -41,6 +41,13 @@ @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] + ellips = Ellipsoid((T(3), T(2), T(1))) + mesh = discretize(ellips, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + ball = Ball(P2(0, 0), T(1)) mesh = discretize(ball, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 1 diff --git a/test/predicates.jl b/test/predicates.jl index 28c1ed229..f60dc0f0b 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -119,6 +119,7 @@ @test isparametrized(Box) @test isparametrized(Ball) @test isparametrized(Sphere) + @test isparametrized(Ellipsoid) @test isparametrized(Disk) @test isparametrized(Circle) @test isparametrized(BezierCurve) @@ -144,6 +145,7 @@ @test isperiodic(Ball{3}) == (false, true, true) @test isperiodic(Sphere{2}) == (true,) @test isperiodic(Sphere{3}) == (true, true) + @test isperiodic(Ellipsoid) == (true, true) @test isperiodic(ParaboloidSurface) == (false, true) @test isperiodic(Torus) == (true, true) diff --git a/test/primitives.jl b/test/primitives.jl index f2ea35741..97ceeb30b 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -691,6 +691,33 @@ end end + @testset "Ellipsoid" begin + e = Ellipsoid((T(3), T(2), T(1))) + @test embeddim(e) == 3 + @test paramdim(e) == 2 + @test coordtype(e) == T + @test radii(e) == (T(3), T(2), T(1)) + @test center(e) == P3(0, 0, 0) + @test isnothing(boundary(e)) + @test perimeter(e) == zero(T) + + e = Ellipsoid((T(3), T(2), T(1))) + @test sprint(show, e) == "Ellipsoid(radii: (3.0, 2.0, 1.0), center: (0.0, 0.0, 0.0), rotation: UniformScaling{Bool}(true))" + if T === Float32 + @test sprint(show, MIME("text/plain"), e) == """ + Ellipsoid{3,Float32} + ├─ radii: (3.0f0, 2.0f0, 1.0f0) + ├─ center: Point(0.0f0, 0.0f0, 0.0f0) + └─ rotation: UniformScaling{Bool}(true)""" + else + @test sprint(show, MIME("text/plain"), e) == """ + Ellipsoid{3,Float64} + ├─ radii: (3.0, 2.0, 1.0) + ├─ center: Point(0.0, 0.0, 0.0) + └─ rotation: UniformScaling{Bool}(true)""" + end + end + @testset "Disk" begin p = Plane(P3(0, 0, 0), V3(0, 0, 1)) d = Disk(p, T(2)) diff --git a/test/sampling.jl b/test/sampling.jl index 3d90a4a73..bc5f47912 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -103,6 +103,20 @@ @test p ≈ t end + e = Ellipsoid((T(3), T(2), T(1)), P3(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) + ps = sample(e, RegularSampling(2, 2)) + ts = P3[ + (2.725814800973295, 2.225814800973295, -0.5871173070873834), + (1.872261410380021, 2.372261410380021, -1.0871173070873832), + (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), + (-0.725814800973295, -0.22581480097329454, 2.587117307087383), + (1.8535533905932737, 0.8535533905932737, 1.5), + (0.14644660940672627, 1.1464466094067263, 0.4999999999999999) + ] + for (p, t) in zip(ps, ts) + @test p ≈ t + end + b = Ball(P2(0, 0), T(2)) ps = sample(b, RegularSampling(3, 4)) @test all(∈(b), ps) From 78afbfabf31c6b3826c9fbf4e766fd2557189165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Apr 2024 08:51:37 -0300 Subject: [PATCH 029/423] Add Ellipsoid to docs --- docs/src/geometries/primitives.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index bc180a2d0..3a33637e5 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -79,6 +79,16 @@ Sphere Ball((0.,0.,0.), 1.) |> viz ``` +## Ellipsoid + +```@docs +Ellipsoid +``` + +```@example primitives +Ellipsoid((3., 2., 1.)) |> viz +``` + ### Disk/Circle ```@docs From d6b79983cd1488fca2724403f2baa3abd6be67a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Apr 2024 09:56:04 -0300 Subject: [PATCH 030/423] Refactor Torus parametrization --- src/primitives/torus.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index bc6e6bd5e..430bedef2 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -44,21 +44,20 @@ radii(t::Torus) = (t.major, t.minor) axis(t::Torus) = Line(t.center, t.center + t.normal) -function (t::Torus{T})(u, v) where {T} - if (u < 0 || u > 1) || (v < 0 || v > 1) - throw(DomainError((u, v), "t(u, v) is not defined for u, v outside [0, 1]².")) +function (t::Torus{T})(θ, φ) where {T} + if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) + throw(DomainError((θ, φ), "t(θ, φ) is not defined for θ, φ outside [0, 1]².")) end - c, n⃗ = t.center, t.normal R, r = t.major, t.minor Q = rotation_between(Vec{3,T}(0, 0, 1), n⃗) - θ = u * T(2π) - ϕ = v * T(2π) - x = (R + r * cos(θ)) * cos(ϕ) - y = (R + r * cos(θ)) * sin(ϕ) - z = r * sin(θ) + sθ, cθ = sincospi(2 * T(θ)) + sφ, cφ = sincospi(2 * T(φ)) + x = (R + r * cθ) * cφ + y = (R + r * cθ) * sφ + z = r * sθ c + Q * Vec{3,T}(x, y, z) end From 37a46365acd7c1d97f3328f8e44ce3dc732bc9ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:10:35 -0300 Subject: [PATCH 031/423] :robot: Format .jl files (#828) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/primitives/ellipsoid.jl | 2 +- test/primitives.jl | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 3c597d133..ab0292deb 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -13,7 +13,7 @@ struct Ellipsoid{T,R} <: Primitive{3,T} rotation::R end -Ellipsoid(radii::NTuple{3,T}, center = (T(0), T(0), T(0)), rotation::R = I) where {T,R} = +Ellipsoid(radii::NTuple{3,T}, center=(T(0), T(0), T(0)), rotation::R=I) where {T,R} = Ellipsoid{T,R}(radii, center, rotation) paramdim(::Type{<:Ellipsoid}) = 2 diff --git a/test/primitives.jl b/test/primitives.jl index 97ceeb30b..f0bf8d477 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -702,7 +702,8 @@ @test perimeter(e) == zero(T) e = Ellipsoid((T(3), T(2), T(1))) - @test sprint(show, e) == "Ellipsoid(radii: (3.0, 2.0, 1.0), center: (0.0, 0.0, 0.0), rotation: UniformScaling{Bool}(true))" + @test sprint(show, e) == + "Ellipsoid(radii: (3.0, 2.0, 1.0), center: (0.0, 0.0, 0.0), rotation: UniformScaling{Bool}(true))" if T === Float32 @test sprint(show, MIME("text/plain"), e) == """ Ellipsoid{3,Float32} From 8596bb8420d2502e2e546286f4a7d04765fc4f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Apr 2024 10:29:56 -0300 Subject: [PATCH 032/423] Refactor Torus parametrization --- src/primitives/torus.jl | 2 +- test/sampling.jl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 430bedef2..5b9fa8b75 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -53,7 +53,7 @@ function (t::Torus{T})(θ, φ) where {T} Q = rotation_between(Vec{3,T}(0, 0, 1), n⃗) - sθ, cθ = sincospi(2 * T(θ)) + sθ, cθ = sincospi(2 * T(-θ)) sφ, cφ = sincospi(2 * T(φ)) x = (R + r * cθ) * cφ y = (R + r * cθ) * sφ diff --git a/test/sampling.jl b/test/sampling.jl index bc5f47912..8b72c2369 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -256,14 +256,14 @@ ps = sample(torus, RegularSampling(3, 3)) ts = P3[ (0, 0, -3), - (sqrt(3) / 2, 0, -1.5), (-sqrt(3) / 2, 0, -1.5), + (sqrt(3) / 2, 0, -1.5), (0, 3sqrt(3) / 2, 1.5), - (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), (-sqrt(3) / 2, 3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), (0, -3sqrt(3) / 2, 1.5), - (sqrt(3) / 2, -3sqrt(3) / 4, 0.75), - (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75) + (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, -3sqrt(3) / 4, 0.75) ] for (p, t) in zip(ps, ts) @test p ≈ t From 8d1df58ee3fc11081098f40a63a0b099091779e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Apr 2024 10:30:44 -0300 Subject: [PATCH 033/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a339a2d3b..d38361a80 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.42.0" +version = "0.42.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f484070768b5227bad7c26f7bae596bdcd7437fc Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:29:40 -0300 Subject: [PATCH 034/423] Pass the observable to the 'makietransform!' function (#830) --- ext/grid/transformed.jl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 389555d0a..aa20af9c1 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -30,34 +30,35 @@ function transformedgrid!(plot, fallback) segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) - makietransform!(plot, trans[]) + makietransform!(plot, trans) else fallback(tgrid) end end -makietransform!(plot, trans::TB.Identity) = nothing +makietransform!(plot, trans::Makie.Observable{<:TB.Identity}) = nothing -makietransform!(plot, trans::TB.SequentialTransform) = foreach(t -> makietransform!(plot, t), trans) +makietransform!(plot, trans::Makie.Observable{<:TB.SequentialTransform}) = + foreach(t -> makietransform!(plot, Makie.Observable(t)), trans[]) -function makietransform!(plot, trans::Rotate{<:Angle2d}) - rot = first(TB.parameters(trans)) +function makietransform!(plot, trans::Makie.Observable{<:Rotate{<:Angle2d}}) + rot = first(TB.parameters(trans[])) θ = first(Rotations.params(rot)) Makie.rotate!(plot, θ) end -function makietransform!(plot, trans::Translate) - offsets = first(TB.parameters(trans)) +function makietransform!(plot, trans::Makie.Observable{<:Translate}) + offsets = first(TB.parameters(trans[])) Makie.translate!(plot, offsets...) end -function makietransform!(plot, trans::Scale) - factors = first(TB.parameters(trans)) +function makietransform!(plot, trans::Makie.Observable{<:Scale}) + factors = first(TB.parameters(trans[])) Makie.scale!(plot, factors...) end -function makietransform!(plot, trans::Affine{2}) - A, b = TB.parameters(trans) +function makietransform!(plot, trans::Makie.Observable{<:Affine{2}}) + A, b = TB.parameters(trans[]) if isdiag(A) s₁, s₂ = A[1, 1], A[2, 2] Makie.scale!(plot, s₁, s₂) From a40717a4fc06d0420d4ccc80a73c914358a974b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 17 Apr 2024 10:48:08 -0300 Subject: [PATCH 035/423] Refactor FIST --- src/discretization/fist.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/discretization/fist.jl b/src/discretization/fist.jl index 8e5b89c94..3414a81bc 100644 --- a/src/discretization/fist.jl +++ b/src/discretization/fist.jl @@ -51,7 +51,7 @@ function discretizewithin(ring::Ring{2}, method::FIST) stdpts = collect(vertices(𝒫)) # keep track of global indices - inds = CircularVector(1:nvertices(𝒫)) + I = CircularVector(1:nvertices(𝒫)) # perform ear clipping 𝒬 = earsccw(𝒫) @@ -65,10 +65,10 @@ function discretizewithin(ring::Ring{2}, method::FIST) i = pop!(𝒬) 𝒬[𝒬 .> i] .-= 1 # 1. push a new triangle to 𝒯 - push!(𝒯, connect((inds[i - 1], inds[i], inds[i + 1]))) + push!(𝒯, connect((I[i - 1], I[i], I[i + 1]))) # 2. remove the vertex from 𝒫 - inds = inds[setdiff(1:n, mod1(i, n))] - 𝒫 = Ring(stdpts[inds]) + I = I[setdiff(1:n, mod1(i, n))] + 𝒫 = Ring(stdpts[I]) n = nvertices(𝒫) # 3. update 𝒬 near clipped ear for j in (i - 1, i) @@ -93,10 +93,10 @@ function discretizewithin(ring::Ring{2}, method::FIST) λ(I) = type(I) == Crossing if intersection(λ, s1, s2) # 1. push a new triangle to 𝒯 - push!(𝒯, connect((inds[i], inds[i + 1], inds[i + 2]))) + push!(𝒯, connect((I[i], I[i + 1], I[i + 2]))) # 2. remove the vertex from 𝒫 - inds = inds[setdiff(1:n, mod1(i + 1, n))] - 𝒫 = Ring(stdpts[inds]) + I = I[setdiff(1:n, mod1(i + 1, n))] + 𝒫 = Ring(stdpts[I]) n = nvertices(𝒫) clipped = true break @@ -105,7 +105,7 @@ function discretizewithin(ring::Ring{2}, method::FIST) end end # remaining polygonal area is the last triangle - push!(𝒯, connect((inds[1], inds[2], inds[3]))) + push!(𝒯, connect((I[1], I[2], I[3]))) SimpleMesh(points, 𝒯) end From bda524124e5dbc51b192c90feb45fea2c9f6bbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 17 Apr 2024 14:10:18 -0300 Subject: [PATCH 036/423] Add FIST recovery process --- src/discretization/fist.jl | 16 ++++++++++++++++ test/discretization.jl | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/discretization/fist.jl b/src/discretization/fist.jl index 3414a81bc..3c325ba71 100644 --- a/src/discretization/fist.jl +++ b/src/discretization/fist.jl @@ -102,6 +102,22 @@ function discretizewithin(ring::Ring{2}, method::FIST) break end end + + # consecutive vertices vᵢ-1, vᵢ, vᵢ+1 form a valid ear + # if vᵢ-1 lies on the edge vᵢ+1 -- vᵢ+2 + v = vertices(𝒫) + for i in 1:n + if v[i - 1] ∈ Segment(v[i + 1], v[i + 2]) + # 1. push a new triangle to 𝒯 + push!(𝒯, connect((I[i - 1], I[i], I[i + 1]))) + # 2. remove the vertex from 𝒫 + I = I[setdiff(1:n, mod1(i, n))] + 𝒫 = Ring(stdpts[I]) + n = nvertices(𝒫) + clipped = true + break + end + end end end # remaining polygonal area is the last triangle diff --git a/test/discretization.jl b/test/discretization.jl index 4ebc223ba..5dce53897 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -191,6 +191,21 @@ mesh = discretize(poly, FIST(rng)) @test nvertices(mesh) == 16 @test nelements(mesh) == 14 + + # https://github.com/JuliaGeometry/Meshes.jl/issues/738 + poly = PolyArea( + P2[ + (-0.5f0, 0.3296139f0), + (-0.19128194f0, -0.5f0), + (-0.37872985f0, 0.29592824f0), + (0.21377224f0, -0.0076110554f0), + (-0.20127837f0, 0.24671146f0) + ] + ) + rng = MersenneTwister(123) + mesh = discretize(poly, FIST(rng)) + @test nvertices(mesh) == 5 + @test nelements(mesh) == 3 end @testset "Miscellaneous" begin From 8726a2de8c7d823bd8991fdc9a2fd0829c79b30c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 18 Apr 2024 09:25:45 -0300 Subject: [PATCH 037/423] Fix #738: add desperate mode from FIST paper --- src/discretization/fist.jl | 57 +++++++++++++++++++++++++------------- test/discretization.jl | 22 +++++++++------ 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/discretization/fist.jl b/src/discretization/fist.jl index 3c325ba71..4a55178be 100644 --- a/src/discretization/fist.jl +++ b/src/discretization/fist.jl @@ -118,8 +118,24 @@ function discretizewithin(ring::Ring{2}, method::FIST) break end end + + # enter in "desperate" mode and clip ears at random + if !clipped + # attempt to clip a convex vertex + isconvex(i) = vexity(v, i) == :CONVEX + j = findfirst(isconvex, 1:n) + i = isnothing(j) ? rand(method.rng, 1:n) : j + # 1. push a new triangle to 𝒯 + push!(𝒯, connect((I[i - 1], I[i], I[i + 1]))) + # 2. remove the vertex from 𝒫 + I = I[setdiff(1:n, mod1(i, n))] + 𝒫 = Ring(stdpts[I]) + n = nvertices(𝒫) + clipped = true + end end end + # remaining polygonal area is the last triangle push!(𝒯, connect((I[1], I[2], I[3]))) @@ -135,26 +151,8 @@ earsccw(𝒫) = filter(i -> isearccw(𝒫, i), 1:nvertices(𝒫)) function isearccw(𝒫::Ring{Dim,T}, i) where {Dim,T} v = vertices(𝒫) - # helper function to compute the vexity of vertex i - function vexity(i) - α = ∠(v[i - 1], v[i], v[i + 1]) # oriented angle - θ = α > 0 ? 2 * T(π) - α : -α # inner angle - θ < π ? :CONVEX : :REFLEX - end - - # helper function to check if vertex j is inside cone i - function incone(j, i) - s1 = sideof(v[j], Line(v[i], v[i - 1])) - s2 = sideof(v[j], Line(v[i], v[i + 1])) - if vexity(i) == :CONVEX - s1 != LEFT && s2 != RIGHT - else - s1 != LEFT || s2 != RIGHT - end - end - # CE1.1: classify angle as convex vs. reflex - isconvex = vexity(i) == :CONVEX + isconvex = vexity(v, i) == :CONVEX # CE1.2: check if segment vᵢ-₁ -- vᵢ+₁ intersects 𝒫 λ(I) = !(type(I) == CornerTouching || type(I) == NotIntersecting) @@ -169,7 +167,26 @@ function isearccw(𝒫::Ring{Dim,T}, i) where {Dim,T} end # CE1.3: check if vᵢ-1 ∈ C(vᵢ, vᵢ+1, vᵢ+2) and vᵢ+1 ∈ C(vᵢ-2, vᵢ-1, vᵢ) - incones = incone(i - 1, i + 1) && incone(i + 1, i - 1) + incones = incone(v, i - 1, i + 1) && incone(v, i + 1, i - 1) isconvex && !hasintersect && incones end + +# helper function to compute the vexity of vertex i +function vexity(v, i) + T = coordtype(first(v)) + α = ∠(v[i - 1], v[i], v[i + 1]) # oriented angle + θ = α > 0 ? 2 * T(π) - α : -α # inner angle + θ < π ? :CONVEX : :REFLEX +end + +# helper function to check if vertex j is inside cone i +function incone(v, j, i) + s1 = sideof(v[j], Line(v[i], v[i - 1])) + s2 = sideof(v[j], Line(v[i], v[i + 1])) + if vexity(v, i) == :CONVEX + s1 != LEFT && s2 != RIGHT + else + s1 != LEFT || s2 != RIGHT + end +end diff --git a/test/discretization.jl b/test/discretization.jl index 5dce53897..0e31e435c 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -195,11 +195,11 @@ # https://github.com/JuliaGeometry/Meshes.jl/issues/738 poly = PolyArea( P2[ - (-0.5f0, 0.3296139f0), - (-0.19128194f0, -0.5f0), - (-0.37872985f0, 0.29592824f0), - (0.21377224f0, -0.0076110554f0), - (-0.20127837f0, 0.24671146f0) + (-0.5, 0.3296139), + (-0.19128194, -0.5), + (-0.37872985, 0.29592824), + (0.21377224, -0.0076110554), + (-0.20127837, 0.24671146) ] ) rng = MersenneTwister(123) @@ -318,10 +318,14 @@ @test nelements(mesh) == length(vertices(mesh)) - 2 # https://github.com/JuliaGeometry/Meshes.jl/issues/738 - #poly = readpoly(T, joinpath(datadir, "hole1.line")) - #mesh = discretize(poly, method) - #@test Set(vertices(poly)) == Set(vertices(mesh)) - #@test nelements(mesh) == 32 + poly = readpoly(T, joinpath(datadir, "hole1.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + if method isa FIST && T == Float32 + @test nelements(mesh) == 33 + else + @test nelements(mesh) == 32 + end poly = readpoly(T, joinpath(datadir, "hole2.line")) mesh = discretize(poly, method) From b4471efb9949108b88b6528b02df5f119120cea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 18 Apr 2024 09:26:45 -0300 Subject: [PATCH 038/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d38361a80..70a600f2e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.42.1" +version = "0.42.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 2cf47bbc3c6c42fa37fda3f128f621f66c7c9455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 19 Apr 2024 11:49:01 -0300 Subject: [PATCH 039/423] Fix Use StableRNGs.jl for creating test data. #833 --- test/Project.toml | 2 +- test/data/random-path-6x6.png | Bin 13872 -> 14595 bytes test/data/random-path-7x7.png | Bin 13378 -> 14584 bytes test/discretization.jl | 14 +- test/partitioning.jl | 236 ++++++++++++++++------------------ test/runtests.jl | 2 +- test/sampling.jl | 28 ++-- test/traversing.jl | 6 +- 8 files changed, 133 insertions(+), 155 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index 3abc56481..872aa9b5b 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -6,10 +6,10 @@ Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" PlyIO = "42171d58-473b-503a-8d5f-782019eb09ec" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf" Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" diff --git a/test/data/random-path-6x6.png b/test/data/random-path-6x6.png index 83a6443a374358b89b4fc3a0f8884c68b4764344..cfc1eb1b9e1e8ae31be437648126e9c6e87093e5 100644 GIT binary patch literal 14595 zcmeHucTkk+_GZthR|f++DyRtP1w=uDfFQ92MuG^85s@S!K|yj(J)*#Nvoaa2}Ij3*^tfjh< zWfu#HMB1pX_R}d6X>~e@wCd)%@9~ZTtI>1(S#7SUsYoKd2wcBpyaunob2z1{L`tpR zJ%AT`Ow>+kl1T3RNTgqGl1K}9>z95K$w`z%8oWp%$wZJy?6xs^C*?4dr2doQ8Q1TI zi+)I_t#Km{*35NRm0f+aj^$?h+2)_FAHQ?VD)Ykj17`QnskXRtZdciVu61PF9gYhp zckdS3|9d7&>+PPepA{1V&XpP-Y*O0Xd`07@A9Vxn-w)}Dp~aSM5O;boNsX1JuBP?5 zU6t5{ss7?Efcd;S^BsxwTOzSWp|g0fNWtQcYHY{T3s3M+dx{tu_7@G6Ti95<8TU6* zO{m-dGHGRMp+@X@AsZ|^R^Z_7?%teX>d;&HgZ$EbDLF1Ove}_Vo`2syCDx}pXR$@)4vzQtDR4jVXs8DWNR9t+ji<{|s>C&Yd`MC}9C$zO` zZhf)1pl+6xt}}13=;g4#epgC*uCL|F>Hqys;cRBwbHmr};Repq-S?FZ7r!(r45(`p zhu5NPdN8$MvOK(SI?<)oPL(UezNehJ+(MO~suZV9CrJ^<_~L10VYE#lJ;LOkZnkw> zFrNlvoRv8qS>xIJCDXEo%v_3J>gKm`tI*pdH~z*XP+k&O4$v{3jdiS7ANu^fEEU(hpye96Ge?(Q*=0lP{Qu_hd+X zWo6}319`b21ZPkoLS5Qf-*=wtj9zb=%=rTa2-6!jZeG-1bb1?z;QAeYs&RMIo1QUGt%T zWMpJM#eMGazEN7b^t7nFyjP99i-@j-$nqJp2EUCQ8C9m?23e(C9-iGi+|r{tUA+teuo7{?Hn?0nuIs!WJp1(A^C&4BL#}ps(7aqhJYzapy|*$lgtIJ0+M$~9j`cG+ zhtDx%x+Gt+jl5l^*}bW~p&_BRjH8-5-~RkzQ0b>qE$@^4m+kAiwdbO^WpoQJj_Rh# zU#*Uj#y+u4^;RvGNEO8YX9RFxEE@N~RP&x6yG65<<_#aPCNEU_Dvq8lDY-nw;*Fc9DcaSCig7am+Q zWK4#0dyW^;kEMC`2r>uL7=x+#qj?L1PYJ7ZJ!{smHE#T-l|z%Ue^2;^V%u89wQWv~ z2IQqq);|XnE{@8=5;da<1MJbkjmu29rU2|DQNz$?3G?b0oF2`%Oxvzx7rZwGIIT+R+Phrpz2rdo_yd0CRajVY8wL-KN=y^k_ z`7Y7CTeoiIk8}O1YaW|~Pk-}UVO z0Nji`5>wG`M(`sIlm4Orf_wk95$x{?{4aN)d^mqNqe3{Z|980nuer9l8{A75F%Skj zte0!wlNfFo+itel2ow@>)f6&UgnGubEc89+^7zKA-sPvn?CMOUV=kTaM0#Uycb8pnRWbR zdYN4C%uu7bYKC#KU!4U)+(mz>U3Vp|n$o?O7bH$cnr64!Yh=+w$n=nG>hj21z?A3) z<^(r$;+{)E*Vj8Dh0C)o+A;%=Y`mwU>D)wscH;$J7&-G2f!aa+E|<6nVpCIr+tgbf z;n>q#i$!m8N!qk8RlfF?lTK6Oh?cTp+>P|w$Iq|K5iMCW{OVG8Sy@@_dkZu7iqVQB z;(gYH06!tU#^-uc6vh%UDGzMUR6KKuz%+v~cO9+_n)jlCW8E>0u!D3EFz@%#Z$Y@7+~V7FJtU zf6Mn_NL_a;rL8YpnLjXc_on8#tlF}-#zns$qolh|d?h9jzwj}6QnXrbs*;FCr%#`z zEswfrAL+a)SD=j`>gLL>Z>t!bs1a_ant_mIyhBM`*^fjDw8!er{EVAG?!tu)qRa$Z zYU&d;=4ccq(AdDG&51Cl0)duK?N}l(ZE=iB#<_y~NVeG)Z)`O57Z9d@6awfMe~ zJwYek5VLm^Xa7_zdK6(G{PnP@dYG`m0svTw+8^gJnmdpb9W2*#K>G&_>pB~$Nz8e! z)u9IQvXnv(r?^emsn*1k$;_%~+EQ;yO3M2??9^^Pkw3>5i^kZM>?Dx)laA5yusL~A z9!A#?H~VT}V1S!3?r$FF)+3u9gYC|ma4$B6>KM}3QpVTPZT9Wk z2RilYw6^?VVr>SdvtH8)++LmQja+*p%?-U~KEV}^PvRUIplsx>0CDd=7jJ%ywPFo@ zal|aT&cdTity%hDw5{(n}KYt|B`E=>X``UKBC zJD+}05i{J8Up_ld;NW~$pmqi@T7Iro9Ph&pher#hs&%Lfp9n9^e-h9LE*#Goh;MhH zdHwm;B8|zgp)9r(iw3A9;A`p2XG;2sHwkW)>(Ixb$1~~DeKA|{Syp^`PAi4EEJC4I zAkSI2&gywFP=1yG|CY0+`dSL~?iL&Ba&wLMEa7+n(b403)88vF&d@2yE~?~B&Q!&{Fel_4OnuC2!8MGF&q#Z=1po9EF<%KUxI%gME6t;Uz=@0n~e zk*%h6*D6>wvu;W{$C1bEs53c0(8E>89)SK6MXcVoqpi8@2n%n0*H2MrerJEZKk#yq z&zk0-N`M`QvXrvlNfOC*I+9Fb_^er9f5KEU7JL-jbwJy#Tq>(fIW@O8%8H=0!nzr} zWzp@^NjPDG3)rPpCV@ipGP5+jLkTL}-23sgcC4(6HI8XVc)683kro_29BPz-?1;mr z;80lu5)-$nUcN|ib1h;}Lb;X9(6g75PxW0peJQi%l(6pgM)Plh*E{iXN$UTsf0%l> z;+pOyJI#4@GcKzW=D;P;QhYj_K^G{FC zs&A6%|E+kF?6W7Sd4o@2$`kO~dZchJBbklDh6MyXtbMcnJ|OlsUySm+<~8uO59b8LVlz|YT5&<$ue4G@nA zSmX8Qa<}jVa z9C{k-s4>eeL)@)D%Frp^%sI(wtBtY4l!2tkDkv78nU-RP!J$qG1%Q8JSHu5FAx zQKP9Hdg#K7OYg&k^gch=a~XP`?frFYJP|<_AF_H`;0TQ%(D@mEDCW4U;^tZ`x}rd4 z@BoOa5mzkCE?xR?!oqZi-{ox`jg5pvLC{sC+7(&q;-QCj`8|B%+}t~$O<4f5qfN!l zJ=4#foU9O`fUvD39gj1X3y|{TNNBRV?iMbO*igrPMK)kxP0mDWf(aLqmx{^5uNt(% z4c)JJ6C^x-{3ic1-Iu+HOB=tP4>T3(*V}@@;HC0N$~3&|5H=_a4e|S$5kkK)%7!hf zyMwL?=XM95){^F<_9J(Tj*L{dhcZF{;KY;4q*YMfKJWxCG3Kb0g@I&l=O$xSPNy?m zlOoJX5!mI0U?T0QasRw>ri8d6x`CLZ4WrkFflZTz&;j*>!u;*Vrd@uWP!yP-vkMRJ zx(m6_qtlnYI$BEP50eSwwEwU-+{l|gWH^_{VV%=?``YAvbGfda+{^u{tg^!p=?Q0Q zo`Wa#L1tVY)S|%2maiFE_-@UI0dMkL+_0q17G4-n7HM?+x|m^;cN!+oG>Df$CZVU4u>++yhaF` zC)Z75jL-;&5wDS(x(0-mZJ)UJgixyiie7}?sFSK6*-dEgcBP!zi=+9>epT{9K6Nqw zu;u3h2b%h*B+(Kgx{1S%q-6z`fM5mKanZ@N%G!%%tC7Rfy<@)>oaDVcYqZS1-%z zPqc>+;MQ?{Z7{*~sFz2+WFlS}1mD;sv;Pm@mE2fNs-H)vl7eSCTU135?>0)&YS!tXqWfRfLF6($rINxk@(b)U`&|y`F2Abs24yIK4AXNG~hKrQL1Jy)mDVjd;^s z-wReNEDp;Bcioc}W*q~WKOPLB`m(ARe85#^mBMh~k&(~)bHBGip_MB=eQ*}`mxU#a}49n8@(3fQ&`nWOwb zvOZ)Qb*MT}b-Nq1$zG^FtT0%0)7duUS+F~*$MQ@gzcgdMs|L%0EGLCCLsq0ai=*a3 z9B(wDEdPG&jsl@Of0HR>e_(4^{z9&?t8a%wGJd{9ve6i^e5&{?*VT0Wd5HhMQKMJ+UyDunsJ08Ju8+gOh;62U9G-Q6;a{*GTE$bYULH7!VO zhWLme?|CCxEi2ba5sR*FT2Yr>|5lg(-hqGHf$}C00rG{+tDgw5vQ%OA#kV-$Mrg1o zTMiNFFeuFy*i=?Xxho;`>gd}YBh0p4z{Ih5qg26kqDWhwljR?8y&MDaGJ-Nq2yqaD zZT={sa~?IFU0$X!mkChB$aJ2R%0i-UvPQ&JF)MeXgo8YOopm))3wbWK=xq~iH8lbe zV-@%O(Lb(>$5&_EB#unH+d8E-n*TLH_6e-qiRc9wD;!n>ul%MY>?gfRXoa>=1zzhk z^jQe?%&Ie78&O^L`8Or`HPflA@?G_xQt#J3=Fr4zcwjOMe&>3bbBMwwpqB^w`xK8>gDcNBApkebt*QsOE0 zITpgIK)I&!FpzJmcLY+8M!2vhan-Hba)-WM6)|C0jnK^hz%LSM+(LXqF~EjR#4yI} z-_Di=TlT-{R4^?d$^+unM|jGBRx4*4U+y}BSc&SIED=X%&%qG>KhIGApWcCSX6D+l zlLn3c+@(fF3O3dgXAiBB7?<2Iar{8u#;4C4s`YPQNpvgVl)L)0JK0o5Bs983f6Bt@ zQsSXw4$;#0<-Ryu<(=g^tP_@Nb(6(-Q=-WJ1Lemy7t(0bGzU3V*34xF$(!y*=4OSJ zoi8H_%3 zjzrFqTqFF-^lV|n|wUAywGwPfv8_KT5mx{8Jx5Hv!0 zCH>~LGxG)Jd;GYT7MHyHtJJO;cYdvy9Mn`OOI_T3)vn_wWD4_V(>twDJfD3Nn|+C`DI4ALnsUDyZ7&Z zaJI0vPL**flvWEnbX8-^mMtt-t*k!kT(PjYzm1J8iTR;#YG4TGp?S*Fv!Kv8$|S0* ztBbRA%W&;pnfkH0(o+6E?!1^_a_M_{_vvw0IUmm7Wp1huHor{1=znxi?IGUEu8HaX zI_2~mw6QT)jq|V>B_*GW{=L1uFZb2k#mvvU>XtkfI(F>XfyY@{=Zq3lQ?-H{hdxoZ zv9l*LOYC-gg&euZQ})A;!yQNNm3Gm zeQBx3>-MOqsIB~3k9~uKf27hxQYcKuzpOGWEG)}qtQ|)5*cT7`_X`OTO52K2lt9JE z#)lxk)W?q><7;`(A4A82RJn3J z>656w9zTBE(6w`of%ujBN62m5<+xTH`%CP}{D{Mr=g>%GXlQ6`Z0zObg}awxi08cR zzkEMDCnpC2)r#+qS6y70OM38%1HCz}LfnodxN~Lnv&;WEALjbP+KIV!Fe|@-XhO8n zp3SvV+WPwXrlyHCeRuBM**d(LlheS!z^n7EZ-2i<(I#yD#*G_iW@hljhf|I#@X^sd zdzw1SLj{9^g5Y_`n70huPJDe&WEz7X2OwzqZ~`9koQBvqIP^}Q+*`5Zzq}VWvrghd z=X;5Zn~f z7CvXMwj6HBPJ8lXu+yKL?cmQRPufj(mwoy2<-1k?m>zCEZJQ5ot*@{D`t|EQPT75Y zeD6z2SR{cGO}P%qjYFK`W&xxejceCNUYL|(zZ&yh#?V^e>+fH&OU2?W%3re(y&w&r zHW0t`?pHJ;AQGIh&G+;3o9HT)I(%4uX}s9yDN;Skg&ffeJZYa&q!|=ko=wcFxYumo9ycIApR~${m4ac8r#mM%4b& z&c9HyjfGe9X@nJ^)haVHljZj9+ZVE|nvs@r?%E}=e#gU^;ieZRU*FyGrW&2{-^7`U zrblF!#r~|xiHWagKI`$Q-rJ^+0Cersr%%{6pQly0QAL;#%WbrI{rc;ELBV_o6QGY! zm)*xZ6w?|7bJR37t7;!A!|?c|?=_%5^bWg-H>91?KyGfhFREd2UbDJIBki73Rag^C z>TI1V_t~>&2P`2{tV?x(l?L&svTfh4KItmgmhUo0_r%vu$X?n0M%zbLY%1 zUk02bp!fFlv>}T|V;J^6PQlCF%WIkFoB&BZ_R~-6gfkI-2;a8O#uKOuYDBjc()m^d ze|3d;8wBsP!i7QL(o%PbHcp#GcoNBf20|AvZ+9}W9-fNgOVTfaBfUEq&^gg+)JF1cv7p zx!t;hCMPFbTU#qDPZSw^PBWq)vH*>DlfCuR-=Mz73&TVeA=)raM-drSOM#mMu+Gr1 zJxMbf{SsfW+M?}7!_9do?Q5y%QH79v-Zt&ovuCe8=4MCR zJV04mvTfM)pXB765AHew1NGrVV#lAN9j|~|X5PI+y#vYd-VAyl=%27b-p3avs<}~H zE-0(3m-WR{gb&Ee8wdq($~sp?9mY01NlOb12-v32D=V8%&=NwP%CrpcFoYfJ)TSF3 ze^dfPljqXjvl;8Y*};Y=&XR{Z(5JW0v)cty^71a&rUasv12c5mZH{gsbbb8T`t;GG zpH9Uqcqu6>uURi4DLLBwQnemd@$v10Jyq8Kzv36xnMJlMb` zciuKgJ!IcN6dH&*s2o9C3HYW=7Q9d zr%xBB8?^a0?vhZmO#rTyh44#BN(!SdTS(|yLz1SC$98sh4e&_ot5+9>jF=ZdtaNmw zENXvy@ZdqA*CGJu{Q2{zPxB0{CC^_UHoN)x=L;8}{068<@x{Y|ek6Vza<1v5)|gX1 z(}y!&zU1@0d6SS3ee`awN%6%VSio}nYT~hc9~)=J~myQnq0SO5SkT;>9R8#~A zu19L^fz6}(_MxUG+!3XUGbHcJQR+=IR>t41Rjb0_gp|gAKTg zmAIoS1l-_FM49r-D_l^^$AjpMMhz7$9YDj%va)7u2&M$#a;7dpb-i_6Ypa}T`2&3I z3QR0g>{$9yWJ77*GlJ?}rGeU7TAvT2^R5Ez(^|1IGw6`DYRMWwFD{4Z#a}vDKtO`F zty_K7($ZzPX#%R~U=##jqHHQCC|FThS%6dqiH3gFy61ZZ1da#O#;EQyS1l|aTRZlY zZ(rl~+VDGDUxR$-Dj3bPXD3c6DZK@`N!E%L$T2u|-v`}<5m8a@*jj|!co`=FIXQh> zU!$e3?C|@+hU6M#fCrl%nv~u{)TwJckC2JF@ZN#`x`b8#*sxiDub9{=MU-CM&@=56 ztKhZp3>p|n1Hud9kc;ZwU3@y?N|wLgx^?nrpaLkrb@vo~+s8~xo$qqW4ogX)!M(S; zTkZC}dxizB6A-ds$p_9SSvIDkk0$~bWH<7(>#u!?TCI+)FQUE1F0?FyemTZsQtEk7 zaj+gfr?GZ<9vYNfyb|2N$lOLC?(@G_`oMPBxFs;zPTooz@FaT<58l92|l{%g$yfXy)fZVTM56f=HPZSWoKp}nfc=zoS zc&r-8b@ib`Up1$UV~!`?-pwtSur%Wj_xF}WAQhQIeG~{#cekCw^inr~Nq%}7PHL?@ zmu46n9X&MR3U}zOi5JOfqcLW)mzI{c@TdfElMRLRb8HnAi6o|j4UK-GcDb;Inwx_R8zaFHHO&9Wq%-INxYKeevRuprA%QA+Avv zo<-lIuaHis$D@6oKGm1+Jo1ENghn+|#IHpBJJ{g6pL8PSJ)Etrtvx-L0IRDT5lbgr zPi4iA^QQqOBJ)k0%r6%^3KD_9EGC@U1H{(%Zp*LfR4xaO* z>quTwdir=Hm67=f^_6_6gx}jX5JwN9nr8SqDkx|IEkE)H51wd5B8(En%iw*QQ3wXS zIUk^BKq*{8Lzn`7(D3%=4|z$C9$l{c{aEm+8!NLzkoN>FK++Zw6l4c_iP8i}s8qYF z2}ntBl2+^jn($Rg)@G{rcAu;r?<}5~!6_3Kq_acp@&{ycM29eXBVVMa+m;0EL>y-q z)<1$|O-oA)2i-W-R^av$MgnUgetZL9*nPzIV#k~72=dwJlf~|n(I5{!2I19tv{hPG zR?jvZjZI*7adK|Pt)VTA|rFV!mU0=VhcDWT0&v|_z|JgLyoIxq!99p2IQ=*`d5MPH`cO# zri4UA3FN4qIr9;!6!8-);x`n0e0-S3y}NfcwX_(hV9G+oN>3Lp+Jt73qku+&uH>9? zcAgD{bOc5Y>J3nnK^rR{pK14pAb=Aj2I~hA5%Xvbv4LKNFai%@*|?DtCx#>JtBi#H zWV)8;)syt}bZJE&uR?C#oJJVSYP~iN-t)uP_Y{5l$EMv8At5|XykcVZ*7vPU|8vsl h|8g$-f3OthdVQm)bW_C^;#Z0!^k zO)8ZJ4H{{lht;%dwbplBw`_x=9;zIX56?|uBStaablec#u8UFUh6$9bG*SAW)4 zU%7n4atejAQsdB1$0(FVsT9h>%S*q*5j7sOB>cC?dhe0F6w0%k+^my}@q59=W9s`T z$rXZq__6Wyp<_oV6b}gs<DtxZ)Mg%uN5sMD>FQyDOtbJQ{MG2kz~# zd8`$d))VFFC;ay9+u@vnWOWTB=h~Lg#oIXS=|o}&EA%a2 zCg=1gFe+5!O2XS%-PdJv&RAH8l^C_n)?LKyJA-rzX8RN6@U+u`yI}8MFHsx)l}D_R z#+j$iHC6@&1avxRhRW{Yuo$%2nrKspH*1RpFKS`z`sC7$Nk*B&35m&v9pB3r2LuLo zhnjeey~%9#7kccH{0*y)8Bg+ujbG;g|)OT)>` z78lKNMl^>dNoAIFmxoU`*%dD4UiWoeoWh~4`pH^pO}4o$u5I3AkwOb59u*6FO7nJC zM0(a4SPQu}J236CrdvH3dN2Zouz)?X>GDcS8O{TB&z?Pl{e5`d(9i(u!6z}PljqM% zmozl|(tFa&%j^94^A;8sI5Ls?ALDTqF1aiQBR%_A;gEpR`9sH!%X#-{+7&Rn#Z*S} z7`W5?bX%cqOHRj4D!X)RigAILdsL}y?oddaW4(#1rGi1O%aB7TNArwT(e1=E38&{% z@2|uJ#7FA4bcc?&Vr1Frd1E*1SnXWY zELgsna*I<<^&`!~>HHDzuB~R-y$`MjV&hqcm0sP7jLzZQkpi*U*w`?wgX-#7b=4yQ z+J_W_W869f+T2RSLpvR=ualXb$}G%p^O`|v z=MLYqm>O*4UMKyIcM#JFv!9==m-{7kBeAKYmbcc)7~Zic3z6yQ=nxeZWm@7RY&5`H zRg|39x4h-UnWj-o-rf+f%jtXUJ=G{#IM~LSZ&UodSk4VV{ngg;CmzW~s<;lr?<;n8 z1f+O%ZDq-+=^iqC@(8vr&!fW_q^jvP?T<(GE>-MllUnibOD!n{iFrCjO2V>B*i$}H8FHO>MR`3lv-UZX`p+Pk;t*aFRsv+PgeIAHk_adOFW6@SzU6pRz_6jxsM5UK8*05Xc{|z zx-}PpMgab3Q0Z~PCy7@_RbHoyuW;soM9|zwA*V{{JJwsza@+duJ@tN81&r(S-?0wn zb_6T#SDbF6aVGj|YJhwvk_=z8xHR1e3^a8t5my&-){~G%dYW{2#INsPU(=K68n9jgG$NDFhed+1-0zYi8^w&Fj@N$)_f{E_k`< z+1fZ_S6~ch*(@dv0in=s>!f~jeRnzBHm5J6$<_!F8hAa3Ku$yK zBL318zoP8HGV;yw3C91uI5npA*OL1G)e~rXMc64}%{x-u%eg?3VIY7tKazB)45im( zoNsi**{`hip@Ol1vaMIkfN3UxjSM_;+;8B5oo7?Ia+@H2XS^u=-upARSr(`qj9*bf zA~ScGFu zo3h3T4_w*sSZe*IRHs^;;Lo2ui7?jiInlZ;69nCz#r zJE&?{p>eA7o3;9#B{HUYJ4>3gFDizq&i2N7vPQ~dqT>aWu4ta-6 zc1kRIuDM4R>5$lnSC2~N?t!Nm3MefCBfjVp{A`(qu=hk2Ic6OEt2;suVSsXlrHEq) zgRUY-X5u1U4Czcp50>-N=Y$pSf$a99MO;1lVe+RB(vic=BmG;2TQu$a?+JTpMyg|1 zDNey!@cD1<_3st?rhDb99t*I*T8e+aq!3*ZzsjH2b==dm(_b!2eoJJFl0S9}HAocn9F-0E{% zUm#L%P01TaR;a@441z1PkxPt&_s$ZcK%9(;$0=aEYH~t_+T0*UE0nybH$|>EN9ZVA zrTU0+%biY=K3$ArM6lA323k8gIh{X0_*TGf1?~q0am+)OY?a8u*p==<`2FOwKts^yx(=sHuM8>_-vysJ}*qD&sxL zHF4_|CYHZz@9Me}&FYj4Bd=9SV>1ZtV+#;~(Ife;mV`}x@=C(K9I&i6ZrlJL{a(oa z2Z73jDbh^pA)v1oxqgg~*!RGqdsXQq{2~%8l=0Zs;3d z8NA!QyULru|E^r&<|8KHRdwAo{nVRl&nwkD`>G!i5|bs>^bTkqs5<@*H14TszQ=37 zmCvm!3H*Qy-YpMTu8i8_3Gym3->a^J_g5_O~YcoW-^{Au>sWtJ3l z2wggIS4)sD0-`wokT1XY0F^y<6UOP&9cEnt8G-W#DLNtbVqC&*<`{?MdSj$Ve|)I1 zV+ojjI;^E|zNyf=h}-nmGE8hId!_^H9j*T{%sPr67l6(kBAP{6A*+ngId$r66|A(7 zn01V2-r677xG9pWV*8AAXOBiYA&;P554_4!EASnNx zw!rVy{Olz1)Xz7RTiqDt$~w_%-i7m1o^ZlEa=`6WiB7s3 zxU2?eF$1yV614vjYfFTud1B}C#e#}zYHCOfk+P0K_bP%+Jkv7bGktAqGSRpuuIcA} zAqp{El;}hx6@x+vaaKq~q|Gwaggt4-nM68oE0H%pWM@|3#k4VH+=Hqa=~S&@HutHX zSTjk=Gzn&-@f;#I;w^r@ohhAOD`#xfqBJaP*(%#)j!Ke_HwZas{ z3ritfQ^ms+EK9!$4dhjtj=W28EZ4lxOf*ajY3L7M zsoj5Y%=6r4?D0^n*B|P_?iP^7+0aby#-#yaRD<#6&|$%7qvqOpXB)j*h8Ot6&bK}d zjtSdE%ah-JaroMA`xFQ2Q=)e}p95d5uk@LE?piq40(m@>{yu8Y+*hF-!ALb&G;rr; zone8tnS*qGMJo%1Fg0%vw&M2m*Or_OyP;aQahD6ua*~%)3LQW|)lR<+096ekf`c6U zi(Cbj!y=M($!c}MF+?~kM950oYr^DJvv1$NEq1Y|qTGUrAWwm-qv@%Lqm_{=%H?`~ zUmz}fl^L}<0)!>p0g^O?MM5+Qz$0I6W%fiDI`vgU<_0o%y+&jsQvW}VFhRlGY_>X7 zTaDR?5wF2UA#OH}URmBx`;cr2Pqy{pHPdR1xI#3HiJ$DD|5Yh=465f=}sZ zAwcatPWY-utimBPh*0!Fq&;S=FMN#7)=@v@LF^To(C^T&!C}RK&^}E~ts#=G?>71W z>*PX3B_)?)A=j^YNxZ@~h}JjNX3vo4-60i=e*?|GSKz;_fS(N*3vHB(X8&O?SEOsJ z`!}nvAMYxoL2Z}$Qwu$>8@$O7H74Mn8A-fI&TE9kHruf)Sl^VXOk*g+A_=7l`qA>` ziDct!&`!u|;y9uu5xfA!N}^*jvY?LHw&v}^;qx}Z3p^_>MqQj6%It9<;uTWsd7Ma1 z`ZGBjce00i)fo>TD12w($eEx1nn7oA*mKtJ@7}hO(%}NJD${lEjqz6qJKvlopPtSR z_w+YadY^U1rPm^~%734tvcIWzq<2$1&CTdc_dOM}sXt}Tnxe&g#De`(caG5(6)86jc4N`iAG4CD;Z(GFRJ;f zWYk>?%)Zh>0YL>b*Wm?!|Ku|_RP(JKi(~&~wekD^PPr_zNDUFEwk`Yn*PZ_on&8yV z-cF&ceL#8v{#I4^|H0KjwGk?ZT*1s2JK9_mxWF_KK3*>15=6F2k|`2zl#cP|ArX-_ z&6|OaTiL;8Mst`E7b|uRJbg~mB^ViD=Yu^|4)e3WNCC8?c-=T*hd)-+Ce&$8pjx!a zL^BguabH|?0RpNZ)EI&Yy;2KZ5EfHR^LLk6t1-e(-`u0bOXL~W@CbX@#I?ow)7dy7 z=h~w+aXO@yP7)Vz98^^nYnXCo>qU@FyEmiii6V;&{IwN2Usj~I%FnU2%8`=V*Elv-r$1~W=cpV!Q0l7SV^0CE%L4Q zQVk(6!lE_LUD3MY{nwJIhAlWG-sx$=cO07MsIcy&_oxHw;*9rR*@Z>tR(~%wx#W$e zw9UGf{^r_U@o#rN;#p_&=+nJZpA6TU^o#F)aj#A(@_61Q-715`FXD}<-Wew%s|;Qw zKWcsB>+497@lcW${FNalDIb5^wo;_wW828Q2PRCJ5$12vvCT9(rll2NxJE!=Ydj>z z$B!i>Bo5*%<Hb7EiW&R-0i%4 z>C*2bV)F8udl+xbes|3__Ekjj-Jo1fQcP@VMLUC8T2?mAWB;bOn7iUbbF=Y@G3tT^ z3vl6@h?w~JK9s|RR16&)930oj&TUauHKrGL9Mx16hM@8K^`zYab@c)2Gign#Scs)Y z8f%ets&rYGqd~~p@4w%;^EXQawdl$;>CiH9@|`YSwkD#ehhY%X<(U2T`?rzinDF{K zp|4Mnmp^*!z=11vas!QmQGZ0NlDEdmmxz-OG_;O@CjpM=`CBf zlyo_gWulDEE968kS-C;15IqKN%~=k!A+xiy`~6pGY*2MeB_k+pDzS6%h;;d$)3UO% zrrC~`R#uP8x~hf3l`f3CsnV-!YQWmC%dPw^)*zWjbabeWo$p}j=%KMQ2zjVOop*u` zO0TZ2KBlYtudyJo{8S&Wy?cuu9Juay@gmoX_4dQf*|63_*KXV>FDv6Ew{0&h>SdEa z+fxx4Dq}P?S#LJdns4N@-)}`pWhJCzQZo8;l;T_IUsBC+S1ummJdeq9KmGL6xpU`2 zWlg{ZF~1S{*@Dad{&9~U{rT#z&A%SE7ZW9?FNyLKTcrq=5Iu*|e(0-hQq&cY(NHKm z>8r_qFK-8s|0sbL|HGFKzWV2eajd>QI+iQCvZsbe2l^GvziB8iFmPSOCIy9Yw70x? z@dA|JS8)*+m-6=QUO=^?q9R|#EMr*>wP+oCKkV_XTepzw)YjK?SJm}S|=n5#q2oAk(^U8S>UQt#BM2j=R6^-clVx~T08hCCgvn~8dR?{wzeT{S9vAH@^KpN zYUqLEUb%AZ;P4+;d3WvFWtx(imUb*v^k%W5`?!U7rgr3RaS4g#Q`HR(I}uYLTlEKa z6yLN1T~$_gogQvMe&#_3b!vK zZ@hFXnrY}09U`0=KBnCNY9H!m{17ap#8_%L3|hcnj}8XC%-QCC}gCo*z~!TgkI z#{pr_$4ifUB0ewY=B}x!u{n26LMJNEbK15@R!T}LMA~5U+1e*ZLGJgOo8M=V%pK_X z;K76Ai6@RgsSp=GkeWhtaLCQRNx^sS9Kz-TS3@lOghMQsO{HOfF;v3?y^D1zW+RT- zVx)5~J3CwVvDPlHnNhgAq}JVycqN-r4>FnHiT8??c%{C;tgI{+i*@p~-|-VC)~#Dd zc0}pev172hFOwdNt#^9O*gH5pd)WBg+I8sD@iqm#G>oOYr)Q)kw|-!9$>PP!GE(i@ z3IM+v9s^04-z{9|>@RNx>G!~a1N1-uNG^d z=zgK*@s0Ks&^_;vorGADZ7kbzJ2EmKv`em#Jn3xCWGY)m`aJK`Rq#lqB8 zVEdV$n=&rI@(lF!?u3PHY52XY>)O?;RYJCzxiK+&Q&U9M1K-(A^j75*e~8)7U@-Dc zN8nQ;G?qirx{9bhTl>K7hN~Z49}bC#IB#%5SGNXcxhOmbI!0$nkT}e#JYp9EWl!HD zWYPPpV*T4!?AWnmZh9ni`^i^}%F>)N(S>1}=OzzYvda8&o&8{gyhey6>XQ*O>Qwyl*M&TSJ2y7ee@xUDzvAy-TwFXL91{~Wp6flAVa``l zQj$_Id(O)%Z`c7pQn^$9PUm1}UzO*%3jdnS#w2pE>hn_%^mA{^`@F&z@BQxp^Kx!rYL- zZru2z40FJYBeiAY#{23wg?&u|7`mDva%Qri0yoyn?@NtEvz?%tR~EnwnQOASD@>Dq z>eMMjapapLp&=o#he=dxV2~*BoUKoeG?$aqiMsQ{>eW@xPOEFwqd4*9y4;2h8$6~4 z!*1Q$C@H_s#y+=&+@zL{B8mnI8dza@dHL@~&Chkjg$Sy+oCg}yqkLxj&^1}~cxt3= z#3i?^v=p}FJkfhp<2s+5wzl@V^F8fFzuD!D9W*y@2Y&|mE;3@cseb(Ukq(DyZD{yM zVN<3Zt)G;_Vey||`*RaR-y9FW#~|G}?Un*>&(o)4S=h8x2&gdgQvQGs8~XeE#Xu1g z#5Zl)G!8W3r8G4@J6(JoSq9j%>cofrs1d9gwCS&nH}e|1!h#2&gJ-@fR-HRT*T^V5 zEUdA%b^^tthmRjGchScq3%k3!VThc5{X)`!bXZf9GZ9BkGR@CPNRSd0?LgZKYS+J; z^C3pl1LSp}nxT{{z6Xywa^#4zZ8K~kZ2Q?`M~@yoc``=u<{8kEclyrnbbf2v3`$7It!TKw^Cs04yd<@i^~)|(Yi>MtWKnw zw_!C3KDlSA9$db>NW;1^s=T7270;@ORH;ufiw+JJC)l%e<;vjN4!_}e(QEeiuKlKHis4OKteFEjVCInZsC%k_3D((66#kudw%C<^N zpNBE+-TP}<*BU|d^k_qRNJz--+s^OyEGAv5g6fMYc9g!`>)=v&K>8D;n>ZAb!LN1k~j_AeG zj#4EKX@q?`q^%v|1keFo-MzcMB`GWG7ZMV%(wDdzr-qu)GX90Z1Xo9M29TAjr~nR) zaFu5|0`d5h*^Y#WYM)>U$Uj`pdfJaoH>~Ov$Hi4H;YBL?Ac>N5w-vwNlr>yxqP`{u1k{q zLHkRvo-d3_s+_d6ah{t4ET-Vm_d*XDx|3@&Yd?M@ z^^;>zBR*wZ*h=V3n2M{dhsW$~QFUCeOk;bMrHOgZ_QgTTpx=S}+`A`5;YmYt^WkfO zfvp)A!V3ru-7G4)*yyg(1w&+Es1&5&a`tB)T5n5kChOYu>yk(A!B%#heh=Lq+oI&d zno(}^s_7V8A)@AmeOtI>#jQ5d6Zwi;E~{nu!-o%O_SQ*VefmB5i;MSnpMj1b7D21Y zgm=+p(Uo)Z)nzcdT;pVr)^`g6J{$&;DdN7qzP@ON?&|JdwSHSWiU(-wy#0fKKzL}V zB!wr7PG6&ekPXg|R?q|+L09!+qeF)e-&3}0MM$E8KdO1p{_*y$3y>PT6tos5ZHR3+ zcP@dV`tD0d2WO_38Ys9c?dek@{DKAe-NufIT-(e922y}IYu<9=rAS9;)3{uUDuzah8Rlj95 zwY9Y*7F49tI6g%M(q%cI9qor=58l`yf9j7n*ZII@k=?D~Tqfb!U@_LJd1o6Bc;Yho`=t&5-IH7xq8>t)rLlHZf=H# z_b3#lQvrRSB`Fyposv6CP)3Z!>XpR*MY-}{lcE3l)Crn-xpUHncL~Ylx5feOpOW{P G`~Dxhl?mPe diff --git a/test/data/random-path-7x7.png b/test/data/random-path-7x7.png index 32ecaa620551b7d2e501d1d0b27d6770424bb242..51c2a09e67c28cf0c13350268451f4ec565435a1 100644 GIT binary patch literal 14584 zcmeHuXH=BgvUaQE7)awq1q0wHK}53T&|^d-iGYYCk)-4#8Jdw~ASkG05J?i7oS_92 zgeFOl(Bvkk2AbU6aO-u>IqR#pyfZ_TW8k84fa{rc{`YuBz_^*mKI&u^b^9Et^l#apI`x)@je&aEChDJt3HTITH#e1ZtrA6@TiVF)L@rh&i-W;WnaF`wH7GHFSWV z&rgetj&`>rch$+XLcbdv&3^O`ap@YzwkQW%MD#eVw_PBKYH=KZI&)eeQ#Kr%8*p=>3YHr9XDw> zmp3TS7i0RZq-2Prz84dVTdg5ipQ`Wu@P%$?aa*)g`A;&9|B?3oB4+%5S=QQ8X{*~w z`yCJXUS`$ChxD$m8uQe#W~P5rw~UXH`|{{gf3-EiPBK>1#b*5|`8Yk3q=^BmE!if) zvz6*^q_@zUXH43!(I)v|^z-LytlDZ(?7^)==%BnR3xn-+p`7ouwI25E7u^XO8OfaiKaf>(>BK{(H7MrMxm z#2bz8Si=N`N+KIca-pzOpmgNv7J2Fb#I8ZjD3y|7ZwicRTX<6zqk(BP-VkIktB zTbx`Y>~^(DyIKUDsU%pn8t<{F=rEHK!R|ht*!Q?KRw_c@xv%LhAE}fW66!gAjwj9u zun(%2?QfFcBLv&pkp13TshpA{`7x`0O;T1>tsZ5t-CPPpQ;V=(3%?&=zn5>K2*X@E zKxHk_Vv2XO?!8W^V~`klH9jGTabqWTW4+hl_0eySR*|m~-b4vR%J92UDw*0?)%=HF zUd-iGEau|{C_@|7B|Qcn*?MIwAe6^irePetl7%J8`DEDaKJ9S} zDs!9?(8(X)0SG~EmD%@)&Z3JfdqQp`aa#7|$j#^08SJb?a5(oGxeVAhuv9Pk$JkbH zMO)@b?Q9Zp#}ib^nZa9DxqFw=>FE2Z!Fb>Hzgw8?FlSY_#! z*jzg4zg%ohC>9zjwvM?q86Ye!7tLsF>+XKEo&eAD&jb}L5Q_bm#ZLw=mQBT9(ufiR z#k8tomJVQ+@20B}PIJ z-BRgB4zudZ=L(%0@*uS^N5#jt_D06X4^8>dCI$MB*Z1l($r9K3!;OOE)@IhmT(Vl@ zA5QK{L^qzP8x`_+QDwGXw%EU~&Q-V6)cqk~i*Jw2hyjfZI?`Xy1ZA$xaZl+>o~ZAQ z^_o=M?U_LYZD9A9@2Orddj9;maflRmsEDwz{gB&~*94#8)^-oB+M+un)N9@tX0H$e zv^HM|dZ1p?(9lqvY>kU)c(O6*V&20jIpV{Kf5(rPbaU+a zMt<#$jEp=;v=usNY=ivw%gB@jEB#*T09?ur<%!`&a{NZ+yc5B?gx|1Qwjvi+n|C5r zZA=O~&-dEz(l&T>%AFqnVVW2o*t(1y@kHC#9DdzmD+dRM&Qz6Q+M)}Y)$)7RiQpTG zjB=~(@N%xqg@VaVVknMOO;MHK8h31ylL)nkMYvAT{~XBS?aJO`-@vw!BIq>}aLVSZ z@qk?&L*EQs6FL}NktAYyH%HHH5Uzpis6WYbN`{61{u>3DpGDtEJ?vbFxm3fao#VOu(y-E`F-*2TO@1vMZYhsWrK|mpOIoZ$69k+LydaMH zUDtN7{K*_RQ`Zhf9yaiBzyW9va6ApTSE%D1#{NZLwDP4x#!YCX`)Y~Ip_w#WuBGsfR2a4l_?DUe=aI59Jvz4%O{i4uAPUC$l5tsVuCupMgt_G`5ghB8e7LiIM= z+toe#;^5iG`QGWHg#m9HzZ>7Bg+}--uh$=F+pI6Y%xWyCf607sx^@1b@f;fMIBAL_ zyBGyA1ga_RaJFc|o%+k95kQtdH;?ge0sgxNf-=7~zlbjIf^ceCvuYrCuT%{pwtnGg zio#%o+?VD@gO^i9r4uO%kjKZ=^5%Rx`H!ls254huSg4V*8ursXGl3t z{sf^NWBazx1!y#8`YJ~`Ab`d4xtxsqc@Mwj0hU|uGZ)k?zNpq}j;8bhX}DCeG!I|T zactd$BSomeB~ekw+4M96hej^9&+7{WTtTNP5Q&=oEYwBPj!nED;1eZn12Bl{pS4wTvxivaDnN=;ey=8mp*s-ef8oePoQo|b*x zovF>EQ*71u1G(4#8hH9YU?K>$->v!!XP9=En8Hs(25@+@UIKxUaR~God(Ok|HvXCk z0L@HV!VUyuJ~b1BkPMY+z~Ig6ADg2w)CP>_$nOWjVm%gt1GKgz$xcbNxlPFoyMYv* z6vFTj2vzG(S6~Sc)n)LBcGxk#yNK2dUo~ZIt(V~9;taMsA|{0mHfve9A+uVxH5p^L z`hmr?=X;L6%*o)etb2K9VL9qkO|uq&JE79EAAz>Xt);8A7+VchO3Co+l?sJguFZBM z2$%xe^{}w8fS+^Uvt`C#U1gM>eh=f93No6bT}W^AOXu_PVL-bC0BVL>BKwJ74l?%y zHb9+LK$qJS>*ovYMr$?HdR&j!PtH717Z5Rih~rQp<7-j!6ELaNkF4s({v*-csN`ygAHsuBYTIbtp`kX@SGEV?@;xO28q zU4d;45HpXO32d`(ro?-$WM?{bXF9iz@f7{f2*1)Xk6SCjP)ZQ$MO*E}Q6~ha?Bnd~ zPq>|(>%q45_U0PZHEWoQ93)z~H-wm+4a#GaaaQ)r1IAR_dn))woe`_KOe#o1$TL=$ z^s|TO;#r0XW*2Z5J3Big^c)(I73w&4gc<=uyR3CL zR?E8Q&xMA79Mrm=Vmx3p&k*PfKLhHN1MMMOO;5BIVub@tjcOA75-c&aNr5ww7Bg={y<1coe4XubxdeapwX3!Pq6r$?E6I*Dx83%e z_4VJgb$hH&`7PfSd#xNOd{@h;k|D58Xg#2A^wH6J9W7H2R&%Xm^^j;Aa9JJ$Dr73- zJnu>Z#{4?e)5OdyHA59mTLl$(;Arc3!P>DeFCzpEy~!;S!wz_U0G|(BG|f8UJbBY% zSEAZHxf}5Y>-k!lcM-yayN##~UabV<16xCg%S^Lq8@1V7K770qe|C%rJFwxq)N%ckpN*lR~27<9AwJ zsvQyjyM;GYb$~B&#lUqG zN5$hHYk~cmL?2jJf}<_iy5V&ZxNnou90HdQ+|g-=ufMvT1vccuZr?22AiMBbO-aOM zaX_}I0VZcwl^lgLpdbVWlwksE%B*}n2efM5I8;s?(Oq@ZJP_Q{^?u8O7~mvorYt+8 zI5RUdIEw0wep?(3S+6Z|1@ornL9WwG2aH*A%!JQBtuO>@|5FiNZRyEo*^~e=RzTI! z1rHWGj&&LZwG3u*2fQpugMt>x~o;FhxHa8niQ(a`MCp>D<2w*0_$Y8T#QY5a& zPUti;ntee|G|!OLSX3w08%ixMyBoWeMiv)>@*EAlHZbm^Jpi|N+wECsXoW!GVw2_Z zSib6$^@PA2xkZIJc6wv08jRF@Oy4i{z1WNQ-Fq-H=Y#UnE)GyQ1XLyd{EFN^Tk&97 ze}zKhzsRjlp&5;>%;sM2600e7 ztTNQczHW5YVAhq-zchMu{JU=WUyACt(flM7iq!(?1^BDh;{PdE1P~8@$-s)gpywz6 zQ~grA(bo#>Zjje&j+2q7cnk?wD8Z~{mkgCd5_TU1el4tfw_^fj09IcTO>fR;I3*tI zKAVp43HAvjmaldw8Losyj4M_EUPa0xYjZtQM@X&>G7ygo=>$q20M11KjD(h1C!hmE za&(U#hPI~o?xGHis~iUmGz2m^K;n6K8@sy=g$lbab%hDVO$07L%G%rA-CgoDyVvSB zs1EExKD7j3j+*BfGn4%0$V7x8JIID4{Q#29ZqH%W9O_S)Qzr~}CLRdgbO2tp+mEI7 z13ps&c_1OE>vk5X1d{DrU0wZf{iRLia{W~%xl4_RaFqTY??uOiim?}bL#wG0kegNf z8~AEz-tA$pL}}@q+2PoUuftD#J#Z=77XEQMp}g^Rj->xKK%74uf| z&2nPiW(M0trw~(OFXh+k*L?REcz5G_Mw!msLl%|&q1;z|+$JvAG44jW1e{y|K{ zmb}f4!R+F8*09UV%W0}{_sbk6{0=b#PeP$ej|5078yE~liJEh^#LE$c=*5Js`wM|> zeedp`7#|NpW@~C~eRq_ZIXpHti#t|z>J6_3RE?IQ*hU^Ga}@P)a+8jQSKT)Ma$x!UayLIMt&18K)*q1A|3}vaWy^FAhh=s;1p1I!WWEN)i|G@U}IoT8y60%Pq?(Sw9@rWO-;*>hc+as z))AMfyz}ceId*6?Ixu zID#3AbT-rgBPczpG)$s?xziD8zPd$*!nDJ2I=(H$<*yGx$M4^tQ3S~?T3{a2!v_!E zCOm!m)FdN2Tgx)Xpy~qenKQpFT;SzZC%CUp|H>;OawqONp7=Z{Xq8N6MnwYiZEAYz z`}AqK3rPU=$SD!;|Ki1q9Q_LEIMqEr)cMrcmrLvJ-3&3&edRfP3=jPx@IDwe{VRWaAKGt1RP*B<)4dj4>vMrzC7IOi!P^3{d z;Ey)p?74Gss^-_Pe_19rbYHoA`S+f=x0mcje*1^rghxi6Z=756hE$4iLx>hL7+iwa zdQ>v>Gw9>LzCMT8ukEmWQ7(K$-3U3&`w!FchEl=m^tYtsWX^NvjOTmvEY3@Hdg0t9i%>jTgg~AFA>oj*W4v#!fqwIjDryhjO4z zn~we={;R~0qeqXfLa?AgvV{hO=CyGcudFN;=q98py4K2ecPMx~-n=DR>-Oy>;NltT zFeRiNti)pw5m6FfZy%WZmec_Cs(kadc>N0JV=OFzetw*soXYXCQkO1i@|&2LXx_bh zYqq~AGui`qpvIj$BBG+9r={-b>kp=>B^+Yrrst5?LuT{YeU=zvUeLFAY36PIPy7i}?Hb$wE`WOBTWPjg6iskZT?6 zNKpc>Vj@Cs+>xvZ87voM0bi)~uaAeT9Y1y~{Tq@#fwUduWH%|4Icd+Gttnn!UW*vQ z0*G`3#WA|(p7h}Z`2Fqx$Ue+L@5Z+$y#?lNP{XueomLk+Dg5v=AGT7&s#j=_kI6yw zbI9z4u*+?a)cA^&>E%FfQ@_l?6uMl7J;^H0&c%Z^Jx?M<&0C|zEKLqGG2!e+uXuUU zko+xm1us~=Sv%oy$Vbf$+7}?}dNv$Carp4z3l}d!24)OWi$ze+1P3S+r>c+7Gj3>2 zKx2#Dstoolv6;8VZh@=pDu(rErNL(zH@)FCz-o7>NDM-d($>%*e_=5UIxV#V;Zs#v z`6vo6>X+~tNj)u%Tvh~cbFc7`I^4+1murw5<_2z;2Hs5{l$fPc;KVQEvZz;J7M%S0 zoC2I;roFvA8gvcqAfS=5&NMZ(1bL_&uaSL#{mJP>xz?8VNOa` z)T7H3&N{)?*w}dgCxzx13AH;Li-YLRk$q{Myc(hGav~xk5ayvf)({Y_KOM_D zY1nY#!uj*B-b6-jLKcnAhr|cJjvhIZ=egxTOEZ8>CSZ>`q5OgZr~q>(S*O4Z;60 zbn3%_0{GBq!w#Q4`+ge1TtQA^&%U3hch4YiY%UW)%I?nq2fd-VpCIcd!pAoUWeH}K zrKn%X#y15H#aXz;o}QJ_N~)^sWHR6+Ky`|WiWUTDAmG%vb?Zg+%NH-oC*J8Ya>&QV z#89AOu88ui0i!j!b@S$Jx_t*23;V1fCO3p~^b|p?x%na_pc95m7ML=x6i zz8c7=MF(1du{l_*;Q8~t{^QG!D`#*iE zrB5WjjfhY>Oh>0-SzBAnsbB4>vrz6Z@gg}lSEz{l;zi|DfSrN<{)c=72~ds!Uc!}< zn##y3^xHxcsDoEjv~$#so=y6ah{(#v8EB0ug_aqF-$4@kpiiizcVpvgeWn%*(kVVa?-B(Q=9o`@QrdfzSN|rtJzJ|6oPb2I?KfvhbimR-J zioSlFG7{xIZ=0Gp|!O#ArLVKe)}#n;x6yr z{r>F8x!KuSP-58Iw_K>|_i&5h5H`qUB{ek{={Sq73K!D+Jd!sCl!p|wpjpTw#)viS z?_z|vnEkk>jApDie*5h=Yiny2mEik8ea|({IA!S9)YOCl+V6^vjy{QSA=GAcY-}t~ zyz2*`Q+G5qyApP(Q|TIMv)i08UD1Aie&F7xXdA;2rkPP3M=bAH0#A&EaM_lCSsW+> zA-;aSS@bKAfv}#r?95CuU~@+}G=wAk;m!miGJiOE&1~5G>!a-KInHHz0*c^iEmRa0 zpK}7^%hr{E5XK%l1wIBWEeQ-T7i_l~XzQrmG>%h1U=c)csThQFK===egN22Kr&D6s zlljDnbn#L>fhYhC5U0EJU`FN9(b2sRtOMGY!H<9!8n7w3?>O7Vt0*iiY@rQNB^j)} zyj&WC!HBsa&#OE(7CTa2qBcWjI@4^2Ds?)>8^e3BhQn1JE;MZjxhi3UHWKt#QJ#hn z4owhtM}VnK9V!OZ)zvK)yV5m8zI^_C(5DETlG4qa2tEK(E*RQCoSI7G_6B1L=TWmT}3k(NkCd1}=fC zC00sQUfzH|R?_|o;<5Mc-TTug7KlsO{CtPzNMV@#E*+0*>?>Yz@w?V9LqjF#ld7wC z$ZcpSIuN=CYJ3lH!7j3GhpYe43$(qKK>c@RWd(96XSleot5^+J>!$*J$kYe-#Tb|b z>fz(FL?j%8Vgv<(A=e;OI`)p711O}OeV>b0PR-$_zK;w*fnPwN+I`L9#*Khz zh|54NN|JxQ8USA4ynq0-TOwh>Y1nI{4+2_5c=)+=uxrRKSko~uAl+~BP;AV~%1VoO zzop_T8xLOm=1B>g*g+eX9}!2wqU$4UffP& z+!^l15dY()k(#6%!C{S~cCxGOSCJY|Cbn7{Vkmgb#x4kv4{G)*3Pe}}K!W{1tDyS* z`xj=Q#jI8WUjP*iKDfkxf;&g(Q@42CA6=z~Lh@lS58pagBdf_-QBm5 zBIa~^BGmS}x-`Yywye|d4t(0gkFZiUC%>=S@k z!2KZMbn}+7a;&^p`AIEJ&E&K+C5ZQ`Dk@q@yc(%l@CM+-$B!Qapn7ua)-9m*GNj4^ zqq?6JVRahxFY}__nFG28WAp%g35O~5;Lf?U&N8s;%}rPRK!YlGQ*(3R4SBh_c3_wm zCIx+n{E+6nhVl#~EWDcmY-pkTAiXL27tIpWY>8iAv=icP?Kaq4I35o z0nX2cTqCe{5_5QTq_}lIpmpH2IbX~Qpb&^TCF5)bFS8oD5AIZCwNJFZ%*omQn;Fkr{moGz|`{z-F-gzj9+Z z=5xZr>L|}!K-AFxK}f^)>1H>-0!f&f^ko6FEo5fMGtk%EV2Ngg2edM!^# zLo{#~!-bDK0dOohSJ0@`hYug7La>I8{9Y^ZAKWGpZnV`vsd|qH5VJ2YSuz=p966$- zvUdaz{~MUm;Cm1BcncwO~c&XosG{}VN-OSJ$1 literal 13378 zcmeHtcUY6zw*F`DFp3Bc0v3uC0YODTz>cAgq9`IwKtw4HNDGj_AUdLg5;_PfO=*!P zolr&)A|OTSy@nP@D1ngVuFsis?zzvoGw0rU&htBS=Xd6hjC|j>_g;JLwby#ryVk?+ zbTzhY{B|RPAX_l!PG3Nf)o&4G)xGsA;fWfzX&U@*we<;|69|$KxM|sZ4g9^r>4L^d zB)x7I1^(D=aqfZ+f?&lE#QPqCEWso1Q3P?8Ly$2u1W^u05WYLn1?P{!rwHcsiHmM4 zCd#&a6khEmyX4@G^YVBxWVKh#Yj=80igzNGXQg7(C?C!A^NTiDpEsFm`)Jl6(fK^wUNC+3IHpHPa}D!&La^GbnBK{~ zFAp56sbE+2jL_c{dn~L4sc@1YTlU2j2=XEU-S2@*@N@GGZ1lJmFZ}arGW>kH06X*! z{TF@u^kewJeISLeHMP4q;>gcZO&7ohZkXA27r9@*e*Nya`NZ0kE?3nkyI?KnZ&wQ<#i`T?BW<|2+~KVD1W4|0OPZ^tm|k8PNL zmTloYShQwi(&lL{27U*}o!>V1G0VZ+`?)&Rbfs@n`3vkAe#@$<1W{onX&!TL_*Sl0 zjpi%Gy-0N`H!GY}XPK_(%{~8r?@ImKz~vN+N_ddu(x0pM$W~=CI*ASd->Wkmdes-93MI=^G0o? z6*<(d_3a*_$83X!L&@T>fPG$Ha!NWHi}Wu?=<2C0TH-7^ave+y?oukB2w2xeyLY>f zJa_(LIQ}KAkXph#V%L6m?(=2YD{sx-`}0B|GxL{hvSBUFVPN*dYHDhS4h1Xj{Px=# zqx_jl0Y&@HaLb{Zkg#ix7%^j~+QUiDUD}6hqlg7#$wTC@`UnMaSXujwm9pyMAORDg zI^WusWj)f8>9H_%DaAlEfArNOnf$?^B72BAb7P-JVmzk4q#03*7rJ?+3|2#!eDhj6 zX>PnlT&eFK4{aPf_)NpZbLPDWL?N5*(TS{jwb@UnTqjy*N>_2GUdh<|x2x$M?YpPl zo@HJC>C>mjI~8c}RN0IH%>DcKb=6t3MGYS#6!>IKJX?%iK`M*C-Rar0XVXdSC93C$ ziXMELYM7;~G!(2*cXXg&JYA*#$9*&d5HidXWOj2q>flJL78h@OYhDqf>XD2_^&-0v z8wEAB5{R85rpx_&3lnVw46$TsOi#><+0kQCjGb#t&~iF%VPS!RwK8Ql?T|i~XYiinKX{NO>oihV8~h(mX@&%q-{b|6Tvake{s_>g|Y5p-)4 zZCMZQ-=E4G2!PYFZhR;n<2IE)g1gmN_gsZoM603VIm|XvSD~x;o8$?tOID4rmg8}w zced3=5#iweZA;8)goc zZ;~yd*Qp5O7=6ZtF1277%!9{{MGuBtHmNR|XwQ)%4A(u6TGgIyqd!j_jSJM3S62Qu z{8b+lw{2}JwPcobuk77S=0myZO7Y=)KDk4Y78Quf$fj8CRSuJQejSAnytr1FOff7W zE+!V^*8dQ0jFmc=Vwk0;=P{&6~dSC7}QLToj$HRpBxoQO;Y#j za&GCub6B@q($SkmmvVtSFh|6zR*S36Jd9AVzQI8yLWjcC`9q;b2B}vLf)qtlMD`f5 zvZ{)3?M~U6U#xz(|B-A7B{2#fhshXYnUlG(poWOqrdal@Q!KNywY7D6dRnn$=E<_Z zEPj#PvplRwNWGTt*p_J(F&jek9Qt9C3}Yq`zxl3++|JB9edWvk zGcz;Rv0g{2$+D#AmgfLK`Aj`$1YJ94a)@4&^_=DU`eXEucxEo%qyM3J5KPkhuETy4 zs0#IWU=m)8z6ZqO`SvOy58I}AZRLT-3I;?IuSSgc(B$GH65|o7Y*v#xt7#+_&o1m? z*^7Dg^Tb|(OZvl_O@Q1>n=d7{DG_kY4Nr3Vf?W&n7o8QHC=UP;)s`B%BH_*#*yvc7?QBk<6h=*OZMxg?UB6Rtb z>91g<9U19~*|KW#Dfl^40j}w&ww$@%0XFtB{~eA-!J9vmUj9fq@t;MnxD>lZ?y7q} z^C!J~T4}yjVVtTE^S96QST&{IZ{%JFER}tf8|_vY@p~v&>LQ&g*Lq`_^ngUwP=Unb zz(_4aII0{mV*j*nMR>uV)HT)2G8gOdxDLBq)tR4o0P{vESl3A;MLRB0l1$zD{MHHH z{jNI(py_0Hu?lnMjh@%7zH&b_OiD^NO+ZLx#HlaE#d4NM+`D=>tezfEdM$q58t(!# z9=Mvl-@yu?yT4TW2ezQsY{Mo#nayBZ`sX8b)tCFY)tPPe0Q47neYs1PX=L}|TOTEDQsf3B3SFt}F#KXzAZ|9Ujr13+js{MZBzZf?UxF+l=2h)-((bkd zxJ)qdn)|%#6G2ZcV(=c1?uU2?_JG7{e`ckY*Ge`GR|+^R6Vw+8zFf-tnY3I8?ieoR z(HA_rNB6AD22d=rZ{NNaQ#{MHg#J7!7Kir<1SI_PWyP8ATI#UgDe;x0fR$ei+HBeMbdgPqWr zJscd@D2*&z&rZ9VBU!!Y!LA^m?_sWWDB=}#{IJI+k(SPQFN>Q&;oBD@hpsGZy&!5TxRfAqQ=mjx#~5bcJP(HG)*;zMnJT2RebpJhz+B2ef+5SH$)X5dyP>u9 zO3)&GgqxdtM8^c~w6-!NIH)~dyqH*v!dEFssB&6NAdP8*qtw3?Q2?Oa8puu*0d~3e zJXbW#x}g&UKZ5w>nO`pdFp;xFL|p_t29+i!({BHdfH+DELlPTBT8tuL9N}Jri5j<& z>OF@Q9tgTz5%XFko3e)$NfBrsO%6SvZ!6?&z!SAu$Li3)3alFsR_&sv zIe6U$#4d2emcgh}#{orvruh~3w{*O-(~<|1GP2LN`o-Nr)^BnBdOzsATE(2wn1CKX z=!^+Y$1XA$16Qqb>~o5qG9@0QkaG^996WSrb}WT>!{0t8SW(Hku^s?3N>|w0POJjJ ztO0$poIa_0;oHMU?&#g^DLYsUiG7(aKAd%HmQOYdk6sv8{BPK@u zNki|yxAo7_fZvIn*$;=!d+oZt-1u$ri}_jQh{|C-Kc9uReYn{+GJgBoj*%8)_?1;- z`vCAmN=dOEoDND=7u%En4)^l@%|(1vL#S{>7wzDe`*%^}{}&B3Is6u_oOu1W-hxv^ zN$0l6!?%Ilcn3sH(Nu$maPi`BltW(M{gm_qNr(F1cnRaRjjfdJdP=+?m;i4a4OK-z z6|P?lt<#Khr%ISp9oaTbpD&vho0a_l-0xtT?f>{&lqZ%*)=m;|DEfY&@(Bj8n|rlr zm}TSPmTOM!Hc58fMTF%D|WlVX96gLMGMlBsgA#$oAwutb@*Rad9b^iE01>bM3pZz*VOM@yyKq-%#378ba=a z@A*u%9VrQ6!%ynem=mYCRqs{>2y!2rF7@Y?%EY5s23Wc9LBOE30?)nX+a3sEM*{>^ zgxq?4{*xS!suIQ@191O|k`x=G7zuLszkfspo*MWr>2y=rvP4u&n>Rc;w zn=y9nnu9QoA}`Qdl=Ju((Vc>Vf3op;-b#Lb|jXDWat*RKPF_0Oo58gXh|j@WC&;M~f|`{6MJ6ACf=__i<+s6UAAnl_(9J z1q`W3)DgRy5>zMEW$T1Jp+Itj^PAkY6HuR`Z6#hzs9|WFJxe=_63~v2LDWGC#&rGr zx67Q#GQ4*4LSNNPi@O8vxl(g_W#`K`+s~(JRxm0?g%5GPp7HPFHp>t)Kh+VGXNynW z{m{Sc@o9?*_8Wem3hm{0hp&o@rKtzmB;Mb0NGq~~pZ8qZw9%WoK=YU%X*x1{8rHK% z?^7n}p2vO&x^UsbF0Kb)B?uy0akJg>#tq4dJFDKU0TPr>%)f+c`lPzjf|p1h%H8!FDHo`h8{k2NQBZ|>=F4iHrDjw#b!v~ zFI{r(E%kv*1*CsYP7a2KEp~QxhQtOcPRHVG3`|W)U=Lo*`K|)zl#Gn?=51|lKF3SP z=f>VQx3tJ!%ew;}6+l z4n?rcvHk~dz!ghKNC=ZPouX2Kwa|nAG&Q%Oi}yZ|$BK5^=hlxj;VUOyn4W5oG9d_C z04;eS$U95$qzLj>6zw`eybtXB`qsAktD?=c&@2W)jO4!x(i*fC{i`(e|MgI@qr13J zxxDT8{~}%^gOP``gbI=?1dFn=GANsEK+0vVxNB_h!lggb|~RMsdcve*6rIG8XAZFfSQs$cq*OV%A_ap}4ph%8ql`VjRg7v8`WE{0ZvO zSS%KGPuuzUq*9BDJb_~H^768>vT}EK&&bHYCPFZuot-_G{N{~>jEun*clX7b5Gm%; zQcX<_jYhj}VX_Xf0(mv_Z{ zXlO|4a{9#hICxnynf&O<6JEqap?p?AP!Q}ZBpX0ofB$uzbLY;jU%e7ITLFIlqu=-6 z>!G$ZLkzxs`?jcEXI=)(#ItSNwv|4m1JrL_4cyMa z6a(|B2?cXPOmIYm5=3zo*QsbVuXgIfChy{%=1a!K*h@i8lxsP+LSnY_^5y`Tv8W0# z&UZXh54{$bFuF-jK;dC8Ky(4@5-6zRGE~F6;>T~`wg+|EFF^Hkc6%5(;q5wZp;32b zWo3~AhMA|$q5i3x)YNxp{c+2&(b1>bmet$*035;DKB#!f=kn_lzg69*O`4x~!ED&D zK_*N~&+wgXMEum$R7S=@i^qwdy1Qj0_UtJea->Lru@RcPy1=d6c)ZnHxEPFddCHX( zbz`E07stw2Z$ ztzYfv=s2tW->>HV1P&Si_c_P>YmwU5u0@waL&>3>XV11`K*xlKj~?l_e)#YsaynBM z=<2vYwF0n0w3j3~SyELG469s76?^f*1sbraC39bljE#*=fu9HI0NO$#@Tt9h3CP>^ z$3;dbCN8$dX#{F&YIb`qbV2_~N=nLj3b91lZMqlokNaB$0KxRQ4Q$b|v$Q-NL>_9i z?C z4`^Nxf&P#YxSSXa1{(vX8XnG{)YSIr)6=l9q_@Gr!H}{P^TEWK(C!32=_<1K#O2GE zwY48NQEYGC{6Hq_U(wX80XNXSNnqzrI8ZW_3)0>|>xZU%mQNz%L>MNlMa=9I8Z}PkBF`-sA@}X7|A17Tl9X}0z zVm47H3VN4B(ynA&hjdCUR*xeJp++N6$cr&;Ze(P1^(q*99>fL^1;X~*w6p^d_4Mx) zb_Ge38!e^D7s5LxcYJlMTH4yd%^e*n)w6SR7&K?>%y&AR^V3hKF%U!|wM|W9mYkO!Nu{qK&M!C_92}; z5cDrNI1t5}T3Wibq65Y*fLsJ0mXV<0qc^Iks#!MQL#ey|}<13-f*ZZYBDS@m-U z1_lt0a5y%|I~Qyww_ie884I3z-%|qc!Pv;? z_TvWbIX06@ZOs^HexnbO75r?I#Z+fLy;?l>TuohFfWQAX?{m}t=$ppo-^`!9Cjl-2 zIN85{|4n=QZb;*;tP+|i!Oe}07feiKsz>MvNlCZMA$Q^uR2BgYAf~8znMYDm5|Bh> zR1`!-LqkK#U1ian?CjvMFfH1mC%`U%YiuZ5XhLG#oncXRD5t!<99jvbY$4}BGi1wX^=$caI&kgB{LQ7 zOT`0jYfH=h6H(AGIbFfMZrwU#nHH!kCU1bbRql>HjR$k+?7XJ)k2baG>()Qi*UL&u z-nx17b#k)uo!;YT&sIVcS9^QAhTwxGVCifx#ER>2u?fl6pDbS1un?{?Xpl6U=TRS;5g#+>pkS}!(TUc1= z(3!VKLQE_X#Dj~=BTWX0g6>kgRaM2*)k&~#C^jZ1kCpnY+PQNlWP)JiguyxN_}JKL zBxtKi$kNi1-zL7Pj$A5`3J@YUlI!ZYR(tp%C%&NhEb>Uc0TTQT)cX~CaLh??nt*_6HZq}47wzMN1W^&10VZ>MVic?c zVlvgW2?r~I6H3X>o}~?uf6k!5jv=J2^xkmRE$6=`%%y}2<*R$~XEejv3ii1jBlHc3 nn8p8ryctb$|B2ap0%zThow?+DUF09pnK5T{Pp6+W`{6$TLCyb| diff --git a/test/discretization.jl b/test/discretization.jl index 0e31e435c..a2de49f6f 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -187,7 +187,7 @@ (1.1794424965e7, 1.7289626797e7) ] ) - rng = MersenneTwister(123) + rng = StableRNG(123) mesh = discretize(poly, FIST(rng)) @test nvertices(mesh) == 16 @test nelements(mesh) == 14 @@ -202,14 +202,14 @@ (-0.20127837, 0.24671146) ] ) - rng = MersenneTwister(123) + rng = StableRNG(123) mesh = discretize(poly, FIST(rng)) @test nvertices(mesh) == 5 @test nelements(mesh) == 3 end @testset "Miscellaneous" begin - rng = MersenneTwister(123) + rng = StableRNG(123) for method in [FIST(rng), Dehn1899()] triangle = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) mesh = discretize(triangle, method) @@ -260,7 +260,7 @@ end @testset "Difficult examples" begin - rng = MersenneTwister(123) + rng = StableRNG(123) for method in [FIST(rng), Dehn1899()] poly = readpoly(T, joinpath(datadir, "taubin.line")) mesh = discretize(poly, method) @@ -321,11 +321,7 @@ poly = readpoly(T, joinpath(datadir, "hole1.line")) mesh = discretize(poly, method) @test Set(vertices(poly)) == Set(vertices(mesh)) - if method isa FIST && T == Float32 - @test nelements(mesh) == 33 - else - @test nelements(mesh) == 32 - end + @test nelements(mesh) == 32 poly = readpoly(T, joinpath(datadir, "hole2.line")) mesh = discretize(poly, method) diff --git a/test/partitioning.jl b/test/partitioning.jl index a15ea3669..f7841a6da 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -1,142 +1,124 @@ @testset "Partitioning" begin setify(lists) = Set(Set.(lists)) - Random.seed!(123) - d = CartesianGrid{T}(10, 10) - p = partition(d, UniformPartition(100)) - @test parent(p) == d - @test sprint(show, p) == "100 Partition" - @test sprint(show, MIME"text/plain"(), p) == """ - 100 Partition - ├─ 1 view(::CartesianGrid{2,$T}, [32]) - ├─ 1 view(::CartesianGrid{2,$T}, [97]) - ├─ 1 view(::CartesianGrid{2,$T}, [3]) - ├─ 1 view(::CartesianGrid{2,$T}, [20]) - ├─ 1 view(::CartesianGrid{2,$T}, [73]) - ⋮ - ├─ 1 view(::CartesianGrid{2,$T}, [89]) - ├─ 1 view(::CartesianGrid{2,$T}, [14]) - ├─ 1 view(::CartesianGrid{2,$T}, [82]) - ├─ 1 view(::CartesianGrid{2,$T}, [78]) - └─ 1 view(::CartesianGrid{2,$T}, [42])""" + g = CartesianGrid{T}(10, 10) + p = partition(g, UniformPartition(100)) + @test parent(p) == g + @test length(p) == 100 @testset "UniformPartition" begin - Random.seed!(123) - - grid = CartesianGrid{T}(3, 3) - p = partition(grid, UniformPartition(3, false)) + rng = StableRNG(123) + g = CartesianGrid{T}(3, 3) + p = partition(rng, g, UniformPartition(3, false)) @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - p = partition(grid, UniformPartition(3)) - if VERSION < v"1.7" - @test setify(indices(p)) == setify([[8, 6, 9], [4, 1, 7], [2, 3, 5]]) - else - @test setify(indices(p)) == setify([[4, 6, 1], [9, 8, 3], [5, 7, 2]]) - end + rng = StableRNG(123) + p = partition(rng, g, UniformPartition(3)) + @test setify(indices(p)) == setify([[5, 4, 2], [6, 7, 8], [9, 3, 1]]) - grid = CartesianGrid{T}(2, 3) - p = partition(grid, UniformPartition(3, false)) + g = CartesianGrid{T}(2, 3) + p = partition(g, UniformPartition(3, false)) @test setify(indices(p)) == setify([[1, 2], [3, 4], [5, 6]]) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, UniformPartition(3)) - rng = MersenneTwister(123) - p2 = partition(rng, grid, UniformPartition(3)) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, UniformPartition(3)) + rng = StableRNG(123) + p2 = partition(rng, g, UniformPartition(3)) @test p1 == p2 end @testset "DirectionPartition" begin - grid = CartesianGrid{T}(3, 3) + g = CartesianGrid{T}(3, 3) - # basic checks on small regular grid data - p = partition(grid, DirectionPartition(T.((1, 0)))) + # basic checks on small grids + p = partition(g, DirectionPartition(T.((1, 0)))) @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - p = partition(grid, DirectionPartition(T.((0, 1)))) + p = partition(g, DirectionPartition(T.((0, 1)))) @test setify(indices(p)) == setify([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) - p = partition(grid, DirectionPartition(T.((1, 1)))) + p = partition(g, DirectionPartition(T.((1, 1)))) @test setify(indices(p)) == setify([[1, 5, 9], [2, 6], [3], [4, 8], [7]]) - p = partition(grid, DirectionPartition(T.((1, -1)))) + p = partition(g, DirectionPartition(T.((1, -1)))) @test setify(indices(p)) == setify([[1], [2, 4], [3, 5, 7], [6, 8], [9]]) # opposite directions produce same partition dir1 = (rand(T), rand(T)) dir2 = .-dir1 - p1 = partition(grid, DirectionPartition(dir1)) - p2 = partition(grid, DirectionPartition(dir2)) + p1 = partition(g, DirectionPartition(dir1)) + p2 = partition(g, DirectionPartition(dir2)) @test setify(indices(p1)) == setify(indices(p2)) - # partition of arbitrarily large regular grid always - # returns the "lines" and "columns" of the grid + # partition of arbitrarily large grid always + # returns the "lines" and "columns" for n in [10, 100, 200] - grid = CartesianGrid{T}(n, n) + g = CartesianGrid{T}(n, n) - p = partition(grid, DirectionPartition(T.((1, 0)))) + p = partition(g, DirectionPartition(T.((1, 0)))) @test setify(indices(p)) == setify([collect(((i - 1) * n + 1):(i * n)) for i in 1:n]) ns = [nelements(d) for d in p] @test all(ns .== n) - p = partition(grid, DirectionPartition(T.((0, 1)))) + p = partition(g, DirectionPartition(T.((0, 1)))) @test setify(indices(p)) == setify([collect(i:n:(n * n)) for i in 1:n]) ns = [nelements(d) for d in p] @test all(ns .== n) end # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, DirectionPartition(T.((1, 0)))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, DirectionPartition(T.((1, 0)))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, DirectionPartition(T.((1, 0)))) + rng = StableRNG(123) + p2 = partition(rng, g, DirectionPartition(T.((1, 0)))) @test p1 == p2 end @testset "FractionPartition" begin - grid = CartesianGrid{T}(10, 10) + g = CartesianGrid{T}(10, 10) - p = partition(grid, FractionPartition(T(0.5))) + p = partition(g, FractionPartition(T(0.5))) @test nelements(p[1]) == nelements(p[2]) == 50 @test length(p) == 2 - p = partition(grid, FractionPartition(T(0.7))) + p = partition(g, FractionPartition(T(0.7))) @test nelements(p[1]) == 70 @test nelements(p[2]) == 30 - p = partition(grid, FractionPartition(T(0.3))) + p = partition(g, FractionPartition(T(0.3))) @test nelements(p[1]) == 30 @test nelements(p[2]) == 70 # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, FractionPartition(T(0.5))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, FractionPartition(T(0.5))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, FractionPartition(T(0.5))) + rng = StableRNG(123) + p2 = partition(rng, g, FractionPartition(T(0.5))) @test p1 == p2 end @testset "BlockPartition" begin - grid = CartesianGrid{T}(10, 10) + g = CartesianGrid{T}(10, 10) - p = partition(grid, BlockPartition(T(5), T(5))) + p = partition(g, BlockPartition(T(5), T(5))) @test length(p) == 4 @test all(nelements.(p) .== 25) - p = partition(grid, BlockPartition(T(5), T(2))) + p = partition(g, BlockPartition(T(5), T(2))) @test length(p) == 12 @test Set(nelements.(p)) == Set([5, 10]) - grid = CartesianGrid{T}(50, 50, 50) + g = CartesianGrid{T}(50, 50, 50) - p = partition(grid, BlockPartition(T(1.0), T(1.0), T(1.0), neighbors=false)) + p = partition(g, BlockPartition(T(1.0), T(1.0), T(1.0), neighbors=false)) @test length(p) == 125000 @test Set(nelements.(p)) == Set(1) @test metadata(p) == Dict{Any,Any}() - p = partition(grid, BlockPartition(T(5.0), T(5.0), T(5.0), neighbors=true)) + p = partition(g, BlockPartition(T(5.0), T(5.0), T(5.0), neighbors=true)) @test length(p) == 1000 @test Set(nelements.(p)) == Set(125) n = metadata(p)[:neighbors] @@ -144,11 +126,11 @@ @test all(0 .< length.(n) .< 27) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, BlockPartition(T(5), T(2))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, BlockPartition(T(5), T(2))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, BlockPartition(T(5), T(2))) + rng = StableRNG(123) + p2 = partition(rng, g, BlockPartition(T(5), T(2))) @test p1 == p2 m1 = BlockPartition((T(5), T(2))) @@ -162,9 +144,9 @@ end @testset "BisectPointPartition" begin - grid = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(grid, BisectPointPartition(T.((0.0, 1.0)), T.((5.0, 5.1)))) + p = partition(g, BisectPointPartition(T.((0.0, 1.0)), T.((5.0, 5.1)))) p1, p2 = p[1], p[2] @test nelements(p1) == 60 @test nelements(p2) == 40 @@ -180,24 +162,24 @@ @test all(X2[2, j] > M1[2] for j in 1:size(X2, 2)) # flipping normal direction is equivalent to swapping subsets - p₁ = partition(grid, BisectPointPartition(T.((1.0, 0.0)), T.((5.1, 5.0)))) - p₂ = partition(grid, BisectPointPartition(T.((-1.0, 0.0)), T.((5.1, 5.0)))) + p₁ = partition(g, BisectPointPartition(T.((1.0, 0.0)), T.((5.1, 5.0)))) + p₂ = partition(g, BisectPointPartition(T.((-1.0, 0.0)), T.((5.1, 5.0)))) @test nelements(p₁[1]) == nelements(p₂[2]) == 60 @test nelements(p₁[2]) == nelements(p₂[1]) == 40 # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, BisectPointPartition(T.((1, 0)), T.((5, 5)))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, BisectPointPartition(T.((1, 0)), T.((5, 5)))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) + rng = StableRNG(123) + p2 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) @test p1 == p2 end @testset "BisectFractionPartition" begin - grid = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(grid, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) p1, p2 = p[1], p[2] @test nelements(p1) == 20 @test nelements(p2) == 80 @@ -213,17 +195,17 @@ @test all(X2[1, j] > M1[1] for j in 1:size(X2, 2)) # flipping normal direction is equivalent to swapping subsets - p₁ = partition(grid, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - p₂ = partition(grid, BisectFractionPartition(T.((-1.0, 0.0)), T(0.8))) + p₁ = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + p₂ = partition(g, BisectFractionPartition(T.((-1.0, 0.0)), T(0.8))) @test nelements(p₁[1]) == nelements(p₂[2]) == 20 @test nelements(p₁[2]) == nelements(p₂[1]) == 80 # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, BisectFractionPartition(T.((1, 0)), T(0.5))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, BisectFractionPartition(T.((1, 0)), T(0.5))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) + rng = StableRNG(123) + p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) @test p1 == p2 end @@ -248,47 +230,47 @@ @test setify(indices(p)) == setify([[1], [2], [3], [4], [5]]) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, BallPartition(T(2))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, BallPartition(T(2))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, BallPartition(T(2))) + rng = StableRNG(123) + p2 = partition(rng, g, BallPartition(T(2))) @test p1 == p2 end @testset "PlanePartition" begin - grid = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(grid, PlanePartition(T.((0, 1)))) + g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) + p = partition(g, PlanePartition(T.((0, 1)))) @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - grid = CartesianGrid((4, 4), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(grid, PlanePartition(T.((0, 1)))) + g = CartesianGrid((4, 4), T.((-0.5, -0.5)), T.((1.0, 1.0))) + p = partition(g, PlanePartition(T.((0, 1)))) @test setify(indices(p)) == setify([1:4, 5:8, 9:12, 13:16]) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, PlanePartition(T.((1, 0)))) - rng = MersenneTwister(123) - p2 = partition(rng, grid, PlanePartition(T.((1, 0)))) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, PlanePartition(T.((1, 0)))) + rng = StableRNG(123) + p2 = partition(rng, g, PlanePartition(T.((1, 0)))) @test p1 == p2 end @testset "PredicatePartition" begin - grid = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) + g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) # partition even from odd locations pred(i, j) = iseven(i + j) partitioner = PredicatePartition(pred) - p = partition(grid, partitioner) + p = partition(g, partitioner) @test setify(indices(p)) == setify([1:2:9, 2:2:8]) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, partitioner) - rng = MersenneTwister(123) - p2 = partition(rng, grid, partitioner) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, partitioner) + rng = StableRNG(123) + p2 = partition(rng, g, partitioner) @test p1 == p2 end @@ -318,11 +300,11 @@ @test count(i -> i == 36, n) == 1 # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, sp) - rng = MersenneTwister(123) - p2 = partition(rng, grid, sp) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, sp) + rng = StableRNG(123) + p2 = partition(rng, g, sp) @test p1 == p2 end @@ -346,11 +328,11 @@ end # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, bmn) - rng = MersenneTwister(123) - p2 = partition(rng, grid, bmn) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, bmn) + rng = StableRNG(123) + p2 = partition(rng, g, bmn) @test p1 == p2 end @@ -366,11 +348,11 @@ @test setify(s1) == setify(s2) # reproducible results with rng - rng = MersenneTwister(123) - grid = CartesianGrid{T}(10, 10) - p1 = partition(rng, grid, bmn) - rng = MersenneTwister(123) - p2 = partition(rng, grid, bmn) + rng = StableRNG(123) + g = CartesianGrid{T}(10, 10) + p1 = partition(rng, g, bmn) + rng = StableRNG(123) + p2 = partition(rng, g, bmn) @test p1 == p2 end diff --git a/test/runtests.jl b/test/runtests.jl index fb092b591..28bd3c600 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,7 +10,7 @@ using SparseArrays using PlyIO using Unitful using Rotations -using Test, Random +using Test, StableRNGs using ReferenceTests, ImageIO using TransformsBase: Identity, → diff --git a/test/sampling.jl b/test/sampling.jl index 8b72c2369..0337d181b 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -1,14 +1,14 @@ @testset "Sampling" begin @testset "UniformSampling" begin - Random.seed!(2021) + rng = StableRNG(123) d = CartesianGrid{T}(100, 100) - s = sample(d, UniformSampling(100)) + s = sample(rng, d, UniformSampling(100)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) # availability of option ordered - s = sample(d, UniformSampling(100, ordered=true)) + s = sample(rng, d, UniformSampling(100, ordered=true)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) @@ -16,25 +16,25 @@ @testset "WeightedSampling" begin # uniform weights => uniform sampler - Random.seed!(2020) + rng = StableRNG(123) d = CartesianGrid{T}(100, 100) - s = sample(d, WeightedSampling(100)) + s = sample(rng, d, WeightedSampling(100)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) # availability of option ordered - s = sample(d, WeightedSampling(100, ordered=true)) + s = sample(rng, d, WeightedSampling(100, ordered=true)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) # utility method - s = sample(d, 100, ordered=true) + s = sample(rng, d, 100, ordered=true) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) - s = sample(d, 100, fill(1, 10000), ordered=true) + s = sample(rng, d, 100, fill(1, 10000), ordered=true) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, T[50.0, 50.0], atol=T(10)) @@ -360,9 +360,9 @@ @testset "RNGs" begin dom = CartesianGrid{T}(100, 100) for method in [UniformSampling(100), WeightedSampling(100), BallSampling(T(10))] - rng = MersenneTwister(2021) + rng = StableRNG(2021) s1 = sample(rng, dom, method) - rng = MersenneTwister(2021) + rng = StableRNG(2021) s2 = sample(rng, dom, method) @test collect(s1) == collect(s2) end @@ -371,9 +371,9 @@ # because of https://github.com/JuliaStats/StatsBase.jl/issues/695 if T == Float64 for method in [HomogeneousSampling(100), MinDistanceSampling(T(5))] - rng = MersenneTwister(2021) + rng = StableRNG(2021) s1 = sample(rng, dom, method) - rng = MersenneTwister(2021) + rng = StableRNG(2021) s2 = sample(rng, dom, method) @test collect(s1) == collect(s2) end @@ -397,9 +397,9 @@ P3(0, 1, 1) ) ] - rng = MersenneTwister(2021) + rng = StableRNG(2021) s1 = sample(rng, geom, method) - rng = MersenneTwister(2021) + rng = StableRNG(2021) s2 = sample(rng, geom, method) @test collect(s1) == collect(s2) end diff --git a/test/traversing.jl b/test/traversing.jl index 121910c34..50612ea14 100644 --- a/test/traversing.jl +++ b/test/traversing.jl @@ -15,9 +15,9 @@ p = traverse(grid, RandomPath()) @test all(1 .≤ collect(p) .≤ 100 * 100) - path = RandomPath(MersenneTwister(123)) + path = RandomPath(StableRNG(123)) grid = CartesianGrid{T}(3, 3) - @test traverse(grid, path) == [8, 7, 5, 3, 4, 1, 6, 9, 2] + @test traverse(grid, path) == [4, 7, 2, 1, 3, 8, 5, 6, 9] end @testset "SourcePath" begin @@ -76,7 +76,7 @@ if visualtests paths = [ LinearPath(), - RandomPath(MersenneTwister(123)), + RandomPath(StableRNG(123)), ShiftedPath(LinearPath(), 10), SourcePath(1:3), MultiGridPath() From a363121a7e0a363f9f09f1b7bbc36f7c14e61d4f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:51:52 -0300 Subject: [PATCH 040/423] :robot: Format .jl files (#835) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- test/traversing.jl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/traversing.jl b/test/traversing.jl index 50612ea14..782e9f094 100644 --- a/test/traversing.jl +++ b/test/traversing.jl @@ -74,13 +74,8 @@ @testset "Miscellaneous" begin if visualtests - paths = [ - LinearPath(), - RandomPath(StableRNG(123)), - ShiftedPath(LinearPath(), 10), - SourcePath(1:3), - MultiGridPath() - ] + paths = + [LinearPath(), RandomPath(StableRNG(123)), ShiftedPath(LinearPath(), 10), SourcePath(1:3), MultiGridPath()] fnames = ["linear-path", "random-path", "shifted-path", "source-path", "multi-grid-path"] From b8069871f788b4a62d2cdf0ac5d82d8aae3ff69e Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Sun, 19 May 2024 11:43:51 -0300 Subject: [PATCH 041/423] Major refactor for CRS (#832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Experiment: Use 'CRS' in 'Point' * IO Methods * Remove '{Dim,T}' from 'Geometry' and 'Primitive' * Remove '{Dim,T}' from 'Polytope's * Remove '{Dim,T}' from 'Primitive's * Remove '{Dim,T}' from 'Multi' * Remove '{Dim,T}' from 'Domain' * Remove '{Dim,T}' from 'GeometrySet' * Remove '{Dim,T}' from 'CylindricalTrajectory' * Remove '{Dim,T}' from 'SubDomain' * Remove '{Dim,T}' from 'Mesh's * Add 'Dim' back to 'Geometry' and 'Primitive's * Add 'Dim' back to 'Polytope's * Add 'Dim' back to 'Multi' * Add 'Dim' back to 'GeometrySet' * Add 'Dim' back to 'SubDomain' * Add 'Dim' back to 'CylindricalTrajectory' * Add 'Dim' back to 'Mesh's * Fix code * Fix code * Adjust 'Point' code * Remove 'Dim' from shows * Add support for units in non-point fields of the geometries * Fix 'Multi' aliases * Adjust 'CartesianGrid' constructors * Update 'CartesianGrid' show * Refactor 'Vec' * Add docstring to 'Point' * Update 'utils.jl' * Update methods from files: 'tolerances.jl', 'partitioning.jl', 'predicates.jl', 'sorting.jl' and 'winding.jl' * Update methods from files: 'sideof.jl' and 'orientation.jl' * Update methods from files: 'clamping.jl' and 'utils.jl' * Update methods from files: 'intersections.jl' and 'utils.jl' * Update methods from files: 'utils.jl' * Update methods from files: 'transforms.jl' * Apply suggestions * Update methods from files: 'distances.jl' * Rename type parameters * 'coordinates' function now returns a 'Vec' * Implement 'coordtype' for geometries * Apply suggestions * Implement 'coordtype' for domains * Uncomment some includes & Update comments * Use 'coordtype' instead of type parameter * Apply suggestions * Apply suggestions * Update rand methods * Update rand methods * Fix code * Fix typo * Apply suggestions * Add 'rand' methods for 'Polytope' subtypes * Fix the definitions of the 'normal' function * Create 'units.jl' file * Fix intersection with 'Ray' and 'Triangle' * Update PartitionMethods * Update Vec testset * Update tests * Update test helpers * Update Primitives testset * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Add: isapproxequal, isapproxzero and isapproxone * Update 'Primitive' constructors * Apply suggestions * Apply suggestions * Apply suggestions * Update 'Polytopes' testset * Rename 'vec' to 'vector' * Update tests: Multi, Connectivities * Update tests: Domain, SubDomains, Sets * Update Mesh testset * Update tests: Trajectories, Utilities, Partitioning * Update tests: Sorting, Paths, Neighbor search * Update tests: Predicates, winding, sideof, orientation, Merging, Clipping, Clamping * Update tests: Simplification, Bounding boxes, Hulls, Sampling * Update tests: Pointification, Discretization * Update tests: Refinement, Coarsening, Transforms, Distances, Support function, Matrices, tolerances * Fix excessive allocations of the method: 'boundingbox(ray)' * Update comments * Update 'rand' methods for: 'Ring', 'Rope' and 'BezierCurve' * Fix 'intersection' methods & Fix 'gjk!' function * Add parametric constructors for Geometries and Domains * Fix Geometric Transforms * Update 'MeshesMakieExt' * Fix 'makietransform!' implementation * Update 'Bounding boxes' testset * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Update src/primitives/box.jl Co-authored-by: Júlio Hoffimann * Apply suggestions * Apply suggestions * Update exports * Apply suggestions * Apply suggestions * Update 'Primitive' show method * Apply suggestions * Update show tests * Apply suggestions * Add LinearAlgebra function wrappers * Add Rotations.jl function wrappers * Apply suggestions * Apply suggestions * Update documentation * Fix 'BezierCurve' constructor * Remove 'Meshes.ascolors' from documentation * Update 'Clamping' documentation * Uncomment some TODOs * Update tolerances * Code style changes * Format code * Update docs/src/algorithms/clamping.md --------- Co-authored-by: Júlio Hoffimann --- Project.toml | 2 + docs/src/algorithms/boundingbox.md | 2 +- docs/src/algorithms/clamping.md | 2 +- docs/src/algorithms/hulls.md | 2 +- docs/src/algorithms/refinement.md | 2 +- docs/src/domains/meshes.md | 4 +- docs/src/domains/sets.md | 4 +- docs/src/geometries/primitives.md | 2 +- docs/src/index.md | 58 +- docs/src/predicates.md | 6 +- docs/src/transforms.md | 4 +- docs/src/vectors.md | 4 - docs/src/visualization.md | 9 +- ext/MeshesMakieExt.jl | 2 + ext/geometryset.jl | 6 +- ext/grid.jl | 6 +- ext/grid/cartesian.jl | 11 +- ext/grid/rectilinear.jl | 2 +- ext/grid/structured.jl | 6 +- ext/grid/transformed.jl | 4 +- ext/simplemesh.jl | 15 +- ext/subcartesiangrid.jl | 6 +- ext/vector.jl | 3 +- src/Meshes.jl | 21 +- src/boundingboxes.jl | 22 +- src/clipping/sutherlandhodgman.jl | 4 +- src/complement.jl | 4 +- src/discretization.jl | 4 +- src/discretization/dehn.jl | 2 +- src/discretization/fist.jl | 5 +- src/distances.jl | 4 +- src/domains.jl | 23 +- src/geometries.jl | 17 +- src/hulls/graham.jl | 5 +- src/hulls/jarvis.jl | 4 +- src/intersections.jl | 2 +- src/intersections/boxes.jl | 4 +- src/intersections/domains.jl | 4 +- src/intersections/planes.jl | 39 +- src/intersections/rays.jl | 57 +- src/intersections/segments.jl | 72 +- src/ioutils.jl | 7 +- src/matrices.jl | 8 +- src/measures.jl | 48 +- src/mesh.jl | 21 +- src/mesh/cartesiangrid.jl | 80 +- src/mesh/rectilineargrid.jl | 17 +- src/mesh/simplemesh.jl | 8 +- src/mesh/structuredgrid.jl | 17 +- src/mesh/transformedmesh.jl | 10 +- src/multigeoms.jl | 22 +- src/neighborhoods/metricball.jl | 4 +- src/neighborsearch.jl | 4 +- src/neighborsearch/ball.jl | 4 +- src/neighborsearch/kball.jl | 7 +- src/neighborsearch/knearest.jl | 9 +- src/orientation.jl | 15 +- src/partitioning/ball.jl | 9 +- src/partitioning/bisectfraction.jl | 20 +- src/partitioning/bisectpoint.jl | 18 +- src/partitioning/block.jl | 9 +- src/partitioning/direction.jl | 20 +- src/partitioning/plane.jl | 19 +- src/polytopes.jl | 27 +- src/polytopes/hexahedron.jl | 3 + src/polytopes/ngon.jl | 21 +- src/polytopes/polyarea.jl | 16 +- src/polytopes/pyramid.jl | 3 + src/polytopes/ring.jl | 20 +- src/polytopes/rope.jl | 8 +- src/polytopes/segment.jl | 3 + src/polytopes/tetrahedron.jl | 3 + src/predicates/in.jl | 44 +- src/predicates/intersects.jl | 58 +- src/predicates/iscollinear.jl | 4 +- src/predicates/isconvex.jl | 2 +- src/predicates/iscoplanar.jl | 5 +- src/primitives.jl | 7 +- src/primitives/ball.jl | 25 +- src/primitives/bezier.jl | 32 +- src/primitives/box.jl | 22 +- src/primitives/circle.jl | 29 +- src/primitives/cone.jl | 11 +- src/primitives/conesurface.jl | 15 +- src/primitives/cylinder.jl | 39 +- src/primitives/cylindersurface.jl | 49 +- src/primitives/disk.jl | 22 +- src/primitives/ellipsoid.jl | 29 +- src/primitives/frustum.jl | 23 +- src/primitives/frustumsurface.jl | 31 +- src/primitives/line.jl | 12 +- src/primitives/paraboloidsurface.jl | 39 +- src/primitives/plane.jl | 36 +- src/primitives/point.jl | 97 +- src/primitives/ray.jl | 12 +- src/primitives/sphere.jl | 27 +- src/primitives/torus.jl | 39 +- src/projecting.jl | 4 +- src/refinement/regular.jl | 12 +- src/sampling/homogeneous.jl | 22 +- src/sampling/mindistance.jl | 2 +- src/sampling/regular.jl | 11 +- src/sets.jl | 8 +- src/sideof.jl | 12 +- src/simplification/douglaspeucker.jl | 18 +- src/simplification/selinger.jl | 16 +- src/sorting/direction.jl | 2 +- src/subdomains.jl | 8 +- src/tolerances.jl | 8 +- src/trajecs.jl | 30 +- src/transforms.jl | 3 + src/transforms/affine.jl | 14 +- src/transforms/bridge.jl | 29 +- src/transforms/repair.jl | 10 +- src/transforms/rotate.jl | 4 +- src/transforms/stdcoords.jl | 2 +- src/transforms/translate.jl | 9 +- src/traversing/source.jl | 5 +- src/units.jl | 19 + src/utils.jl | 49 +- src/vectors.jl | 65 +- src/winding.jl | 15 +- test/boundingboxes.jl | 108 +- test/clamping.jl | 18 +- test/clipping.jl | 73 +- test/coarsening.jl | 10 +- test/complement.jl | 42 +- test/connectivities.jl | 6 +- test/discretization.jl | 176 ++-- test/distances.jl | 36 +- test/domains.jl | 38 +- test/dummy.jl | 16 +- test/hulls.jl | 198 ++-- test/intersections.jl | 848 ++++++++-------- test/matrices.jl | 10 +- test/merging.jl | 10 +- test/mesh.jl | 560 +++++------ test/multigeoms.jl | 68 +- test/neighborsearch.jl | 62 +- test/orientation.jl | 8 +- test/partitioning.jl | 44 +- test/pointification.jl | 28 +- test/polytopes.jl | 887 +++++++++-------- test/predicates.jl | 365 +++---- test/primitives.jl | 1365 +++++++++++++------------- test/refinement.jl | 18 +- test/runtests.jl | 33 +- test/sampling.jl | 373 +++---- test/sets.jl | 82 +- test/sideof.jl | 42 +- test/simplification.jl | 30 +- test/sorting.jl | 14 +- test/subdomains.jl | 132 +-- test/supportfun.jl | 32 +- test/tolerances.jl | 18 +- test/trajecs.jl | 14 +- test/transforms.jl | 415 ++++---- test/traversing.jl | 18 +- test/utils.jl | 32 +- test/vectors.jl | 121 +-- test/viewing.jl | 78 +- test/winding.jl | 14 +- 162 files changed, 4446 insertions(+), 4017 deletions(-) create mode 100644 src/units.jl diff --git a/Project.toml b/Project.toml index 70a600f2e..c6f7bcf8e 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.42.2" Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" CircularArrays = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" Colorfy = "03fe91ce-8ec6-4610-8e8d-e7491ccca690" +CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" @@ -29,6 +30,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" +CoordRefSystems = "0.7" Distances = "0.10" LinearAlgebra = "1.9" Makie = "0.20" diff --git a/docs/src/algorithms/boundingbox.md b/docs/src/algorithms/boundingbox.md index 21d6e4fa6..3f78b80c8 100644 --- a/docs/src/algorithms/boundingbox.md +++ b/docs/src/algorithms/boundingbox.md @@ -10,7 +10,7 @@ boundingbox ``` ```@example boundingbox -pset = PointSet(rand(Point2, 100)) +pset = PointSet([rand(Point{2}) for _ in 1:100]) bbox = boundingbox(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/algorithms/clamping.md b/docs/src/algorithms/clamping.md index c3c5c51d4..f87bc4668 100644 --- a/docs/src/algorithms/clamping.md +++ b/docs/src/algorithms/clamping.md @@ -3,7 +3,7 @@ Meshes adds methods to Julia's built-in `clamp` function. The additional methods clamp points to the edges of a box in any number of dimensions. The target points and boxes must have the same number of dimensions and the same numeric type. ```@docs -clamp(::Point, ::Box) +clamp(::Point{Dim}, ::Box{Dim}) where {Dim} clamp(::PointSet, ::Box) ``` diff --git a/docs/src/algorithms/hulls.md b/docs/src/algorithms/hulls.md index 5fb9351f7..6bd2feca5 100644 --- a/docs/src/algorithms/hulls.md +++ b/docs/src/algorithms/hulls.md @@ -14,7 +14,7 @@ JarvisMarch ``` ```@example hull -pset = PointSet(rand(Point2, 100)) +pset = PointSet([rand(Point{2}) for _ in 1:100]) chul = convexhull(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/algorithms/refinement.md b/docs/src/algorithms/refinement.md index 883873060..52d25344e 100644 --- a/docs/src/algorithms/refinement.md +++ b/docs/src/algorithms/refinement.md @@ -84,7 +84,7 @@ CatmullClark ```@example refinement # define a cube in R^3 -points = Point3[(0,0,0),(1,0,0),(1,1,0),(0,1,0),(0,0,1),(1,0,1),(1,1,1),(0,1,1)] +points = [(0,0,0),(1,0,0),(1,1,0),(0,1,0),(0,0,1),(1,0,1),(1,1,1),(0,1,1)] connec = connect.([(1,4,3,2),(5,6,7,8),(1,2,6,5),(3,4,8,7),(1,5,8,4),(2,3,7,6)]) mesh = SimpleMesh(points, connec) diff --git a/docs/src/domains/meshes.md b/docs/src/domains/meshes.md index 1511565ba..6dfa14cbe 100644 --- a/docs/src/domains/meshes.md +++ b/docs/src/domains/meshes.md @@ -58,7 +58,7 @@ SimpleMesh ```@example meshes # global vector of 2D points -points = Point2[(0,0),(1,0),(0,1),(1,1),(0.25,0.5),(0.75,0.5)] +points = [(0,0),(1,0),(0,1),(1,1),(0.25,0.5),(0.75,0.5)] # connect the points into N-gon connec = connect.([(1,2,6,5),(2,4,6),(4,3,5,6),(3,1,5)], Ngon) @@ -99,7 +99,7 @@ Adjacency ```@example meshes # global vector of 2D points -points = Point2[(0,0),(1,0),(0,1),(1,1),(0.25,0.5),(0.75,0.5)] +points = [(0,0),(1,0),(0,1),(1,1),(0.25,0.5),(0.75,0.5)] # connect the points into N-gon connec = connect.([(1,2,6,5),(2,4,6),(4,3,5,6),(3,1,5)], Ngon) diff --git a/docs/src/domains/sets.md b/docs/src/domains/sets.md index ded5521fe..6c5e6a55d 100644 --- a/docs/src/domains/sets.md +++ b/docs/src/domains/sets.md @@ -13,7 +13,7 @@ GeometrySet ``` ```@example sets -GeometrySet(rand(Ball{3,Float64}, 3)) |> viz +GeometrySet([rand(Ball{3}) for _ in 1:3]) |> viz ``` ```@docs @@ -21,5 +21,5 @@ PointSet ``` ```@example sets -PointSet(rand(Point2, 100)) |> viz +PointSet([rand(Point{2}) for _ in 1:100]) |> viz ``` \ No newline at end of file diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 3a33637e5..8c7c3521d 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -20,7 +20,7 @@ Point ``` ```@example primitives -rand(Point3, 100) |> viz +[rand(Point{3}) for _ in 1:100] |> viz ``` ```@docs diff --git a/docs/src/index.md b/docs/src/index.md index 6050fc772..65a4a31d9 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -78,58 +78,60 @@ import CairoMakie as Mke ### Points and vectors A [`Point`](@ref) is defined by its coordinates in a global reference system. The type of the -coordinates is determined automatically based on the specified literals, or is forced -to a specific type using helper constructors (e.g. `Point2`, `Point3`, `Point2f`, `Point3f`). +coordinates is determined automatically based on the specified literals. `Integer` coordinates are converted to `Float64` to fulfill the requirements of most geometric processing algorithms, which would be undefined in a discrete scale. -A vector [`Vec`](@ref) follows the same pattern. It can be constructed with the generic `Vec` -constructor or with the variants `Vec2` and `Vec3` for double precision and `Vec2f` -and `Vec3f` for single precision. +A vector [`Vec`](@ref) follows the same pattern. It can be constructed with the `Vec` constructor. ```@example overview -# 2D points -A = Point(0.0, 1.0) # double precision as expected -B = Point(0f0, 1f0) # single precision as expected -C = Point(0, 0) # Integer is converted to Float64 by design -D = Point2(0, 1) # explicitly ask for double precision -E = Point2f(0, 1) # explicitly ask for single precision +Point(0.0, 1.0) # double precision as expected +``` + +```@example overview +Point(0f0, 1f0) # single precision as expected +``` + +```@example overview +Point(0, 0) # Integer is converted to Float64 by design +``` + +```@example overview +Point(1.0, 2.0, 3.0) # double precision as expected +``` -# 3D points -F = Point(1.0, 2.0, 3.0) # double precision as expected -G = Point(1f0, 2f0, 3f0) # single precision as expected -H = Point(1, 2, 3) # Integer is converted to Float64 by design -I = Point3(1, 2, 3) # explicitly ask for double precision -J = Point3f(1, 2, 3) # explicitly ask for single precision +```@example overview +Point(1f0, 2f0, 3f0) # single precision as expected +``` -for P in (A,B,C,D,E,F,G,H,I,J) - println("Coordinate type: ", coordtype(P)) - println("Embedding dimension: ", embeddim(P)) -end +```@example overview +Point(1, 2, 3) # Integer is converted to Float64 by design ``` Points can be subtracted to produce a vector: ```@example overview +A = Point(1.0, 1.0) +B = Point(3.0, 3.0) B - A ``` They can't be added, but their coordinates can: ```@example overview -coordinates(F) + coordinates(H) +coordinates(A) + coordinates(B) ``` We can add a point to a vector though, and get a new point: ```@example overview -F + Vec(1, 1, 1) +A + Vec(1, 1) ``` And finally, we can create points at random with: ```@example overview -ps = rand(Point2, 10) +rand(Point{2}) ``` ### Primitives @@ -195,7 +197,11 @@ viz(t) Some of these geometries have additional functionality like the measure (or area): ```@example overview -measure(t) == area(t) == 1/2 +measure(t) +``` + +```@example overview +measure(t) == area(t) ``` Or the ability to know whether or not a point is inside: @@ -316,7 +322,7 @@ and a collection of [`Connectivity`](@ref) that store the indices to the global vector of points: ```@example overview -points = Point2[(0,0), (1,0), (0,1), (1,1), (0.25,0.5), (0.75,0.5)] +points = [(0,0), (1,0), (0,1), (1,1), (0.25,0.5), (0.75,0.5)] tris = connect.([(1,5,3), (4,6,2)], Triangle) quads = connect.([(1,2,6,5), (4,3,5,6)], Quadrangle) mesh = SimpleMesh(points, [tris; quads]) diff --git a/docs/src/predicates.md b/docs/src/predicates.md index 4a1a682a1..57551e1e8 100644 --- a/docs/src/predicates.md +++ b/docs/src/predicates.md @@ -81,9 +81,9 @@ supportfun ``` ```@example intersects -outer = Point2[(0,0),(1,0),(1,1),(0,1)] -hole1 = Point2[(0.2,0.2),(0.4,0.2),(0.4,0.4),(0.2,0.4)] -hole2 = Point2[(0.6,0.2),(0.8,0.2),(0.8,0.4),(0.6,0.4)] +outer = [(0,0),(1,0),(1,1),(0,1)] +hole1 = [(0.2,0.2),(0.4,0.2),(0.4,0.4),(0.2,0.4)] +hole2 = [(0.6,0.2),(0.8,0.2),(0.8,0.4),(0.6,0.4)] poly = PolyArea([outer, hole1, hole2]) ball1 = Ball((0.5,0.5), 0.05) ball2 = Ball((0.3,0.3), 0.05) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 1ed93a4bc..938147426 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -128,7 +128,7 @@ Repair ```@example transforms # mesh with unreferenced point -points = Point3[(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)] +points = [(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)] connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) mesh = SimpleMesh(points, connec) @@ -174,7 +174,7 @@ function readply(fname) x = ply["vertex"]["x"] y = ply["vertex"]["y"] z = ply["vertex"]["z"] - points = Point3.(x, y, z) + points = Point.(x, y, z) connec = [connect(Tuple(c.+1)) for c in ply["face"]["vertex_indices"]] SimpleMesh(points, connec) end diff --git a/docs/src/vectors.md b/docs/src/vectors.md index a7451875b..ea092f3c4 100644 --- a/docs/src/vectors.md +++ b/docs/src/vectors.md @@ -8,7 +8,3 @@ import CairoMakie as Mke # hide ```@docs Vec ``` - -```@example vectors -rand(Vec3, 100) -``` \ No newline at end of file diff --git a/docs/src/visualization.md b/docs/src/visualization.md index d69ad4d6e..14dcaaa36 100644 --- a/docs/src/visualization.md +++ b/docs/src/visualization.md @@ -14,19 +14,12 @@ viz viz! ``` -Vectors of custom objects can be visualized with the `color` -argument provided that they implement the `ascolors` trait: - -```@docs -Meshes.ascolors -``` - ## Geometries We can visualize a single geometry or multiple geometries in a vector: ```@example viz -triangles = rand(Triangle{2,Float64}, 10) +triangles = [rand(Triangle{2}) for _ in 1:10] viz(triangles, color = 1:10) ``` diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 55ef91f2c..c58fbdd85 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -5,7 +5,9 @@ module MeshesMakieExt using Meshes +using Unitful using Rotations +using StaticArrays using LinearAlgebra using Colorfy diff --git a/ext/geometryset.jl b/ext/geometryset.jl index d0482fd5b..23139c5e3 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -48,7 +48,7 @@ function Makie.plot!(plot::Viz{<:Tuple{PointSet}}) # get geometries and coordinates geoms = Makie.@lift parent($pset) - coords = Makie.@lift coordinates.($geoms) + coords = Makie.@lift map(g -> ustrip.(coordinates(g)), $geoms) # visualize point set Makie.scatter!(plot, coords, color=colorant, markersize=pointsize, overdraw=true) @@ -157,6 +157,6 @@ function asmakie(poly::Polygon) end end -asmakie(p::Point{Dim,T}) where {Dim,T} = Makie.Point{Dim,T}(Tuple(coordinates(p))) +asmakie(p::Point{Dim}) where {Dim} = Makie.Point{Dim}(ustrip.(Tuple(coordinates(p)))) -asmakie(v::Vec{Dim,T}) where {Dim,T} = Makie.Vec{Dim,T}(Tuple(v)) +asmakie(v::Vec{Dim}) where {Dim} = Makie.Vec{Dim}(ustrip.(Tuple(v))) diff --git a/ext/grid.jl b/ext/grid.jl index 7ca6fee13..2b1da0f6f 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -37,11 +37,11 @@ function Makie.data_limits(plot::Viz{<:Tuple{Grid}}) bbox = boundingbox(grid) pmin = aspoint3f(minimum(bbox)) pmax = aspoint3f(maximum(bbox)) - Makie.limits_from_transformed_points([pmin, pmax]) + Makie.Rect3f([pmin, pmax]) end -aspoint3f(p::Point{2}) = Makie.Point3f(coordinates(p)..., 0) -aspoint3f(p::Point{3}) = Makie.Point3f(coordinates(p)...) +aspoint3f(p::Point{2}) = Makie.Point3f(ustrip.(coordinates(p))..., 0) +aspoint3f(p::Point{3}) = Makie.Point3f(ustrip.(coordinates(p))...) # ---------------- # SPECIALIZATIONS diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index ba026159a..554bd467e 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -20,8 +20,8 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 # origin, spacing and size of grid - or = Makie.@lift coordinates(minimum($grid)) - sp = Makie.@lift spacing($grid) + or = Makie.@lift ustrip.(coordinates(minimum($grid))) + sp = Makie.@lift ustrip.(spacing($grid)) sz = Makie.@lift size($grid) if nc[] == 1 @@ -31,7 +31,8 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) viz!(plot, bbox, color=colorant) if showsegments[] - tup = Makie.@lift xysegments(Meshes.xyz($grid)...) + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) + tup = Makie.@lift xysegments($xyz...) x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end @@ -79,8 +80,8 @@ function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 # spacing and coordinates - sp = Makie.@lift spacing($grid) - xyz = Makie.@lift Meshes.xyz($grid) + sp = Makie.@lift ustrip.(spacing($grid)) + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) if nc[] == 1 # visualize bounding box with a single diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 963c8fd86..a83acc073 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -20,7 +20,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 # grid coordinates - xyz = Makie.@lift Meshes.xyz($grid) + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) xs = Makie.@lift $xyz[1] ys = Makie.@lift $xyz[2] diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 444e84c7c..eec861c39 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -22,7 +22,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) if nc[] == nv[] # size and coordinates sz = Makie.@lift size($grid) .+ 1 - XYZ = Makie.@lift Meshes.XYZ($grid) + XYZ = Makie.@lift map(X -> ustrip.(X), Meshes.XYZ($grid)) X = Makie.@lift $XYZ[1] Y = Makie.@lift $XYZ[2] @@ -47,7 +47,7 @@ function structuredsegments(grid) for j in axes(cinds, 2) for i in axes(cinds, 1) p = vertex(grid, cinds[i, j]) - c = Tuple(coordinates(p)) + c = ustrip.(Tuple(coordinates(p))) push!(coords, c) end push!(coords, (NaN, NaN)) @@ -56,7 +56,7 @@ function structuredsegments(grid) for i in axes(cinds, 1) for j in axes(cinds, 2) p = vertex(grid, cinds[i, j]) - c = Tuple(coordinates(p)) + c = ustrip.(Tuple(coordinates(p))) push!(coords, c) end push!(coords, (NaN, NaN)) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index aa20af9c1..00c8badf3 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -49,7 +49,7 @@ end function makietransform!(plot, trans::Makie.Observable{<:Translate}) offsets = first(TB.parameters(trans[])) - Makie.translate!(plot, offsets...) + Makie.translate!(plot, ustrip.(offsets)...) end function makietransform!(plot, trans::Makie.Observable{<:Scale}) @@ -67,5 +67,5 @@ function makietransform!(plot, trans::Makie.Observable{<:Affine{2}}) θ = first(Rotations.params(rot)) Makie.rotate!(plot, θ) end - Makie.translate!(plot, b...) + Makie.translate!(plot, ustrip.(b)...) end diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index 9ad59da10..ad02295f3 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -72,7 +72,7 @@ function vizmesh2D!(plot) elems = elements(topo) # coordinates of vertices - coords = coordinates.(verts) + coords = map(p -> ustrip.(coordinates(p)), verts) # fan triangulation (assume convexity) tris4elem = map(elems) do elem @@ -148,11 +148,12 @@ function vizmesh2D!(plot) # retrieve coordinates parameters xparams = Makie.@lift let # relevant settings + T = Unitful.numtype(Meshes.lentype($mesh)) dim = embeddim($mesh) topo = topology($mesh) nvert = nvertices($mesh) verts = vertices($mesh) - coords = coordinates.(verts) + coords = map(p -> ustrip.(coordinates(p)), verts) # use a sophisticated data structure # to extract the edges from the n-gons @@ -168,7 +169,7 @@ function vizmesh2D!(plot) end # fill sentinel index with NaN coordinates - push!(coords, Vec(ntuple(i -> NaN, dim))) + push!(coords, SVector(ntuple(i -> T(NaN), dim))) # extract incident vertices coords = coords[inds] @@ -199,10 +200,10 @@ end function segmentsof(topo, vert) p = first(vert) - T = coordtype(p) + T = Unitful.numtype(Meshes.lentype(p)) Dim = embeddim(p) - nan = Vec{Dim,T}(ntuple(i -> NaN, Dim)) - xs = coordinates.(vert) + nan = SVector(ntuple(i -> T(NaN), Dim)) + xs = map(p -> ustrip.(coordinates(p)), vert) coords = map(elements(topo)) do e inds = indices(e) @@ -213,7 +214,7 @@ function segmentsof(topo, vert) end function segmentsof(topo::GridTopology, vert) - xs = coordinates.(vert) + xs = map(p -> ustrip.(coordinates(p)), vert) ip = first(isperiodic(topo)) ip ? [xs; [first(xs)]] : xs end diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index 361d2d320..b87d192d3 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -const SubCartesianGrid{Dim,T} = SubDomain{Dim,T,<:CartesianGrid{Dim,T}} +const SubCartesianGrid{Dim} = SubDomain{Dim,<:CartesianGrid{Dim}} function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) subgrid = plot[:object] @@ -18,10 +18,10 @@ function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) gparams = Makie.@lift let grid = parent($subgrid) dim = embeddim(grid) - sp = spacing(grid) + sp = ustrip.(spacing(grid)) # coordinates of centroids - coord(e) = coordinates(centroid(e)) + coord(e) = ustrip.(coordinates(centroid(e))) coords = [coord(e) .+ sp ./ 2 for e in $subgrid] # rectangle marker diff --git a/ext/vector.jl b/ext/vector.jl index 3f13a20b1..48e077296 100644 --- a/ext/vector.jl +++ b/ext/vector.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,T}}}}) where {Dim,T} +function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,ℒ}}}}) where {Dim,ℒ} vecs = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -16,6 +16,7 @@ function Makie.plot!(plot::Viz{<:Tuple{AbstractVector{Vec{Dim,T}}}}) where {Dim, colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) # visualize as built-in arrows + T = Unitful.numtype(ℒ) orig = Makie.@lift fill(zero(Makie.Point{Dim,T}), length($vecs)) dirs = Makie.@lift asmakie.($vecs) size = Makie.@lift 0.1 * $segmentsize diff --git a/src/Meshes.jl b/src/Meshes.jl index 478fe0ea9..35fed8800 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -4,6 +4,7 @@ module Meshes +using CoordRefSystems using StaticArrays using SparseArrays using CircularArrays @@ -35,6 +36,12 @@ import TransformsBase: isrevertible, isinvertible import TransformsBase: apply, revert, reapply, inverse import TransformsBase: parameters +# CoordRefSystems API +import CoordRefSystems: lentype + +# unit utils +include("units.jl") + # IO utils include("ioutils.jl") @@ -113,12 +120,6 @@ include("viz.jl") export # vectors Vec, - Vec1, - Vec2, - Vec3, - Vec1f, - Vec2f, - Vec3f, ∠, ⋅, ×, @@ -127,19 +128,12 @@ export Geometry, embeddim, paramdim, - coordtype, center, centroid, # primitives Primitive, Point, - Point1, - Point2, - Point3, - Point1f, - Point2f, - Point3f, Ray, Line, BezierCurve, @@ -271,7 +265,6 @@ export SubDomain, embeddim, paramdim, - coordtype, element, nelements, diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 61a4036b1..98ddcff71 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -29,9 +29,9 @@ boundingbox(p::Point) = Box(p, p) boundingbox(b::Box) = b -function boundingbox(r::Ray{Dim,T}) where {Dim,T} - lower(p, v) = v < 0 ? typemin(T) : p - upper(p, v) = v > 0 ? typemax(T) : p +function boundingbox(r::Ray) + lower(p, v) = v < zero(v) ? typemin(p) : p + upper(p, v) = v > zero(v) ? typemax(p) : p p = r(0) v = r(1) - r(0) l = lower.(coordinates(p), v) @@ -60,11 +60,11 @@ function boundingbox(c::ConeSurface) boundingbox([ps; apex(c)]) end -function boundingbox(p::ParaboloidSurface{T}) where {T} +function boundingbox(p::ParaboloidSurface) v = apex(p) r = radius(p) f = focallength(p) - Box(v + Vec(-r, -r, T(0)), v + Vec(r, r, r^2 / (4f))) + Box(v + Vec(-r, -r, zero(r)), v + Vec(r, r, r^2 / (4f))) end boundingbox(t::Torus) = _pboxes(pointify(t)) @@ -73,10 +73,10 @@ boundingbox(g::CartesianGrid) = Box(extrema(g)...) boundingbox(g::RectilinearGrid) = Box(extrema(g)...) -boundingbox(g::TransformedGrid{Dim,T,<:CartesianGrid{Dim,T}}) where {Dim,T} = +boundingbox(g::TransformedGrid{Dim,<:CartesianGrid{Dim}}) where {Dim} = boundingbox(parent(g)) |> transform(g) |> boundingbox -boundingbox(g::TransformedGrid{Dim,T,<:RectilinearGrid{Dim,T}}) where {Dim,T} = +boundingbox(g::TransformedGrid{Dim,<:RectilinearGrid{Dim}}) where {Dim} = boundingbox(parent(g)) |> transform(g) |> boundingbox boundingbox(m::Mesh) = _pboxes(vertices(m)) @@ -89,14 +89,14 @@ _bboxes(boxes) = _pboxes(point for box in boxes for point in extrema(box)) function _pboxes(points) p = first(points) - T = coordtype(p) + ℒ = lentype(p) Dim = embeddim(p) - xmin = MVector(ntuple(i -> typemax(T), Dim)) - xmax = MVector(ntuple(i -> typemin(T), Dim)) + xmin = MVector(ntuple(i -> typemax(ℒ), Dim)) + xmax = MVector(ntuple(i -> typemin(ℒ), Dim)) for p in points x = coordinates(p) @. xmin = min(x, xmin) @. xmax = max(x, xmax) end - Box(Point(xmin), Point(xmax)) + Box(Point(Vec(xmin)), Point(Vec(xmax))) end diff --git a/src/clipping/sutherlandhodgman.jl b/src/clipping/sutherlandhodgman.jl index af74f20d0..5f2f1e016 100644 --- a/src/clipping/sutherlandhodgman.jl +++ b/src/clipping/sutherlandhodgman.jl @@ -24,7 +24,7 @@ function clip(poly::Polygon, other::Geometry, method::SutherlandHodgman) isempty(r) ? nothing : PolyArea(r) end -function clip(ring::Ring{Dim,T}, other::Ring{Dim,T}, ::SutherlandHodgman) where {Dim,T} +function clip(ring::Ring{Dim}, other::Ring{Dim}, ::SutherlandHodgman) where {Dim} # make sure other ring is CCW occw = orientation(other) == CCW ? other : reverse(other) @@ -36,7 +36,7 @@ function clip(ring::Ring{Dim,T}, other::Ring{Dim,T}, ::SutherlandHodgman) where n = length(r) - u = Point{Dim,T}[] + u = Vector{eltype(r)}() for j in 1:n r₁ = r[j] r₂ = r[mod1(j + 1, n)] diff --git a/src/complement.jl b/src/complement.jl index 1f1b3895e..5c1c5f31b 100644 --- a/src/complement.jl +++ b/src/complement.jl @@ -11,11 +11,11 @@ respect to its bounding box. !(g::Geometry) = _complement(_boxboundary(g), boundary(g)) function _boxboundary(g) - T = coordtype(g) + ℒ = lentype(g) b = boundingbox(g) c = coordinates(center(b)) l = sides(b) - α = (l .+ 2atol(T)) ./ l + α = (l .+ 2atol(ℒ)) ./ l t = Translate(-c...) → Scale(α) → Translate(c...) boundary(t(b)) end diff --git a/src/discretization.jl b/src/discretization.jl index 4e7089714..8026133a2 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -35,7 +35,7 @@ function discretizewithin end discretize(geometry::Geometry, method::BoundaryDiscretizationMethod) = discretizewithin(boundary(geometry), method) -function discretize(polygon::Polygon{Dim,T}, method::BoundaryDiscretizationMethod) where {Dim,T} +function discretize(polygon::Polygon, method::BoundaryDiscretizationMethod) # clean up polygon if necessary cpoly = polygon |> Repair{0}() |> Repair{8}() @@ -49,7 +49,7 @@ function discretize(polygon::Polygon{Dim,T}, method::BoundaryDiscretizationMetho # build bridges in case the polygon has holes, # i.e. reduce to a single outer boundary - bpoly, dups = apply(Bridge(2atol(T)), cpoly) + bpoly, dups = apply(Bridge(2atol(lentype(polygon))), cpoly) # discretize using outer boundary mesh = discretizewithin(boundary(bpoly), method) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index 37373708d..6735fe81c 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -31,7 +31,7 @@ function discretizewithin(ring::Ring{2}, ::Dehn1899) SimpleMesh(points, connec) end -function dehn1899(v::AbstractVector{Point{Dim,T}}, inds) where {Dim,T} +function dehn1899(v::AbstractVector{<:Point}, inds) I = CircularVector(inds) n = length(I) diff --git a/src/discretization/fist.jl b/src/discretization/fist.jl index 4a55178be..caf888e9d 100644 --- a/src/discretization/fist.jl +++ b/src/discretization/fist.jl @@ -148,7 +148,7 @@ earsccw(𝒫) = filter(i -> isearccw(𝒫, i), 1:nvertices(𝒫)) # tells whether or not vertex i is an ear of 𝒫 # assuming that 𝒫 has counter-clockwise orientation -function isearccw(𝒫::Ring{Dim,T}, i) where {Dim,T} +function isearccw(𝒫::Ring, i) v = vertices(𝒫) # CE1.1: classify angle as convex vs. reflex @@ -174,9 +174,8 @@ end # helper function to compute the vexity of vertex i function vexity(v, i) - T = coordtype(first(v)) α = ∠(v[i - 1], v[i], v[i + 1]) # oriented angle - θ = α > 0 ? 2 * T(π) - α : -α # inner angle + θ = α > 0 ? oftype(α, 2π) - α : -α # inner angle θ < π ? :CONVEX : :REFLEX end diff --git a/src/distances.jl b/src/distances.jl index 464406d1e..2a7e71fd4 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -22,11 +22,11 @@ end evaluate(Euclidean(), line1, line2) Evaluate the minimum Euclidean distance between `line1` and `line2`. """ -function evaluate(::Euclidean, line1::Line{Dim,T}, line2::Line{Dim,T}) where {Dim,T} +function evaluate(::Euclidean, line1::Line{Dim}, line2::Line{Dim}) where {Dim} λ₁, λ₂, r, rₐ = intersectparameters(line1(0), line1(1), line2(0), line2(1)) if (r == rₐ == 2) || (r == rₐ == 1) # lines intersect or are colinear - return T(0) + return zero(lentype(line1)) elseif (r == 1) && (rₐ == 2) # lines are parallel return evaluate(Euclidean(), line1(0), line2) else # get distance between closest points on each line diff --git a/src/domains.jl b/src/domains.jl index dcea5481d..a11dd36cb 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Domain + Domain{Dim} A domain is an indexable collection of geometries (e.g. mesh). """ -abstract type Domain{Dim,T} end +abstract type Domain{Dim} end """ element(domain, ind) @@ -60,7 +60,7 @@ Base.vcat(ds::Domain...) = reduce(vcat, ds) Return the number of dimensions of the space where the `domain` is embedded. """ -embeddim(::Type{<:Domain{Dim,T}}) where {Dim,T} = Dim +embeddim(::Type{<:Domain{Dim}}) where {Dim} = Dim embeddim(d::Domain) = embeddim(typeof(d)) """ @@ -72,12 +72,11 @@ parametric dimensions of its elements. paramdim(d::Domain) = paramdim(first(d)) """ - coordtype(domain) + lentype(domain) -Return the machine type of each coordinate used to describe the `domain`. +Return the length type of the `domain`. """ -coordtype(::Type{<:Domain{Dim,T}}) where {Dim,T} = T -coordtype(d::Domain) = coordtype(typeof(d)) +lentype(d::Domain) = lentype(typeof(d)) """ centroid(domain, ind) @@ -92,14 +91,14 @@ centroid(d::Domain, ind::Int) = centroid(d[ind]) Return the centroid of the `domain`, i.e. the centroid of all its element's centroids. """ -function centroid(d::Domain{Dim,T}) where {Dim,T} +function centroid(d::Domain) coords(i) = coordinates(centroid(d, i)) volume(i) = measure(element(d, i)) n = nelements(d) x = coords.(1:n) w = volume.(1:n) - all(iszero, w) && (w = ones(T, n)) - Point(sum(w .* x) / sum(w)) + all(iszero, w) && (w = ones(eltype(w), n)) + Point(Vec(sum(w .* x) / sum(w))) end """ @@ -121,10 +120,10 @@ topology(d::Domain) = d.topology # IO METHODS # ----------- -function Base.summary(io::IO, d::Domain{Dim,T}) where {Dim,T} +function Base.summary(io::IO, d::Domain) nelm = nelements(d) name = prettyname(d) - print(io, "$nelm $name{$Dim,$T}") + print(io, "$nelm $name") end Base.show(io::IO, d::Domain) = summary(io, d) diff --git a/src/geometries.jl b/src/geometries.jl index d51cd11c2..c8ca9103d 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Geometry{Dim,T} + Geometry{Dim} -A geometry embedded in a `Dim`-dimensional space with coordinates of type `T`. +A geometry embedded in a `Dim`-dimensional space. """ -abstract type Geometry{Dim,T} end +abstract type Geometry{Dim} end Broadcast.broadcastable(g::Geometry) = Ref(g) @@ -16,7 +16,7 @@ Broadcast.broadcastable(g::Geometry) = Ref(g) Return the number of dimensions of the space where the `geometry` is embedded. """ -embeddim(::Type{<:Geometry{Dim,T}}) where {Dim,T} = Dim +embeddim(::Type{<:Geometry{Dim}}) where {Dim} = Dim embeddim(g::Geometry) = embeddim(typeof(g)) """ @@ -30,12 +30,11 @@ See also [`isparametrized`](@ref). paramdim(g::Geometry) = paramdim(typeof(g)) """ - coordtype(geometry) + lentype(geometry) -Return the machine type of each coordinate used to describe the `geometry`. +Return the length type of the `geometry`. """ -coordtype(::Type{<:Geometry{Dim,T}}) where {Dim,T} = T -coordtype(g::Geometry) = coordtype(typeof(g)) +lentype(g::Geometry) = lentype(typeof(g)) """ centroid(geometry) @@ -56,7 +55,7 @@ Base.extrema(g::Geometry) = extrema(boundingbox(g)) # IO METHODS # ----------- -Base.summary(io::IO, geom::Geometry{Dim,T}) where {Dim,T} = print(io, "$(prettyname(geom)){$Dim,$T}") +Base.summary(io::IO, geom::Geometry) = print(io, prettyname(geom)) # ---------------- # IMPLEMENTATIONS diff --git a/src/hulls/graham.jl b/src/hulls/graham.jl index 4f1153af6..a5238a18d 100644 --- a/src/hulls/graham.jl +++ b/src/hulls/graham.jl @@ -21,7 +21,8 @@ struct GrahamScan <: HullMethod end function hull(points, ::GrahamScan) pₒ = first(points) Dim = embeddim(pₒ) - T = coordtype(pₒ) + ℒ = lentype(pₒ) + T = numtype(ℒ) @assert Dim == 2 "Graham's scan only defined in 2D" @@ -39,7 +40,7 @@ function hull(points, ::GrahamScan) # sort points by polar angle O = p[1] q = p[2:n] - A = O + Vec{2,T}(0, -1) + A = O + Vec(zero(ℒ), -oneunit(ℒ)) θ = [∠(A, O, B) for B in q] q = q[sortperm(θ)] diff --git a/src/hulls/jarvis.jl b/src/hulls/jarvis.jl index 7894090ef..ba9391451 100644 --- a/src/hulls/jarvis.jl +++ b/src/hulls/jarvis.jl @@ -22,7 +22,7 @@ struct JarvisMarch <: HullMethod end function hull(points, ::JarvisMarch) pₒ = first(points) Dim = embeddim(pₒ) - T = coordtype(pₒ) + ℒ = lentype(pₒ) @assert Dim == 2 "Jarvis's march only defined in 2D" @@ -42,7 +42,7 @@ function hull(points, ::JarvisMarch) # find next point with smallest angle O = p[i] - A = O + Vec{2,T}(0, -1) + A = O + Vec(zero(ℒ), -oneunit(ℒ)) j = argmin(l -> ∠(A, O, p[l]), 𝒞) # initialize ring of indices diff --git a/src/intersections.jl b/src/intersections.jl index db733f283..f46455bcb 100644 --- a/src/intersections.jl +++ b/src/intersections.jl @@ -70,7 +70,7 @@ end Return the intersection of two geometries or domains `g₁` and `g₂` as a new (multi-)geometry. """ -Base.intersect(g₁::Union{Geometry,Domain}, g₂::Union{Geometry,Domain}) = get(intersection(g₁, g₂)) +Base.intersect(g₁::GeometryOrDomain, g₂::GeometryOrDomain) = get(intersection(g₁, g₂)) """ intersection([f], g₁, g₂) diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 295094c35..11cde1904 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -8,7 +8,7 @@ # 2. intersect at corner point (CornerTouching -> Point) # 3. intersect at one of the facets (Touching -> Box) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, box₁::Box{Dim,T}, box₂::Box{Dim,T}) where {Dim,T} +function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} # retrieve corner points m1, M1 = coordinates.(extrema(box₁)) m2, M2 = coordinates.(extrema(box₂)) @@ -20,7 +20,7 @@ function intersection(f, box₁::Box{Dim,T}, box₂::Box{Dim,T}) where {Dim,T} # auxiliary variables δ = v - u δ̄ = abs.(δ) - τ = atol(T) + τ = atol(eltype(δ)) # branch on possible configurations if all(>(τ), δ) diff --git a/src/intersections/domains.jl b/src/intersections/domains.jl index 272c5e979..99e0bcfd4 100644 --- a/src/intersections/domains.jl +++ b/src/intersections/domains.jl @@ -13,9 +13,9 @@ end intersection(f, dom::Domain, pset::PointSet) = intersection(f, Multi(collect(dom)), pset) -function intersection(f, dom₁::Domain{Dim,T}, dom₂::Domain{Dim,T}) where {Dim,T} +function intersection(f, dom₁::Domain{Dim}, dom₂::Domain{Dim}) where {Dim} # loop over all geometries - gs = Geometry{Dim,T}[] + gs = Geometry{Dim}[] for g₁ in dom₁, g₂ in dom₂ g = g₁ ∩ g₂ isnothing(g) || push!(gs, g) diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index eaa98ebff..1ac4b8385 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -3,37 +3,40 @@ # ------------------------------------------------------------------ # (https://en.wikipedia.org/wiki/Plane-plane_intersection) -function intersection(f, plane1::Plane{T}, plane2::Plane{T}) where {T} - n1 = normal(plane1) - n2 = normal(plane2) +function intersection(f, plane1::Plane, plane2::Plane) + u = unit(lentype(plane1)) + n1 = ustrip.(normal(plane1)) + n2 = ustrip.(normal(plane2)) + o1 = ustrip.(coordinates(plane1.p)) + o2 = ustrip.(coordinates(plane2.p)) n1n2 = n1 ⋅ n2 - if isapprox(abs(n1n2), one(T), atol=atol(T)) + if isapproxone(abs(n1n2)) # planes are parallel and do not intersect return @IT NotIntersecting nothing f else d = n1 × n2 - h1 = n1 ⋅ plane1.p.coords - h2 = n2 ⋅ plane2.p.coords + h1 = n1 ⋅ o1 + h2 = n2 ⋅ o2 c1 = (h1 - h2 * n1n2) / (1 - n1n2^2) c2 = (h2 - h1 * n1n2) / (1 - n1n2^2) p1 = (c1 * n1) + (c2 * n2) p2 = p1 + d - return @IT Intersecting Line(Point(p1), Point(p2)) f + return @IT Intersecting Line(Point(Vec(p1 * u)), Point(Vec(p2 * u))) f end end -const LineLike{T} = Union{Line{3,T},Ray{3,T},Segment{3,T}} +const LineLike = Union{Segment{3},Ray{3},Line{3}} # (https://en.wikipedia.org/wiki/Line-plane_intersection) -function intersection(f, line::LineLike{T}, plane::Plane{T}) where {T} +function intersection(f, line::LineLike, plane::Plane) # auxiliary parameters d = line(1) - line(0) n = normal(plane) a = (plane(0, 0) - line(0)) ⋅ n b = d ⋅ n - if isapprox(b, zero(T), atol=atol(T)) - if isapprox(a, zero(T), atol=atol(T)) + if isapproxzero(b) + if isapproxzero(a) return @IT Overlapping line f else return @IT NotIntersecting nothing f @@ -49,19 +52,19 @@ end # λ < 0 or λ > 1 ⟹ NotIntersecting # λ ≈ 0 or λ ≈ 1 ⟹ Touching # λ > 0 and λ < 1 ⟹ Crossing -function _intersection(f, seg::Segment{3,T}, λ) where {T} +function _intersection(f, seg::Segment{3}, λ) # if λ is approximately 0, set as so to prevent any domain errors - if isapprox(λ, zero(T), atol=atol(T)) + if isapproxzero(λ) return @IT Touching seg(0) f end # if λ is approximately 1, set as so to prevent any domain errors - if isapprox(λ, one(T), atol=atol(T)) + if isapproxone(λ) return @IT Touching seg(1) f end # if λ is out of bounds for the segment, then there is no intersection - if (λ < zero(T) || λ > one(T)) + if (λ < zero(λ) || λ > one(λ)) return @IT NotIntersecting nothing f else return @IT Crossing seg(λ) f @@ -74,14 +77,14 @@ end # λ < 0 ⟹ NotIntersecting # λ ≈ 0 ⟹ Touching # λ > 0 ⟹ Crossing -function _intersection(f, ray::Ray{3,T}, λ) where {T} +function _intersection(f, ray::Ray{3}, λ) # if λ is approximately 0, set as so to prevent any domain errors - if isapprox(λ, zero(T), atol=atol(T)) + if isapproxzero(λ) return @IT Touching ray(0) f end # if λ is out of bounds for the ray, then there is no intersection - if (λ < zero(T)) + if (λ < zero(λ)) return @IT NotIntersecting nothing f else return @IT Crossing ray(λ) f diff --git a/src/intersections/rays.jl b/src/intersections/rays.jl index 8de035cdc..aa3cd1285 100644 --- a/src/intersections/rays.jl +++ b/src/intersections/rays.jl @@ -10,12 +10,14 @@ # 4. overlap with aligned vectors (PosOverlapping -> Ray) # 5. overlap with colliding vectors (NegOverlapping -> Segment) # 6. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, ray₁::Ray{N,T}, ray₂::Ray{N,T}) where {N,T} +function intersection(f, ray₁::Ray{Dim}, ray₂::Ray{Dim}) where {Dim} + ℒ = lentype(ray₁) a, b = ray₁(0), ray₁(1) c, d = ray₂(0), ray₂(1) # normalize points to gain parameters λ₁, λ₂ corresponding to arc lengths - l₁, l₂ = norm(b - a), norm(d - c) + l₁ = ustrip(norm(b - a)) + l₂ = ustrip(norm(d - c)) b₀ = a + 1 / l₁ * (b - a) d₀ = c + 1 / l₂ * (d - c) @@ -26,8 +28,8 @@ function intersection(f, ray₁::Ray{N,T}, ray₂::Ray{N,T}) where {N,T} return @IT NotIntersecting nothing f #CASE 6 # collinear elseif r == rₐ == 1 - if (b - a) ⋅ (d - c) ≥ 0 # rays aligned in same direction - if (a - c) ⋅ (b - a) ≥ 0 # origin of ray₁ ∈ ray₂ + if (b - a) ⋅ (d - c) ≥ zero(ℒ)^2 # rays aligned in same direction + if (a - c) ⋅ (b - a) ≥ zero(ℒ)^2 # origin of ray₁ ∈ ray₂ return @IT PosOverlapping ray₁ f # CASE 4: ray₁ else return @IT PosOverlapping ray₂ f # CASE 4: ray₂ @@ -43,8 +45,8 @@ function intersection(f, ray₁::Ray{N,T}, ray₂::Ray{N,T}) where {N,T} end # in same plane, not parallel else - λ₁ = mayberound(λ₁, zero(T)) - λ₂ = mayberound(λ₂, zero(T)) + λ₁ = mayberound(λ₁, zero(λ₁)) + λ₂ = mayberound(λ₂, zero(λ₂)) if λ₁ < 0 || λ₂ < 0 return @IT NotIntersecting nothing f # CASE 6 elseif λ₁ == 0 @@ -69,12 +71,12 @@ end # 2. intersect at origin of ray (Touching -> Point) # 3. overlap of line and ray (Overlapping -> Ray) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, ray::Ray{N,T}, line::Line{N,T}) where {N,T} +function intersection(f, ray::Ray{Dim}, line::Line{Dim}) where {Dim} a, b = ray(0), ray(1) c, d = line(0), line(1) # rescaling of point b necessary to gain a parameter λ₁ representing the arc length - l₁ = norm(b - a) + l₁ = ustrip(norm(b - a)) b₀ = a + 1 / l₁ * (b - a) λ₁, _, r, rₐ = intersectparameters(a, b₀, c, d) @@ -84,7 +86,7 @@ function intersection(f, ray::Ray{N,T}, line::Line{N,T}) where {N,T} elseif r == rₐ == 1 # collinear return @IT Overlapping ray f # CASE 3 else # in same plane, not parallel - λ₁ = mayberound(λ₁, zero(T)) + λ₁ = mayberound(λ₁, zero(λ₁)) if λ₁ > 0 return @IT Crossing ray(λ₁ / l₁) f # CASE 1 elseif λ₁ == 0 @@ -97,11 +99,13 @@ end # Williams A, Barrus S, Morley R K, et al., 2005. # (https://dl.acm.org/doi/abs/10.1145/1198555.1198748) -function intersection(f, ray::Ray{Dim,T}, box::Box{Dim,T}) where {Dim,T} - invdir = one(T) ./ (ray(1) - ray(0)) +function intersection(f, ray::Ray{Dim}, box::Box{Dim}) where {Dim} + ℒ = lentype(ray) + invdir = inv.(ray(1) - ray(0)) lo, up = coordinates.(extrema(box)) orig = coordinates(ray(0)) + T = numtype(ℒ) tmin = zero(T) tmax = typemax(T) @@ -111,7 +115,8 @@ function intersection(f, ray::Ray{Dim,T}, box::Box{Dim,T}) where {Dim,T} imax = (up[i] - orig[i]) * invdir[i] # swap variables if necessary - invdir[i] < zero(T) && ((imin, imax) = (imax, imin)) + iinv = invdir[i] + iinv < zero(iinv) && ((imin, imax) = (imax, imin)) # the ray is on a face of the box, avoid NaN (isnan(imin) || isnan(imax)) && continue @@ -138,7 +143,7 @@ end # # Möller, T. & Trumbore, B., 1997. # (https://www.tandfonline.com/doi/abs/10.1080/10867651.1997.10487468) -function intersection(f, ray::Ray{3,T}, tri::Triangle{3,T}) where {T} +function intersection(f, ray::Ray{3}, tri::Triangle{3}) vs = vertices(tri) o = ray(0) d = ray(1) - ray(0) @@ -149,21 +154,21 @@ function intersection(f, ray::Ray{3,T}, tri::Triangle{3,T}) where {T} det = e₁ ⋅ p # keep det > 0, modify T accordingly - if det > atol(T) + if det > atol(det) τ = o - vs[1] else τ = vs[1] - o det = -det end - if det < atol(T) + if det < atol(det) # This ray is parallel to the plane of the triangle. return @IT NotIntersecting nothing f end # calculate u parameter and test bounds u = τ ⋅ p - if u < -atol(T) || u > det + if u < -atol(u) || u > det return @IT NotIntersecting nothing f end @@ -171,36 +176,36 @@ function intersection(f, ray::Ray{3,T}, tri::Triangle{3,T}) where {T} # calculate v parameter and test bounds v = d ⋅ q - if v < -atol(T) || u + v > det + if v < -atol(v) || u + v > det return @IT NotIntersecting nothing f end - λ = (e₂ ⋅ q) * (one(T) / det) + λ = (e₂ ⋅ q) * inv(det) - if λ < -atol(T) + if λ < -atol(λ) return @IT NotIntersecting nothing f end # assemble barycentric weights - w = Vec(u, v, det - u - v) + w = (u, v, det - u - v) - if any(isapprox.(o, vs, atol=atol(T))) + if any(isapprox.(o, vs)) return @IT CornerTouching ray(λ) f - elseif isapprox(λ, zero(T), atol=atol(T)) - if all(>(zero(T)), w) + elseif isapproxzero(λ) + if all(x -> x > zero(x), w) return @IT Touching ray(λ) f else return @IT EdgeTouching ray(λ) f end end - if count(x -> isapprox(x, zero(T), atol=atol(T)), w) == 1 + if count(x -> isapproxzero(x), w) == 1 return @IT EdgeCrossing ray(λ) f - elseif count(x -> isapprox(x, det, atol=atol(T)), w) == 1 + elseif count(x -> isapproxequal(x, det), w) == 1 return @IT CornerCrossing ray(λ) f end - λ = clamp(λ, zero(T), typemax(T)) + λ = clamp(λ, zero(λ), typemax(λ)) return @IT Crossing ray(λ) f end diff --git a/src/intersections/segments.jl b/src/intersections/segments.jl index 032ecf4b3..feada9de8 100644 --- a/src/intersections/segments.jl +++ b/src/intersections/segments.jl @@ -9,7 +9,7 @@ # 3. intersect at one endpoint of both segments (CornerTouching -> Point) # 4. overlap of segments (Overlapping -> Segments) # 5. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg₁::Segment{N,T}, seg₂::Segment{N,T}) where {N,T} +function intersection(f, seg₁::Segment{Dim}, seg₂::Segment{Dim}) where {Dim} a, b = vertices(seg₁) c, d = vertices(seg₂) @@ -38,8 +38,8 @@ function intersection(f, seg₁::Segment{N,T}, seg₂::Segment{N,T}) where {N,T} end end - l₁ = length(seg₁) - l₂ = length(seg₂) + l₁ = ustrip(length(seg₁)) + l₂ = ustrip(length(seg₂)) b₀ = a + 1 / l₁ * (b - a) d₀ = c + 1 / l₂ * (d - c) @@ -57,8 +57,8 @@ function intersection(f, seg₁::Segment{N,T}, seg₂::Segment{N,T}) where {N,T} vd = d - a λc = vc[i] / v[i] λd = vd[i] / v[i] - λc = mayberound(mayberound(λc, zero(T)), l₁) - λd = mayberound(mayberound(λd, zero(T)), l₁) + λc = mayberound(mayberound(λc, zero(λc)), l₁) + λd = mayberound(mayberound(λd, zero(λd)), l₁) if (λc > l₁ && λd > l₁) || (λc < 0 && λd < 0) return @IT NotIntersecting nothing f # CASE 5 elseif (λc == 0 && λd < 0) || (λd == 0 && λc < 0) @@ -66,14 +66,14 @@ function intersection(f, seg₁::Segment{N,T}, seg₂::Segment{N,T}) where {N,T} elseif (λc == l₁ && λd > l₁) || (λd == l₁ && λc > l₁) return @IT CornerTouching b f # CASE 3 else - t₁, t₂ = _sort4vals(zero(T), one(T), λc / l₁, λd / l₁) + t₁, t₂ = _sort4vals(zero(λc), one(λc), λc / l₁, λd / l₁) p₁ = seg₁(t₁) p₂ = seg₁(t₂) return @IT Overlapping Segment(p₁, p₂) f # CASE 4 end else # in same plane, not parallel - λ₁ = mayberound(mayberound(λ₁, zero(T)), l₁) - λ₂ = mayberound(mayberound(λ₂, zero(T)), l₂) + λ₁ = mayberound(mayberound(λ₁, zero(λ₁)), l₁) + λ₂ = mayberound(mayberound(λ₂, zero(λ₂)), l₂) if λ₁ < 0 || λ₂ < 0 || λ₁ > l₁ || λ₂ > l₂ return @IT NotIntersecting nothing f # CASE 5 # 8 cases remain @@ -104,12 +104,13 @@ end # 3. intersects at one end point of segment and origin of ray (CornerTouching -> Point) # 4. overlap at more than one point (Overlapping -> Segment) # 5. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg::Segment{N,T}, ray::Ray{N,T}) where {N,T} +function intersection(f, seg::Segment{Dim}, ray::Ray{Dim}) where {Dim} a, b = ray(0), ray(1) c, d = seg(0), seg(1) # normalize points to gain parameters λ₁, λ₂ corresponding to arc lengths - l₁, l₂ = norm(b - a), length(seg) + l₁ = ustrip(norm(b - a)) + l₂ = ustrip(length(seg)) b₀ = a + 1 / l₁ * (b - a) d₀ = c + 1 / l₂ * (d - c) @@ -120,10 +121,10 @@ function intersection(f, seg::Segment{N,T}, ray::Ray{N,T}) where {N,T} return @IT NotIntersecting nothing f # CASE 5 # collinear elseif r == rₐ == 1 - rc = sum((c - a) ./ (b - a)) / N - rd = sum((d - a) ./ (b - a)) / N - rc = mayberound(rc, zero(T)) - rd = mayberound(rd, zero(T)) + rc = sum((c - a) ./ (b - a)) / Dim + rd = sum((d - a) ./ (b - a)) / Dim + rc = mayberound(rc, zero(rc)) + rd = mayberound(rd, zero(rd)) if rc > 0 # c ∈ ray if rd ≥ 0 return @IT Overlapping seg f # CASE 4 @@ -147,8 +148,8 @@ function intersection(f, seg::Segment{N,T}, ray::Ray{N,T}) where {N,T} end # in same plane, not parallel else - λ₁ = mayberound(λ₁, zero(T)) - λ₂ = mayberound(mayberound(λ₂, zero(T)), l₂) + λ₁ = mayberound(λ₁, zero(λ₁)) + λ₂ = mayberound(mayberound(λ₂, zero(λ₂)), l₂) if λ₁ < 0 || (λ₂ < 0 || λ₂ > l₂) return @IT NotIntersecting nothing f elseif λ₁ == 0 @@ -172,12 +173,12 @@ end # 2. intersect at an end point of segment (Touching -> Point) # 3. overlap of line and segment (Overlapping -> Segment) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg::Segment{N,T}, line::Line{N,T}) where {N,T} +function intersection(f, seg::Segment{Dim}, line::Line{Dim}) where {Dim} a, b = line(0), line(1) c, d = seg(0), seg(1) # normalize points to gain parameter λ₂ corresponding to arc lengths - l₂ = length(seg) + l₂ = ustrip(length(seg)) d₀ = c + 1 / l₂ * (d - c) _, λ₂, r, rₐ = intersectparameters(a, b, c, d₀) @@ -190,7 +191,7 @@ function intersection(f, seg::Segment{N,T}, line::Line{N,T}) where {N,T} return @IT Overlapping seg f # CASE 3 # in same plane, not parallel else - λ₂ = mayberound(mayberound(λ₂, zero(T)), l₂) + λ₂ = mayberound(mayberound(λ₂, zero(λ₂)), l₂) if λ₂ > 0 && λ₂ < l₂ return @IT Crossing seg(λ₂ / l₂) f # CASE 1, equal to line(λ₁) elseif λ₂ == 0 || λ₂ == l₂ @@ -203,7 +204,7 @@ end # Algorithm 4 of Jiménez, J., Segura, R. and Feito, F. 2009. # (https://www.sciencedirect.com/science/article/pii/S0925772109001448?via%3Dihub) -function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} +function intersection(f, seg::Segment{3}, tri::Triangle{3}) Q1, Q2 = vertices(seg) V1, V2, V3 = vertices(tri) @@ -224,9 +225,9 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} D = Q2 - V3 s = D ⋅ W₁ - if w > atol(T) + if w > atol(w) # rejection 2 - if s > atol(T) + if s > atol(s) return @IT NotIntersecting nothing f end @@ -234,14 +235,14 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} t = W₂ ⋅ C # rejection 3 - if t < -atol(T) + if t < -atol(t) return @IT NotIntersecting nothing f end u = -(W₂ ⋅ B) # rejection 4 - if u < -atol(T) + if u < -atol(u) return @IT NotIntersecting nothing f end @@ -249,9 +250,9 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} if w < (s + t + u) return @IT NotIntersecting nothing f end - elseif w < -atol(T) + elseif w < -atol(w) # rejection 2 - if s < -atol(T) + if s < -atol(s) return @IT NotIntersecting nothing f end @@ -259,14 +260,14 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} t = W₂ ⋅ C # rejection 3 - if t > atol(T) + if t > atol(t) return @IT NotIntersecting nothing f end u = -(W₂ ⋅ B) # rejection 4 - if u > atol(T) + if u > atol(u) return @IT NotIntersecting nothing f end @@ -275,19 +276,19 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} return @IT NotIntersecting nothing f end else # w ≈ 0 - if s > atol(T) + if s > atol(s) W₂ = D × A t = W₂ ⋅ C # rejection 3 - if t < -atol(T) + if t < -atol(t) return @IT NotIntersecting nothing f end u = -(W₂ ⋅ B) # rejection 4 - if u < -atol(T) + if u < -atol(u) return @IT NotIntersecting nothing f end @@ -295,19 +296,19 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} if -s < (t + u) return @IT NotIntersecting nothing f end - elseif s < -atol(T) + elseif s < -atol(s) W₂ = D × A t = W₂ ⋅ C # rejection 3 - if t > atol(T) + if t > atol(t) return @IT NotIntersecting nothing f end u = -(W₂ ⋅ B) # rejection 4 - if u > atol(T) + if u > atol(u) return @IT NotIntersecting nothing f end @@ -321,7 +322,8 @@ function intersection(f, seg::Segment{3,T}, tri::Triangle{3,T}) where {T} end end - λ = clamp(w / (w - s), zero(T), one(T)) + λ = w / (w - s) + λ = clamp(λ, zero(λ), one(λ)) p = Segment(Q1, Q2)(λ) diff --git a/src/ioutils.jl b/src/ioutils.jl index fba6dde99..e6294301a 100644 --- a/src/ioutils.jl +++ b/src/ioutils.jl @@ -59,12 +59,11 @@ printinds(io::IO, inds::AbstractRange) = print(io, inds) printfields(io, obj; kwargs...) = printfields(io, obj, fieldnames(typeof(obj)); kwargs...) -function printfields(io, obj, fnames; compact=false) - if compact - ioctx = IOContext(io, :compact => true) +function printfields(io, obj, fnames; singleline=false) + if singleline vals = map(enumerate(fnames)) do (i, field) val = getfield(obj, i) - str = repr(val, context=ioctx) + str = repr(val, context=io) "$field: $str" end join(io, vals, ", ") diff --git a/src/matrices.jl b/src/matrices.jl index 314e08cc0..e370a53fc 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -90,13 +90,13 @@ function measurematrix(mesh) # retrieve coboundary relation ∂ = Coboundary{0,2}(topology(ℳ)) - # initialize matrix - n = nvertices(ℳ) - M = 1.0 * I(n) - # pre-compute all measures A = measure.(ℳ) + # initialize matrix + n = nvertices(ℳ) + M = oneunit(eltype(A)) * I(n) + # fill matrix with measures for i in 1:n Aᵢ = sum(A[∂(i)]) / 3 diff --git a/src/measures.jl b/src/measures.jl index c71531991..3a04cce60 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -17,56 +17,62 @@ Return the measure or "volume" of the `object`. """ function measure end -measure(::Point{Dim,T}) where {Dim,T} = zero(T) +measure(p::Point) = zero(lentype(p)) -measure(::Ray{Dim,T}) where {Dim,T} = typemax(T) +measure(r::Ray) = typemax(lentype(r)) -measure(::Line{Dim,T}) where {Dim,T} = typemax(T) +measure(l::Line) = typemax(lentype(l)) -measure(::Plane{T}) where {T} = typemax(T) +measure(p::Plane) = typemax(lentype(p))^2 measure(b::Box) = prod(maximum(b) - minimum(b)) # https://en.wikipedia.org/wiki/Volume_of_an_n-ball function measure(b::Ball{Dim}) where {Dim} + T = numtype(lentype(b)) r, n = radius(b), Dim - (π^(n / 2) * r^n) / gamma(n / 2 + 1) + T(π)^T(n / 2) * r^n / gamma(T(n / 2) + 1) end # https://en.wikipedia.org/wiki/N-sphere#Volume_and_surface_area function measure(s::Sphere{Dim}) where {Dim} + T = numtype(lentype(s)) r, n = radius(s), Dim - 2π^(n / 2) * r^(n - 1) / gamma(n / 2) + 2 * T(π)^T(n / 2) * r^(n - 1) / gamma(T(n / 2)) end -measure(d::Disk{T}) where {T} = T(π) * radius(d)^2 +measure(d::Disk) = π * radius(d)^2 -measure(c::Circle{T}) where {T} = 2 * T(π) * radius(c) +measure(c::Circle) = 2 * (π * radius(c)) -function measure(c::Cylinder{T}) where {T} +function measure(c::Cylinder) t = top(c) b = bottom(c) r = radius(c) - norm(t(0, 0) - b(0, 0)) * T(π) * r^2 + h = norm(t(0, 0) - b(0, 0)) + π * r^2 * h end -function measure(c::CylinderSurface{T}) where {T} +function measure(c::CylinderSurface) t = top(c) b = bottom(c) r = radius(c) - (norm(t(0, 0) - b(0, 0)) + r) * 2 * r * T(π) + h = norm(t(0, 0) - b(0, 0)) + 2 * (π * r) * (h + r) end -function measure(p::ParaboloidSurface{T}) where {T} +function measure(p::ParaboloidSurface) + T = numtype(lentype(p)) r = radius(p) f = focallength(p) - T(8π / 3) * f^2 * ((T(1) + r^2 / (2f)^2)^(3 / 2) - T(1)) + (8 * T(π) / 3) * f^2 * ((1 + r^2 / (2f)^2)^T(3 / 2) - 1) end # https://en.wikipedia.org/wiki/Torus -function measure(t::Torus{T}) where {T} +function measure(t::Torus) + T = numtype(lentype(t)) R, r = radii(t) - 4T(π)^2 * R * r + 4 * T(π)^2 * R * r end measure(s::Segment) = norm(maximum(s) - minimum(s)) @@ -89,7 +95,7 @@ measure(g::Geometry) = sum(measure, simplexify(g)) measure(m::Multi) = sum(measure, parent(m)) -measure(::PointSet{Dim,T}) where {Dim,T} = zero(T) +measure(d::PointSet) = zero(lentype(d)) measure(d::Domain) = sum(measure, d) @@ -151,10 +157,10 @@ the [`measure`](@ref) of its [`boundary`](@ref). """ perimeter(g) = measure(boundary(g)) -perimeter(::Line{Dim,T}) where {Dim,T} = zero(T) +perimeter(l::Line) = zero(lentype(l)) -perimeter(::Plane{T}) where {T} = zero(T) +perimeter(p::Plane) = zero(lentype(p)) -perimeter(::Sphere{Dim,T}) where {Dim,T} = zero(T) +perimeter(s::Sphere) = zero(lentype(s)) -perimeter(::Ellipsoid{T}) where {T} = zero(T) +perimeter(e::Ellipsoid) = zero(lentype(e)) diff --git a/src/mesh.jl b/src/mesh.jl index 332ffdddf..1685b748a 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -3,12 +3,11 @@ # ------------------------------------------------------------------ """ - Mesh{Dim,T,TP} + Mesh{Dim,TP} -A mesh embedded in a `Dim`-dimensional space with coordinates of type `T` -and topology of type `TP`. +A mesh embedded in a `Dim`-dimensional space with topology of type `TP`. """ -abstract type Mesh{Dim,T,TP<:Topology} <: Domain{Dim,T} end +abstract type Mesh{Dim,TP<:Topology} <: Domain{Dim} end """ vertex(mesh, ind) @@ -123,7 +122,7 @@ topoconvert(TP::Type{<:Topology}, m::Mesh) = SimpleMesh(vertices(m), convert(TP, ==(m₁::Mesh, m₂::Mesh) = vertices(m₁) == vertices(m₂) && topology(m₁) == topology(m₂) -function Base.show(io::IO, ::MIME"text/plain", m::Mesh{Dim,T}) where {Dim,T} +function Base.show(io::IO, ::MIME"text/plain", m::Mesh) t = topology(m) verts = vertices(m) elems = elements(t) @@ -139,18 +138,18 @@ function Base.show(io::IO, ::MIME"text/plain", m::Mesh{Dim,T}) where {Dim,T} end """ - Grid{Dim,T} + Grid{Dim} -A grid embedded in a `Dim`-dimensional space with coordinates of type `T`. +A grid embedded in a `Dim`-dimensional space. """ -const Grid{Dim,T} = Mesh{Dim,T,GridTopology{Dim}} +const Grid{Dim} = Mesh{Dim,GridTopology{Dim}} """ - SubGrid{Dim,T} + SubGrid{Dim} -A view of a grid in a `Dim`-dimensinoal space with coordinates of type `T`. +A view of a grid in a `Dim`-dimensinoal space. """ -const SubGrid{Dim,T} = SubDomain{Dim,T,<:Grid{Dim,T}} +const SubGrid{Dim} = SubDomain{Dim,<:Grid{Dim}} """ vertex(grid, ijk) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 652dd533d..50c0e0499 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -50,66 +50,84 @@ Create a 1D grid from -1 to 1 with 100 segments: julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` """ -struct CartesianGrid{Dim,T} <: Grid{Dim,T} - origin::Point{Dim,T} - spacing::NTuple{Dim,T} +struct CartesianGrid{Dim,P<:Point{Dim},ℒ<:Len} <: Grid{Dim} + origin::P + spacing::NTuple{Dim,ℒ} offset::Dims{Dim} topology::GridTopology{Dim} + + function CartesianGrid{Dim,P,ℒ}(origin, spacing, offset, topology) where {Dim,P<:Point{Dim},ℒ<:Len} + if !all(>(zero(ℒ)), spacing) + throw(ArgumentError("spacing must be positive")) + end + new(origin, spacing, offset, topology) + end end +CartesianGrid( + origin::P, + spacing::NTuple{Dim,ℒ}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim,P<:Point{Dim},ℒ<:Len} = CartesianGrid{Dim,P,float(ℒ)}(origin, spacing, offset, topology) + +CartesianGrid(origin::Point{Dim}, spacing::NTuple{Dim}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = + CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) + function CartesianGrid( dims::Dims{Dim}, - origin::Point{Dim,T}, - spacing::NTuple{Dim,T}, + origin::Point{Dim}, + spacing::NTuple{Dim}, offset::Dims{Dim}=ntuple(i -> 1, Dim) -) where {Dim,T} - @assert all(>(0), dims) "dimensions must be positive" - @assert all(>(zero(T)), spacing) "spacing must be positive" - CartesianGrid{Dim,T}(origin, spacing, offset, GridTopology(dims)) +) where {Dim} + if !all(>(0), dims) + throw(ArgumentError("dimensions must be positive")) + end + CartesianGrid(origin, spacing, offset, GridTopology(dims)) end CartesianGrid( dims::Dims{Dim}, - origin::NTuple{Dim,T}, - spacing::NTuple{Dim,T}, + origin::NTuple{Dim}, + spacing::NTuple{Dim}, offset::Dims{Dim}=ntuple(i -> 1, Dim) -) where {Dim,T} = CartesianGrid(dims, Point(origin), spacing, offset) +) where {Dim} = CartesianGrid(dims, Point(origin), spacing, offset) -function CartesianGrid(start::Point{Dim,T}, finish::Point{Dim,T}, spacing::NTuple{Dim,T}) where {Dim,T} +function CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} dims = Tuple(ceil.(Int, (finish - start) ./ spacing)) origin = start offset = ntuple(i -> 1, Dim) CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid(start::NTuple{Dim,T}, finish::NTuple{Dim,T}, spacing::NTuple{Dim,T}) where {Dim,T} = +CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim}) where {Dim} = + CartesianGrid(start, finish, addunit.(spacing, u"m")) + +CartesianGrid(start::NTuple{Dim}, finish::NTuple{Dim}, spacing::NTuple{Dim}) where {Dim} = CartesianGrid(Point(start), Point(finish), spacing) -function CartesianGrid(start::Point{Dim,T}, finish::Point{Dim,T}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim,T} +function CartesianGrid(start::Point{Dim}, finish::Point{Dim}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} origin = start spacing = Tuple((finish - start) ./ dims) offset = ntuple(i -> 1, Dim) CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid(start::NTuple{Dim,T}, finish::NTuple{Dim,T}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim,T} = - CartesianGrid(Point(start), Point(finish); dims=dims) +CartesianGrid(start::NTuple{Dim}, finish::NTuple{Dim}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} = + CartesianGrid(Point(start), Point(finish); dims) -function CartesianGrid{T}(dims::Dims{Dim}) where {Dim,T} - origin = ntuple(i -> zero(T), Dim) - spacing = ntuple(i -> oneunit(T), Dim) +function CartesianGrid(dims::Dims{Dim}) where {Dim} + origin = ntuple(i -> 0.0, Dim) + spacing = ntuple(i -> 1.0, Dim) offset = ntuple(i -> 1, Dim) CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid{T}(dims::Vararg{Int,Dim}) where {Dim,T} = CartesianGrid{T}(dims) - -CartesianGrid(dims::Dims{Dim}) where {Dim} = CartesianGrid{Float64}(dims) +CartesianGrid(dims::Int...) = CartesianGrid(dims) -CartesianGrid(dims::Vararg{Int,Dim}) where {Dim} = CartesianGrid{Float64}(dims) +lentype(::Type{<:CartesianGrid{Dim,P}}) where {Dim,P} = lentype(P) -vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = - Point(coordinates(g.origin) .+ (ijk .- g.offset) .* g.spacing) +vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = g.origin + Vec((ijk .- g.offset) .* g.spacing) spacing(g::CartesianGrid) = g.spacing @@ -150,16 +168,16 @@ end # IO METHODS # ----------- -function Base.summary(io::IO, g::CartesianGrid{Dim,T}) where {Dim,T} +function Base.summary(io::IO, g::CartesianGrid) dims = join(size(g.topology), "×") - print(io, "$dims CartesianGrid{$Dim,$T}") + print(io, "$dims CartesianGrid") end Base.show(io::IO, g::CartesianGrid) = summary(io, g) function Base.show(io::IO, ::MIME"text/plain", g::CartesianGrid) println(io, g) - println(io, " minimum: ", minimum(g)) - println(io, " maximum: ", maximum(g)) - print(io, " spacing: ", spacing(g)) + println(io, "├─ minimum: ", minimum(g)) + println(io, "├─ maximum: ", maximum(g)) + print(io, "└─ spacing: ", spacing(g)) end diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 29227c5b3..92ce07445 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -18,11 +18,20 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Dim,T,V<:AbstractVector{T}} <: Grid{Dim,T} +struct RectilinearGrid{Dim,V<:AbstractVector{<:Len}} <: Grid{Dim} xyz::NTuple{Dim,V} topology::GridTopology{Dim} + RectilinearGrid{Dim,V}(xyz, topology) where {Dim,V<:AbstractVector{<:Len}} = new(xyz, topology) end +function RectilinearGrid(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Dim,V<:AbstractVector{<:Len}} + coords = float.(xyz) + RectilinearGrid{Dim,eltype(coords)}(coords, topology) +end + +RectilinearGrid(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Dim,V<:AbstractVector} = + RectilinearGrid(addunit.(xyz, u"m"), topology) + function RectilinearGrid(xyz::Tuple) coords = promote(collect.(xyz)...) topology = GridTopology(length.(coords) .- 1) @@ -31,6 +40,8 @@ end RectilinearGrid(xyz...) = RectilinearGrid(xyz) +lentype(::Type{<:RectilinearGrid{Dim,V}}) where {Dim,V} = eltype(V) + vertex(g::RectilinearGrid{Dim}, ijk::Dims{Dim}) where {Dim} = Point(getindex.(g.xyz, ijk)) xyz(g::RectilinearGrid) = g.xyz @@ -53,7 +64,7 @@ function Base.getindex(g::RectilinearGrid{Dim}, I::CartesianIndices{Dim}) where RectilinearGrid(xyz, GridTopology(dims)) end -function Base.summary(io::IO, g::RectilinearGrid{Dim,T}) where {Dim,T} +function Base.summary(io::IO, g::RectilinearGrid) join(io, size(g), "×") - print(io, " RectilinearGrid{$Dim,$T}") + print(io, " RectilinearGrid") end diff --git a/src/mesh/simplemesh.jl b/src/mesh/simplemesh.jl index d4b378fbd..136285b63 100644 --- a/src/mesh/simplemesh.jl +++ b/src/mesh/simplemesh.jl @@ -31,25 +31,27 @@ See also [`Topology`](@ref), [`GridTopology`](@ref), of the mesh to a [`HalfEdgeTopology`](@ref) instead of a [`SimpleTopology`](@ref). """ -struct SimpleMesh{Dim,T,V<:AbstractVector{Point{Dim,T}},TP<:Topology} <: Mesh{Dim,T,TP} +struct SimpleMesh{Dim,V<:AbstractVector{<:Point{Dim}},TP<:Topology} <: Mesh{Dim,TP} vertices::V topology::TP end -SimpleMesh(coords::AbstractVector{<:NTuple}, topology::Topology) = SimpleMesh(Point.(coords), topology) +SimpleMesh(coords::AbstractVector{<:Tuple}, topology::Topology) = SimpleMesh(Point.(coords), topology) function SimpleMesh(vertices, connec::AbstractVector{<:Connectivity}; relations=false) topology = relations ? HalfEdgeTopology(connec) : SimpleTopology(connec) SimpleMesh(vertices, topology) end +lentype(::Type{<:SimpleMesh{Dim,V}}) where {Dim,V} = lentype(eltype(V)) + vertex(m::SimpleMesh, ind::Int) = m.vertices[ind] vertices(m::SimpleMesh) = m.vertices nvertices(m::SimpleMesh) = length(m.vertices) -function Base.getindex(m::SimpleMesh{Dim,T,V,GridTopology{Dim}}, I::CartesianIndices{Dim}) where {Dim,T,V} +function Base.getindex(m::SimpleMesh{Dim,V,GridTopology{Dim}}, I::CartesianIndices{Dim}) where {Dim,V} @boundscheck _checkbounds(m, I) dims = size(I) odims = size(m) diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index 64145afe1..cf496f78f 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -18,11 +18,20 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Dim,T,A<:AbstractArray{T}} <: Grid{Dim,T} +struct StructuredGrid{Dim,A<:AbstractArray{<:Len}} <: Grid{Dim} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} + StructuredGrid{Dim,A}(XYZ, topology) where {Dim,A<:AbstractArray{<:Len}} = new(XYZ, topology) end +function StructuredGrid(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Dim,A<:AbstractArray{<:Len}} + coords = float.(XYZ) + StructuredGrid{Dim,eltype(coords)}(coords, topology) +end + +StructuredGrid(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Dim,A<:AbstractArray} = + StructuredGrid(addunit.(XYZ, u"m"), topology) + function StructuredGrid(XYZ::Tuple) coords = promote(XYZ...) topology = GridTopology(size(first(coords)) .- 1) @@ -31,6 +40,8 @@ end StructuredGrid(XYZ...) = StructuredGrid(XYZ) +lentype(::Type{<:StructuredGrid{Dim,A}}) where {Dim,A} = eltype(A) + vertex(g::StructuredGrid{Dim}, ijk::Dims{Dim}) where {Dim} = Point(ntuple(d -> g.XYZ[d][ijk...], Dim)) XYZ(g::StructuredGrid) = g.XYZ @@ -43,7 +54,7 @@ function Base.getindex(g::StructuredGrid{Dim}, I::CartesianIndices{Dim}) where { StructuredGrid(XYZ, GridTopology(dims)) end -function Base.summary(io::IO, g::StructuredGrid{Dim,T}) where {Dim,T} +function Base.summary(io::IO, g::StructuredGrid) join(io, size(g), "×") - print(io, " StructuredGrid{$Dim,$T}") + print(io, " StructuredGrid") end diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 81b12a39d..a96bf95ec 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,7 +7,7 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{Dim,T,TP<:Topology,M<:Mesh{Dim,T,TP},TR<:Transform} <: Mesh{Dim,T,TP} +struct TransformedMesh{Dim,TP<:Topology,M<:Mesh{Dim,TP},TR<:Transform} <: Mesh{Dim,TP} mesh::M transform::TR end @@ -15,6 +15,8 @@ end # specialize constructor to avoid deep structures TransformedMesh(m::TransformedMesh, t::Transform) = TransformedMesh(m.mesh, m.transform → t) +lentype(::Type{<:TransformedMesh{Dim,TP,M}}) where {Dim,TP,M} = lentype(M) + Base.parent(m::TransformedMesh) = m.mesh transform(m::TransformedMesh) = m.transform @@ -24,14 +26,14 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{Dim,T,G<:Grid{Dim,T},TR} = TransformedMesh{Dim,T,GridTopology{Dim},G,TR} +const TransformedGrid{Dim,G<:Grid{Dim},TR} = TransformedMesh{Dim,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) @propagate_inbounds Base.getindex(g::TransformedGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} = TransformedGrid(getindex(g.mesh, I), g.transform) -function Base.summary(io::IO, g::TransformedGrid{Dim,T}) where {Dim,T} +function Base.summary(io::IO, g::TransformedGrid) join(io, size(g), "×") - print(io, " TransformedGrid{$Dim,$T}") + print(io, " TransformedGrid") end diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 31ed7026a..5c7a51ce6 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -15,7 +15,7 @@ multiple polygons as a single entity (e.g. country with islands). - Type aliases are [`MultiPoint`](@ref), [`MultiSegment`](@ref), [`MultiRope`](@ref), [`MultiRing`](@ref), [`MultiPolygon`](@ref). """ -struct Multi{Dim,T,G<:Geometry{Dim,T}} <: Geometry{Dim,T} +struct Multi{Dim,G<:Geometry{Dim}} <: Geometry{Dim} geoms::Vector{G} end @@ -23,15 +23,17 @@ end Multi(geoms) = Multi(collect(geoms)) # type aliases for convenience -const MultiPoint{Dim,T} = Multi{Dim,T,<:Point{Dim,T}} -const MultiSegment{Dim,T} = Multi{Dim,T,<:Segment{Dim,T}} -const MultiRope{Dim,T} = Multi{Dim,T,<:Rope{Dim,T}} -const MultiRing{Dim,T} = Multi{Dim,T,<:Ring{Dim,T}} -const MultiPolygon{Dim,T} = Multi{Dim,T,<:Polygon{Dim,T}} -const MultiPolyhedron{Dim,T} = Multi{Dim,T,<:Polyhedron{Dim,T}} +const MultiPoint{Dim} = Multi{Dim,<:Point{Dim}} +const MultiSegment{Dim} = Multi{Dim,<:Segment{Dim}} +const MultiRope{Dim} = Multi{Dim,<:Rope{Dim}} +const MultiRing{Dim} = Multi{Dim,<:Ring{Dim}} +const MultiPolygon{Dim} = Multi{Dim,<:Polygon{Dim}} +const MultiPolyhedron{Dim} = Multi{Dim,<:Polyhedron{Dim}} paramdim(m::Multi) = maximum(paramdim, m.geoms) +lentype(::Type{<:Multi{Dim,G}}) where {Dim,G} = lentype(G) + vertex(m::Multi, ind) = vertices(m)[ind] vertices(m::Multi) = [vertex for geom in m.geoms for vertex in vertices(geom)] @@ -50,7 +52,7 @@ function centroid(m::Multi) Point(sum(cs) / length(cs)) end -rings(m::MultiPolygon{Dim,T}) where {Dim,T} = [ring for poly in m.geoms for ring in rings(poly)] +rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] Base.parent(m::Multi) = m.geoms @@ -62,9 +64,9 @@ Base.isapprox(m₁::Multi, m₂::Multi) = all(g -> g[1] ≈ g[2], zip(m₁.geoms # IO METHODS # ----------- -function Base.summary(io::IO, m::Multi{Dim,T}) where {Dim,T} +function Base.summary(io::IO, m::Multi) name = prettyname(eltype(m.geoms)) - print(io, "Multi$name{$Dim,$T}") + print(io, "Multi$name") end function Base.show(io::IO, m::Multi) diff --git a/src/neighborhoods/metricball.jl b/src/neighborhoods/metricball.jl index acc16ab52..bb0acd293 100644 --- a/src/neighborhoods/metricball.jl +++ b/src/neighborhoods/metricball.jl @@ -31,8 +31,8 @@ Axis-aligned 3D ellipsoid with radii `(3.0, 2.0, 1.0)`: julia> mahalanobis = MetricBall((3.0, 2.0, 1.0)) ``` """ -struct MetricBall{L,R,M} <: Neighborhood - radii::L +struct MetricBall{ℒ,R,M} <: Neighborhood + radii::ℒ rotation::R # state fields diff --git a/src/neighborsearch.jl b/src/neighborsearch.jl index 6bc08b1a8..f3740dfa4 100644 --- a/src/neighborsearch.jl +++ b/src/neighborsearch.jl @@ -63,7 +63,7 @@ function maxneighbors end # ---------- function search!(neighbors, pₒ::Point, method::BoundedNeighborSearchMethod; mask=nothing) - distances = Vector{coordtype(pₒ)}(undef, maxneighbors(method)) + distances = Vector{lentype(pₒ)}(undef, maxneighbors(method)) searchdists!(neighbors, distances, pₒ, method; mask) end @@ -75,7 +75,7 @@ end function searchdists(pₒ::Point, method::BoundedNeighborSearchMethod; mask=nothing) neighbors = Vector{Int}(undef, maxneighbors(method)) - distances = Vector{coordtype(pₒ)}(undef, maxneighbors(method)) + distances = Vector{lentype(pₒ)}(undef, maxneighbors(method)) nneigh = searchdists!(neighbors, distances, pₒ, method; mask) view(neighbors, 1:nneigh), view(distances, 1:nneigh) end diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index a3497b70b..71cca8969 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -18,7 +18,7 @@ end function BallSearch(domain::D, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [coordinates(centroid(domain, i)) for i in 1:nelements(domain)] + xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) BallSearch{D,B,typeof(tree)}(domain, ball, tree) end @@ -29,7 +29,7 @@ function search(pₒ::Point, method::BallSearch; mask=nothing) tree = method.tree dmax = radius(method.ball) - inds = inrange(tree, coordinates(pₒ), dmax) + inds = inrange(tree, ustrip.(coordinates(pₒ)), dmax) if isnothing(mask) inds diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 69ad9c4ab..7af409e20 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -20,7 +20,7 @@ end function KBallSearch(domain::D, k::Int, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [coordinates(centroid(domain, i)) for i in 1:nelements(domain)] + xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) KBallSearch{D,B,typeof(tree)}(domain, k, ball, tree) end @@ -30,11 +30,12 @@ KBallSearch(geoms, k, ball) = KBallSearch(GeometrySet(geoms), k, ball) maxneighbors(method::KBallSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; mask=nothing) + u = unit(lentype(pₒ)) tree = method.tree dmax = radius(method.ball) k = method.k - inds, dists = knn(tree, coordinates(pₒ), k, true) + inds, dists = knn(tree, ustrip.(coordinates(pₒ)), k, true) # keep neighbors inside ball keep = dists .≤ dmax @@ -47,7 +48,7 @@ function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; ma if keep[i] nneigh += 1 neighbors[nneigh] = inds[i] - distances[nneigh] = dists[i] + distances[nneigh] = dists[i] * u end end diff --git a/src/neighborsearch/knearest.jl b/src/neighborsearch/knearest.jl index e73a2e299..b2e93e37d 100644 --- a/src/neighborsearch/knearest.jl +++ b/src/neighborsearch/knearest.jl @@ -18,7 +18,7 @@ struct KNearestSearch{D<:Domain,T} <: BoundedNeighborSearchMethod end function KNearestSearch(domain::D, k::Int; metric=Euclidean()) where {D<:Domain} - xs = [coordinates(centroid(domain, i)) for i in 1:nelements(domain)] + xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] tree = metric isa MinkowskiMetric ? KDTree(xs, metric) : BallTree(xs, metric) KNearestSearch{D,typeof(tree)}(domain, k, tree) end @@ -28,16 +28,17 @@ KNearestSearch(geoms, k; metric=Euclidean()) = KNearestSearch(GeometrySet(geoms) maxneighbors(method::KNearestSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KNearestSearch; mask=nothing) + u = unit(lentype(pₒ)) tree = method.tree k = method.k - inds, dists = knn(tree, coordinates(pₒ), k, true) + inds, dists = knn(tree, ustrip.(coordinates(pₒ)), k, true) if isnothing(mask) nneigh = k @inbounds for i in 1:k neighbors[i] = inds[i] - distances[i] = dists[i] + distances[i] = dists[i] * u end else nneigh = 0 @@ -45,7 +46,7 @@ function searchdists!(neighbors, distances, pₒ::Point, method::KNearestSearch; if mask[inds[i]] nneigh += 1 neighbors[nneigh] = inds[i] - distances[nneigh] = dists[i] + distances[nneigh] = dists[i] * u end end end diff --git a/src/orientation.jl b/src/orientation.jl index 11ee7c69b..2b233b47e 100644 --- a/src/orientation.jl +++ b/src/orientation.jl @@ -58,12 +58,13 @@ based on the winding number. """ struct WindingOrientation <: OrientationMethod end -function orientation(r::Ring{2,T}, ::WindingOrientation) where {T} +function orientation(r::Ring{2}, ::WindingOrientation) # pick any segment - x1, x2 = r.vertices[1:2] - x̄ = center(Segment(x1, x2)) - w = T(2π) * winding(x̄, r) - ∠(x1, x̄, x2) - isapprox(w, T(π), atol=atol(T)) ? CCW : CW + x₁, x₂ = r.vertices[1:2] + x̄ = center(Segment(x₁, x₂)) + w̄ = winding(x̄, r) + w = oftype(w̄, 2π) * w̄ - ∠(x₁, x̄, x₂) + isapproxequal(w, oftype(w, π)) ? CCW : CW end """ @@ -79,9 +80,9 @@ based on signed triangular areas. """ struct TriangleOrientation <: OrientationMethod end -function orientation(r::Ring{2,T}, ::TriangleOrientation) where {T} +function orientation(r::Ring{2}, ::TriangleOrientation) v = vertices(r) Δ(i) = signarea(v[1], v[i], v[i + 1]) a = mapreduce(Δ, +, 2:(length(v) - 1)) - a ≥ zero(T) ? CCW : CW + a ≥ zero(a) ? CCW : CW end diff --git a/src/partitioning/ball.jl b/src/partitioning/ball.jl index 63fd90934..e6e26a04e 100644 --- a/src/partitioning/ball.jl +++ b/src/partitioning/ball.jl @@ -8,11 +8,14 @@ A method for partitioning spatial objects into balls of a given `radius` using a `metric`. """ -struct BallPartition{T,M} <: SPredicatePartitionMethod - radius::T +struct BallPartition{ℒ<:Len,M} <: SPredicatePartitionMethod + radius::ℒ metric::M + BallPartition(radius::ℒ, metric::M) where {ℒ<:Len,M} = new{float(ℒ),M}(radius, metric) end -BallPartition(radius::T; metric::M=Euclidean()) where {T,M} = BallPartition{T,M}(radius, metric) +BallPartition(radius, metric) = BallPartition(addunit(radius, u"m"), metric) + +BallPartition(radius; metric=Euclidean()) = BallPartition(radius, metric) (p::BallPartition)(x, y) = evaluate(p.metric, x, y) < p.radius diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index da93bbfea..4156c4fa4 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -9,23 +9,21 @@ A method for partitioning spatial objects into two half spaces defined by a `normal` direction and a `fraction` of points. The partition is returned within `maxiter` bisection iterations. """ -struct BisectFractionPartition{Dim,T} <: PartitionMethod - normal::Vec{Dim,T} +struct BisectFractionPartition{V<:Vec} <: PartitionMethod + normal::V fraction::Float64 maxiter::Int - - function BisectFractionPartition{Dim,T}(normal, fraction, maxiter) where {Dim,T} - new(normalize(normal), fraction, maxiter) - end + BisectFractionPartition{V}(normal, fraction, maxiter) where {V<:Vec} = new(unormalize(normal), fraction, maxiter) end -BisectFractionPartition(normal::Vec{Dim,T}, fraction=0.5, maxiter=10) where {Dim,T} = - BisectFractionPartition{Dim,T}(normal, fraction, maxiter) +BisectFractionPartition(normal::V, fraction=0.5, maxiter=10) where {V<:Vec} = + BisectFractionPartition{V}(normal, fraction, maxiter) -BisectFractionPartition(normal::NTuple{Dim,T}, fraction=0.5, maxiter=10) where {Dim,T} = +BisectFractionPartition(normal::Tuple, fraction=0.5, maxiter=10) = BisectFractionPartition(Vec(normal), fraction, maxiter) function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionPartition) + u = unit(lentype(domain)) bbox = boundingbox(domain) n = method.normal f = method.fraction @@ -36,8 +34,8 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP maxiter = method.maxiter iter = 0 - a = c - d / 2 * n - b = c + d / 2 * n + a = c - d / 2u * n + b = c + d / 2u * n subsets = Vector{Int}[] metadata = Dict() while iter < maxiter diff --git a/src/partitioning/bisectpoint.jl b/src/partitioning/bisectpoint.jl index 23721f5ab..7dd3bba16 100644 --- a/src/partitioning/bisectpoint.jl +++ b/src/partitioning/bisectpoint.jl @@ -8,18 +8,16 @@ A method for partitioning spatial objects into two half spaces defined by a `normal` direction and a reference `point`. """ -struct BisectPointPartition{Dim,T} <: PartitionMethod - normal::Vec{Dim,T} - point::Point{Dim,T} - - function BisectPointPartition{Dim,T}(normal, point) where {Dim,T} - new(normalize(normal), point) - end +struct BisectPointPartition{Dim,V<:Vec{Dim},P<:Point{Dim}} <: PartitionMethod + normal::V + point::P + BisectPointPartition{Dim,V,P}(normal, point) where {Dim,V<:Vec{Dim},P<:Point{Dim}} = new(unormalize(normal), point) end -BisectPointPartition(normal::Vec{Dim,T}, point::Point{Dim,T}) where {Dim,T} = BisectPointPartition{Dim,T}(normal, point) +BisectPointPartition(normal::V, point::P) where {Dim,V<:Vec{Dim},P<:Point{Dim}} = + BisectPointPartition{Dim,V,P}(normal, point) -BisectPointPartition(normal::NTuple{Dim,T}, point::NTuple{Dim,T}) where {Dim,T} = +BisectPointPartition(normal::NTuple{Dim}, point::NTuple{Dim}) where {Dim} = BisectPointPartition(Vec(normal), Point(point)) function partitioninds(::AbstractRNG, domain::Domain, method::BisectPointPartition) @@ -29,7 +27,7 @@ function partitioninds(::AbstractRNG, domain::Domain, method::BisectPointPartiti left, right = Int[], Int[] for location in 1:nelements(domain) pₒ = centroid(domain, location) - if (pₒ - p) ⋅ n < zero(coordtype(domain)) + if (pₒ - p) ⋅ n < zero(lentype(domain))^2 push!(left, location) else push!(right, location) diff --git a/src/partitioning/block.jl b/src/partitioning/block.jl index 698f4eaee..5c67a0606 100644 --- a/src/partitioning/block.jl +++ b/src/partitioning/block.jl @@ -12,12 +12,15 @@ Optionally, compute the `neighbors` of a block as the metadata. Alternatively, specify the sides `side₁`, `side₂`, ..., `sideₙ`. """ -struct BlockPartition{S} <: PartitionMethod - sides::S +struct BlockPartition{Dim,ℒ<:Len} <: PartitionMethod + sides::NTuple{Dim,ℒ} neighbors::Bool + BlockPartition(sides::NTuple{Dim,ℒ}, neighbors::Bool) where {Dim,ℒ<:Len} = new{Dim,float(ℒ)}(sides, neighbors) end -BlockPartition(sides; neighbors=false) = BlockPartition(sides, neighbors) +BlockPartition(sides::NTuple{Dim,Len}; neighbors=false) where {Dim} = BlockPartition(promote(sides...), neighbors) + +BlockPartition(sides::Tuple; neighbors=false) = BlockPartition(addunit.(sides, u"m"), neighbors) BlockPartition(sides...; neighbors=false) = BlockPartition(sides; neighbors=neighbors) diff --git a/src/partitioning/direction.jl b/src/partitioning/direction.jl index e21677ff4..347e46d1d 100644 --- a/src/partitioning/direction.jl +++ b/src/partitioning/direction.jl @@ -8,21 +8,21 @@ A method for partitioning spatial objects along a given `direction` with bandwidth tolerance `tol`. """ -struct DirectionPartition{Dim,T} <: SPredicatePartitionMethod - direction::Vec{Dim,T} - tol::Float64 - - function DirectionPartition{Dim,T}(direction, tol) where {Dim,T} - new(normalize(direction), tol) - end +struct DirectionPartition{V<:Vec,ℒ<:Len} <: SPredicatePartitionMethod + direction::V + tol::ℒ + DirectionPartition(direction::V, tol::ℒ) where {V<:Vec,ℒ<:Len} = new{V,float(ℒ)}(unormalize(direction), tol) end -DirectionPartition(direction::Vec{Dim,T}; tol=1e-6) where {Dim,T} = DirectionPartition{Dim,T}(direction, tol) +DirectionPartition(direction::Vec, tol) = DirectionPartition(direction, addunit(tol, u"m")) + +DirectionPartition(direction::Vec; tol=1e-6u"m") = DirectionPartition(direction, tol) -DirectionPartition(direction::NTuple{Dim,T}; tol=1e-6) where {Dim,T} = DirectionPartition(Vec(direction), tol=tol) +DirectionPartition(direction::Tuple; tol=1e-6u"m") = DirectionPartition(Vec(direction), tol) function (p::DirectionPartition)(x, y) δ = x - y d = p.direction - norm(δ - (δ ⋅ d) * d) < p.tol + k = ustrip.(δ ⋅ d) + norm(δ - k * d) < p.tol end diff --git a/src/partitioning/plane.jl b/src/partitioning/plane.jl index caf57d52b..2cee1aa27 100644 --- a/src/partitioning/plane.jl +++ b/src/partitioning/plane.jl @@ -9,17 +9,16 @@ A method for partitioning spatial objects into a family of hyperplanes defined by a `normal` direction. Two points `x` and `y` belong to the same hyperplane when `(x - y) ⋅ normal < tol`. """ -struct PlanePartition{Dim,T} <: SPredicatePartitionMethod - normal::Vec{Dim,T} - tol::Float64 - - function PlanePartition{Dim,T}(normal, tol) where {Dim,T} - new(normalize(normal), tol) - end +struct PlanePartition{V<:Vec,ℒ<:Len} <: SPredicatePartitionMethod + normal::V + tol::ℒ + PlanePartition(normal::V, tol::ℒ) where {V<:Vec,ℒ<:Len} = new{V,float(ℒ)}(unormalize(normal), tol) end -PlanePartition(normal::Vec{Dim,T}; tol=1e-6) where {Dim,T} = PlanePartition{Dim,T}(normal, tol) +PlanePartition(normal::Vec, tol) = PlanePartition(normal, addunit(tol, u"m")) + +PlanePartition(normal::Vec; tol=1e-6u"m") = PlanePartition(normal, tol) -PlanePartition(normal::NTuple{Dim,T}; tol=1e-6) where {Dim,T} = PlanePartition(Vec(normal), tol=tol) +PlanePartition(normal::Tuple; tol=1e-6u"m") = PlanePartition(Vec(normal), tol) -(p::PlanePartition)(x, y) = abs((x - y) ⋅ p.normal) < p.tol +(p::PlanePartition)(x, y) = abs((x - y) ⋅ p.normal) < p.tol^2 diff --git a/src/polytopes.jl b/src/polytopes.jl index 2c73b1d0c..3cad87fa9 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -3,12 +3,12 @@ # ------------------------------------------------------------------ """ - Polytope{K,Dim,T} + Polytope{K,Dim,P} We say that a geometry is a K-polytope when it is a collection of "flat" sides that constitute a `K`-dimensional subspace. They are called chain, polygon and polyhedron respectively for 1D (`K=1`), 2D (`K=2`) and 3D (`K=3`) subspaces, -embedded in a `Dim`-dimensional space. The parameter `K` is also known as the +embedded in a `Dim`-dimensional space with point type `P`. The parameter `K` is also known as the rank or parametric dimension of the polytope: . The term polytope expresses a particular combinatorial structure. A polyhedron, @@ -25,17 +25,17 @@ have (K-1)-polytopes in common. See . - Type aliases are `Chain`, `Polygon`, `Polyhedron`. """ -abstract type Polytope{K,Dim,T} <: Geometry{Dim,T} end +abstract type Polytope{K,Dim,P<:Point} <: Geometry{Dim} end # heper macro to define polytopes macro polytope(type, K, N) expr = quote - $Base.@__doc__ struct $type{Dim,T} <: Polytope{$K,Dim,T} - vertices::NTuple{$N,Point{Dim,T}} + $Base.@__doc__ struct $type{Dim,P<:Point{Dim}} <: Polytope{$K,Dim,P} + vertices::NTuple{$N,P} end $type(vertices::Vararg{Tuple,$N}) = $type(Point.(vertices)) - $type(vertices::Vararg{Point{Dim,T},$N}) where {Dim,T} = $type{Dim,T}(vertices) + $type(vertices::Vararg{P,$N}) where {P<:Point} = $type(vertices) end esc(expr) end @@ -45,7 +45,7 @@ end # ------------------- """ - Chain{Dim,T} + Chain{Dim,P} A chain is a 1-polytope, i.e. a polytope with parametric dimension 1. See . @@ -85,7 +85,7 @@ function Base.open(::Chain) end Remove duplicate vertices in the `chain`. Closed chains remain closed. """ -function Base.unique!(c::Chain{Dim,T}) where {Dim,T} +function Base.unique!(c::Chain) # sort vertices lexicographically verts = vertices(open(c)) perms = sortperm(coordinates.(verts)) @@ -94,7 +94,7 @@ function Base.unique!(c::Chain{Dim,T}) where {Dim,T} keep = Int[] sorted = @view verts[perms] for i in 1:(length(sorted) - 1) - if !isapprox(sorted[i], sorted[i + 1], atol=atol(T)) + if !isapprox(sorted[i], sorted[i + 1]) # save index in the original vector push!(keep, perms[i]) end @@ -152,7 +152,7 @@ include("polytopes/ring.jl") # --------------------- """ - Polygon{Dim,T} + Polygon{Dim,P} A polygon is a 2-polytope, i.e. a polytope with parametric dimension 2. @@ -176,7 +176,7 @@ include("polytopes/polyarea.jl") # ------------------------ """ - Polyhedron{Dim,T} + Polyhedron{Dim,P} A polyhedron is a 3-polytope, i.e. a polytope with parametric dimension 3. @@ -200,6 +200,8 @@ Return the parametric dimension or rank of the polytope. """ paramdim(::Type{<:Polytope{K}}) where {K} = K +lentype(::Type{<:Polytope{K,Dim,P}}) where {K,Dim,P} = lentype(P) + """ vertex(polytope, ind) @@ -235,9 +237,6 @@ Return a new `polytope` without duplicate vertices. """ Base.unique(p::Polytope) = unique!(deepcopy(p)) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{PL}) where {PL<:Polytope} = - PL(ntuple(i -> rand(rng, Point{embeddim(PL),coordtype(PL)}), nvertices(PL))) - # ----------- # IO METHODS # ----------- diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index f610ce685..adf090700 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -30,3 +30,6 @@ function (h::Hexahedron)(u, v, w) u * v * w * A8 ) end + +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Hexahedron{Dim}}) where {Dim} = + Hexahedron(ntuple(i -> rand(rng, Point{Dim}), 8)) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 8daeb789f..6eebd69fb 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -20,9 +20,9 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. - Type aliases are `Triangle`, `Quadrangle`, `Pentagon`, `Hexagon`, `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ -struct Ngon{N,Dim,T} <: Polygon{Dim,T} - vertices::NTuple{N,Point{Dim,T}} - function Ngon{N,Dim,T}(vertices::NTuple{N,Point{Dim,T}}) where {N,Dim,T} +struct Ngon{N,Dim,P<:Point{Dim}} <: Polygon{Dim,P} + vertices::NTuple{N,P} + function Ngon{N,Dim,P}(vertices) where {N,Dim,P<:Point{Dim}} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) end @@ -30,12 +30,12 @@ struct Ngon{N,Dim,T} <: Polygon{Dim,T} end end -Ngon{N}(vertices::NTuple{N,Point{Dim,T}}) where {N,Dim,T} = Ngon{N,Dim,T}(vertices) -Ngon{N}(vertices::Vararg{Point{Dim,T},N}) where {N,Dim,T} = Ngon{N}(vertices) +Ngon{N}(vertices::NTuple{N,P}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) +Ngon{N}(vertices::Vararg{P,N}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,Point{Dim,T}}) where {N,Dim,T} = Ngon{N,Dim,T}(vertices) -Ngon(vertices::Point{Dim,T}...) where {Dim,T} = Ngon(vertices) +Ngon(vertices::NTuple{N,P}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) +Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) # type aliases for convenience @@ -48,6 +48,8 @@ const Octagon = Ngon{8} const Nonagon = Ngon{9} const Decagon = Ngon{10} +lentype(::Type{<:Ngon{N,Dim,P}}) where {N,Dim,P} = lentype(P) + Base.unique!(ngon::Ngon) = ngon nvertices(::Type{<:Ngon{N}}) where {N} = N @@ -65,6 +67,9 @@ innerangles(ngon::Ngon) = innerangles(boundary(ngon)) signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ngon{N,Dim}}) where {N,Dim} = + Ngon{N}(ntuple(i -> rand(rng, Point{Dim}), N)) + # ---------- # TRIANGLES # ---------- @@ -78,7 +83,7 @@ signarea(::Triangle{3}) = error("signed area only defined for triangles embedded function normal(t::Triangle{3}) A, B, C = t.vertices - ((B - A) × (C - A)) / 2 + ucross((B - A), (C - A)) / 2 end function (t::Triangle)(u, v) diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index cdf25fa72..8b119f343 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -24,10 +24,10 @@ in the real world, including issues with: * `degeneracy` - Sometimes data is shared with degenerate rings (e.g. only 2 vertices). """ -struct PolyArea{Dim,T,R<:Ring{Dim,T}} <: Polygon{Dim,T} +struct PolyArea{Dim,P<:Point{Dim},R<:Ring{Dim,P}} <: Polygon{Dim,P} rings::Vector{R} - function PolyArea{Dim,T,R}(rings; fix=true) where {Dim,T,R<:Ring{Dim,T}} + function PolyArea{Dim,P,R}(rings; fix=true) where {Dim,P<:Point{Dim},R<:Ring{Dim,P}} if isempty(rings) throw(ArgumentError("cannot create PolyArea without rings")) end @@ -57,7 +57,7 @@ struct PolyArea{Dim,T,R<:Ring{Dim,T}} <: Polygon{Dim,T} end end -PolyArea(rings::AbstractVector{R}; fix=true) where {Dim,T,R<:Ring{Dim,T}} = PolyArea{Dim,T,R}(rings; fix) +PolyArea(rings::AbstractVector{R}; fix=true) where {Dim,P<:Point{Dim},R<:Ring{Dim,P}} = PolyArea{Dim,P,R}(rings; fix) PolyArea(vertices::AbstractVector{<:AbstractVector}; fix=true) = PolyArea([Ring(v) for v in vertices]; fix) @@ -67,6 +67,8 @@ PolyArea(outer::AbstractVector; fix=true) = PolyArea(Ring(outer); fix) PolyArea(outer...; fix=true) = PolyArea(collect(outer); fix) +lentype(::Type{<:PolyArea{Dim,R}}) where {Dim,R} = lentype(R) + ==(p₁::PolyArea, p₂::PolyArea) = p₁.rings == p₂.rings function Base.isapprox(p₁::PolyArea, p₂::PolyArea; kwargs...) @@ -103,9 +105,10 @@ function Base.show(io::IO, p::PolyArea) print(io, ")") end -function Base.show(io::IO, ::MIME"text/plain", p::PolyArea{Dim,T}) where {Dim,T} +function Base.show(io::IO, ::MIME"text/plain", p::PolyArea) rings = p.rings - println(io, "PolyArea{$Dim,$T}") + summary(io, p) + println(io) println(io, " outer") print(io, " └─ $(rings[1])") if length(rings) > 1 @@ -115,5 +118,4 @@ function Base.show(io::IO, ::MIME"text/plain", p::PolyArea{Dim,T}) where {Dim,T} end end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:PolyArea{Dim,T}}) where {Dim,T} = - PolyArea(rand(rng, Ring{Dim,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:PolyArea{Dim}}) where {Dim} = PolyArea(rand(rng, Ring{Dim})) diff --git a/src/polytopes/pyramid.jl b/src/polytopes/pyramid.jl index 02bc2e62d..b10ecae53 100644 --- a/src/polytopes/pyramid.jl +++ b/src/polytopes/pyramid.jl @@ -13,3 +13,6 @@ nvertices(::Type{<:Pyramid}) = 5 Base.isapprox(p₁::Pyramid, p₂::Pyramid; kwargs...) = all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) + +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Pyramid{Dim}}) where {Dim} = + Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 30f25a89b..cf84c9369 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -9,10 +9,10 @@ A closed polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Rope`](@ref). """ -struct Ring{Dim,T,V<:CircularVector{Point{Dim,T}}} <: Chain{Dim,T} +struct Ring{Dim,P<:Point{Dim},V<:CircularVector{P}} <: Chain{Dim,P} vertices::V - function Ring{Dim,T,V}(vertices) where {Dim,T,V} + function Ring{Dim,P,V}(vertices) where {Dim,P<:Point{Dim},V<:CircularVector{P}} if first(vertices) == last(vertices) && length(vertices) ≥ 2 throw(ArgumentError(""" First and last vertices of `Ring` constructor must be different @@ -24,11 +24,11 @@ struct Ring{Dim,T,V<:CircularVector{Point{Dim,T}}} <: Chain{Dim,T} end end -Ring(vertices::CircularVector{Point{Dim,T}}) where {Dim,T} = Ring{Dim,T,typeof(vertices)}(vertices) +Ring(vertices::CircularVector{P}) where {Dim,P<:Point{Dim}} = Ring{Dim,P,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) -Ring(vertices::Point{Dim,T}...) where {Dim,T} = Ring(collect(vertices)) +Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) -Ring(vertices::AbstractVector{Point{Dim,T}}) where {Dim,T} = Ring(CircularVector(vertices)) +Ring(vertices::AbstractVector{<:Point}) = Ring(CircularVector(vertices)) nvertices(r::Ring) = length(r.vertices) @@ -47,10 +47,10 @@ Base.open(r::Ring) = open(Rope(parent(r.vertices))) # do not change which vertex comes first for closed chains Base.reverse!(r::Ring) = (reverse!(@view r.vertices[(begin + 1):end]); r) -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Ring{Dim,T}}) where {Dim,T} - v = rand(rng, Point{Dim,T}, rand(3:50)) +function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Ring{Dim}}) where {Dim} + v = [rand(rng, Point{Dim}) for _ in 1:rand(rng, 3:50)] while first(v) == last(v) - v = rand(rng, Point{Dim,T}, rand(3:50)) + v = [rand(rng, Point{Dim}) for _ in 1:rand(rng, 3:50)] end Ring(v) end @@ -62,10 +62,10 @@ Return inner angles of the `ring`. Inner angles are always positive, and unlike `angles` they can be greater than `π`. """ -function innerangles(r::Ring{2,T}) where {T} +function innerangles(r::Ring{2}) # correct sign of angles in case orientation is CW θs = orientation(r) == CW ? -angles(r) : angles(r) - [θ > 0 ? 2 * T(π) - θ : -θ for θ in θs] + [θ > 0 ? 2 * oftype(θ, π) - θ : -θ for θ in θs] end innerangles(r::Ring{3}) = innerangles(Ring(proj2D(vertices(r)))) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index 2701922a3..603328166 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -9,12 +9,12 @@ An open polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Ring`](@ref). """ -struct Rope{Dim,T,V<:AbstractVector{Point{Dim,T}}} <: Chain{Dim,T} +struct Rope{Dim,P<:Point{Dim},V<:AbstractVector{P}} <: Chain{Dim,P} vertices::V end Rope(vertices::Tuple...) = Rope([Point(v) for v in vertices]) -Rope(vertices::Point{Dim,T}...) where {Dim,T} = Rope(collect(vertices)) +Rope(vertices::P...) where {P<:Point} = Rope(collect(vertices)) Rope(vertices::AbstractVector{<:Tuple}) = Rope(Point.(vertices)) nvertices(r::Rope) = length(r.vertices) @@ -32,5 +32,5 @@ Base.open(r::Rope) = r Base.reverse!(r::Rope) = (reverse!(r.vertices); r) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Rope{Dim,T}}) where {Dim,T} = - Rope(rand(rng, Point{Dim,T}, rand(2:50))) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Rope{Dim}}) where {Dim} = + Rope([rand(rng, Point{Dim}) for _ in 1:rand(rng, 2:50)]) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index 1a7b50c4d..0e683ccad 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -36,3 +36,6 @@ function (s::Segment)(t) a, b = s.vertices a + t * (b - a) end + +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Segment{Dim}}) where {Dim} = + Segment(ntuple(i -> rand(rng, Point{Dim}), 2)) diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index ab6df655e..29d042949 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -22,3 +22,6 @@ function (t::Tetrahedron)(u, v, w) v₁, v₂, v₃, v₄ = coordinates.(t.vertices) Point(v₁ * z + v₂ * u + v₃ * v + v₄ * w) end + +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Tetrahedron{Dim}}) where {Dim} = + Tetrahedron(ntuple(i -> rand(rng, Point{Dim}), 4)) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 580613f86..753860b91 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -11,15 +11,15 @@ Base.in(p::Point, g::Geometry) = sideof(p, boundary(g)) == IN Base.in(p₁::Point, p₂::Point) = p₁ == p₂ -function Base.in(p::Point{Dim,T}, s::Segment{Dim,T}) where {Dim,T} +function Base.in(p::Point{Dim}, s::Segment{Dim}) where {Dim} # given collinear points (a, b, p), the point p intersects # segment ab if and only if vectors satisfy 0 ≤ ap ⋅ ab ≤ ||ab||² a, b = vertices(s) ab, ap = b - a, p - a - iscollinear(a, b, p) && zero(T) ≤ ab ⋅ ap ≤ ab ⋅ ab + iscollinear(a, b, p) && zero(lentype(p))^2 ≤ ab ⋅ ap ≤ ab ⋅ ab end -Base.in(p::Point, r::Ray) = p ∈ Line(r(0), r(1)) && (p - r(0)) ⋅ (r(1) - r(0)) ≥ 0 +Base.in(p::Point, r::Ray) = p ∈ Line(r(0), r(1)) && (p - r(0)) ⋅ (r(1) - r(0)) ≥ zero(lentype(p))^2 function Base.in(p::Point, l::Line) w = norm(l(1) - l(0)) @@ -29,65 +29,68 @@ end Base.in(p::Point, c::Chain) = any(s -> p ∈ s, segments(c)) -Base.in(p::Point{3,T}, pl::Plane{T}) where {T} = isapprox(normal(pl) ⋅ (p - pl(0, 0)), zero(T), atol=atol(T)) +Base.in(p::Point{3}, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) Base.in(p::Point, b::Box) = minimum(b) ⪯ p ⪯ maximum(b) -function Base.in(p::Point{Dim,T}, b::Ball{Dim,T}) where {Dim,T} +function Base.in(p::Point{Dim}, b::Ball{Dim}) where {Dim} c = center(b) r = radius(b) s = norm(p - c) - s < r || isapprox(s, r, atol=atol(T)) + s < r || isapproxequal(s, r) end -function Base.in(p::Point{Dim,T}, s::Sphere{Dim,T}) where {Dim,T} +function Base.in(p::Point{Dim}, s::Sphere{Dim}) where {Dim} c = center(s) r = radius(s) s = norm(p - c) - isapprox(s, r, atol=atol(T)) + isapproxequal(s, r) end -function Base.in(p::Point{3,T}, d::Disk{T}) where {T} +function Base.in(p::Point{3}, d::Disk) p ∉ plane(d) && return false c = center(d) r = radius(d) s = norm(p - c) - s < r || isapprox(s, r, atol=atol(T)) + s < r || isapproxequal(s, r) end -function Base.in(p::Point{3,T}, c::Circle{T}) where {T} +function Base.in(p::Point{3}, c::Circle) p ∉ plane(c) && return false o = center(c) r = radius(c) s = norm(p - o) - isapprox(s, r, atol=atol(T)) + isapproxequal(s, r) end function Base.in(p::Point{3}, c::Cone) + z = zero(lentype(p))^2 a = apex(c) b = center(base(c)) ax = a - b - (a - p) ⋅ ax ≥ 0 || return false - (b - p) ⋅ ax ≤ 0 || return false + (a - p) ⋅ ax ≥ z || return false + (b - p) ⋅ ax ≤ z || return false ∠(b, a, p) ≤ halfangle(c) end function Base.in(p::Point{3}, c::Cylinder) + z = zero(lentype(p))^2 b = bottom(c)(0, 0) t = top(c)(0, 0) r = radius(c) a = t - b - (p - b) ⋅ a ≥ 0 || return false - (p - t) ⋅ a ≤ 0 || return false + (p - b) ⋅ a ≥ z || return false + (p - t) ⋅ a ≤ z || return false norm((p - b) × a) / norm(a) ≤ r end function Base.in(p::Point{3}, f::Frustum) + z = zero(lentype(p))^2 t = center(top(f)) b = center(bottom(f)) ax = b - t - (p - t) ⋅ ax ≥ 0 || return false - (p - b) ⋅ ax ≤ 0 || return false + (p - t) ⋅ ax ≥ z || return false + (p - b) ⋅ ax ≤ z || return false # axial distance of p ad = (p - t) ⋅ normalize(ax) adrel = ad / norm(ax) @@ -100,10 +103,11 @@ function Base.in(p::Point{3}, f::Frustum) rd ≤ r end -function Base.in(p::Point{3,T}, t::Torus{T}) where {T} +function Base.in(p::Point{3}, t::Torus) + ℒ = lentype(p) R, r = radii(t) c, n = center(t), normal(t) - Q = rotation_between(n, Vec{3,T}(0, 0, 1)) + Q = urotbetween(n, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) x, y, z = Q * (p - c) (R - √(x^2 + y^2))^2 + z^2 ≤ r^2 end diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 791ccfbdd..a7dd283de 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -65,7 +65,9 @@ intersects(c::Chain, g::Geometry) = any(∈(g), vertices(c)) || intersects(c, bo intersects(g::Geometry, c::Chain) = intersects(c, g) -function intersects(g₁::Geometry{Dim,T}, g₂::Geometry{Dim,T}) where {Dim,T} +function intersects(g₁::Geometry{Dim}, g₂::Geometry{Dim}) where {Dim} + ℒ = lentype(g₁) + # must have intersection of bounding boxes intersects(boundingbox(g₁), boundingbox(g₂)) || return false @@ -80,13 +82,13 @@ function intersects(g₁::Geometry{Dim,T}, g₂::Geometry{Dim,T}) where {Dim,T} # initial direction c₁, c₂ = centroid(g₁), centroid(g₂) - d = c₁ ≈ c₂ ? rand(Vec{Dim,T}) : c₂ - c₁ + d = c₁ ≈ c₂ ? rand(Vec{Dim,ℒ}) : c₂ - c₁ # first point in Minkowski difference P = minkowskipoint(g₁, g₂, d) # origin of coordinate system - O = minkowskiorigin(Dim, T) + O = minkowskiorigin(Dim, ℒ) # initialize simplex vertices points = [P] @@ -95,7 +97,7 @@ function intersects(g₁::Geometry{Dim,T}, g₂::Geometry{Dim,T}) where {Dim,T} d = O - P while true P = minkowskipoint(g₁, g₂, d) - if (P - O) ⋅ d < zero(T) + if (P - O) ⋅ d < zero(ℒ)^2 return false end push!(points, P) @@ -121,25 +123,26 @@ See also [`intersects`](@ref). """ function gjk! end -function gjk!(O::Point{2,T}, points) where {T} +function gjk!(O::Point{2}, points) + ℒ = lentype(O) # line segment case if length(points) == 2 B, A = points AB = B - A AO = O - A - d = perpendicular(AB, AO) + d = perphint(AB, AO) else # triangle simplex case C, B, A = points AB = B - A AC = C - A AO = O - A - ABᵀ = -perpendicular(AB, AC) - ACᵀ = -perpendicular(AC, AB) - if ABᵀ ⋅ AO > zero(T) + ABᵀ = -perphint(AB, AC) + ACᵀ = -perphint(AC, AB) + if ABᵀ ⋅ AO > zero(ℒ)^2 popat!(points, 1) # pop C d = ABᵀ - elseif ACᵀ ⋅ AO > zero(T) + elseif ACᵀ ⋅ AO > zero(ℒ)^2 popat!(points, 2) # pop B d = ACᵀ else @@ -149,21 +152,22 @@ function gjk!(O::Point{2,T}, points) where {T} d end -function gjk!(O::Point{3,T}, points) where {T} +function gjk!(O::Point{3}, points) + ℒ = lentype(O) # line segment case if length(points) == 2 B, A = points AB = B - A AO = O - A - d = perpendicular(AB, AO) + d = perphint(AB, AO) elseif length(points) == 3 # triangle case C, B, A = points AB = B - A AC = C - A AO = O - A - ABCᵀ = AB × AC - if ABCᵀ ⋅ AO < 0 + ABCᵀ = ucross(AB, AC) + if ABCᵀ ⋅ AO < zero(ℒ)^2 points[1], points[2] = points[2], points[1] ABCᵀ = -ABCᵀ end @@ -185,16 +189,16 @@ function gjk!(O::Point{3,T}, points) where {T} AC = C - A AD = D - A AO = O - A - ABCᵀ = AB × AC - ADBᵀ = AD × AB - ACDᵀ = AC × AD - if ABCᵀ ⋅ AO > zero(T) + ABCᵀ = ucross(AB, AC) + ADBᵀ = ucross(AD, AB) + ACDᵀ = ucross(AC, AD) + if ABCᵀ ⋅ AO > zero(ℒ)^2 popat!(points, 1) # pop D d = ABCᵀ - elseif ADBᵀ ⋅ AO > zero(T) + elseif ADBᵀ ⋅ AO > zero(ℒ)^2 popat!(points, 2) # pop C d = ADBᵀ - elseif ACDᵀ ⋅ AO > zero(T) + elseif ACDᵀ ⋅ AO > zero(ℒ)^2 popat!(points, 3) # pop B d = ACDᵀ else @@ -246,16 +250,16 @@ intersects(m::Multi, c::Chain) = intersects(c, m) minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(supportfun(g₁, d) - supportfun(g₂, -d)) # origin of coordinate system -minkowskiorigin(Dim, T) = Point(ntuple(i -> zero(T), Dim)) +minkowskiorigin(Dim, ℒ) = Point(ntuple(i -> zero(ℒ), Dim)) # find a vector perpendicular to `v` using vector `d` as some direction hint -# expect that `perpendicular(v, d) ⋅ d ≥ 0` or, in other words, +# expect that `perphint(v, d) ⋅ d ≥ 0` or, in other words, # that the angle between the result vector and `d` is less or equal than 90º -function perpendicular(v::Vec{2,T}, d::Vec{2,T}) where {T} - a = Vec(v[1], v[2], zero(T)) - b = Vec(d[1], d[2], zero(T)) - r = a × b × a +function perphint(v::Vec{2,ℒ}, d::Vec{2,ℒ}) where {ℒ} + a = Vec(v[1], v[2], zero(ℒ)) + b = Vec(d[1], d[2], zero(ℒ)) + r = ucross(a, b, a) Vec(r[1], r[2]) end -perpendicular(v::Vec{3}, d::Vec{3}) = v × d × v +perphint(v::Vec{3,ℒ}, d::Vec{3,ℒ}) where {ℒ} = ucross(v, d, v) diff --git a/src/predicates/iscollinear.jl b/src/predicates/iscollinear.jl index 5ac7ed381..210522b4f 100644 --- a/src/predicates/iscollinear.jl +++ b/src/predicates/iscollinear.jl @@ -7,7 +7,7 @@ Tells whether or not the points `A`, `B` and `C` are collinear. """ -function iscollinear(A::Point{Dim,T}, B::Point{Dim,T}, C::Point{Dim,T}) where {Dim,T} +function iscollinear(A::Point{Dim}, B::Point{Dim}, C::Point{Dim}) where {Dim} # points A, B, C are collinear if and only if the # cross-products for segments AB and AC with respect # to all possible pairs of coordinates are zero @@ -16,7 +16,7 @@ function iscollinear(A::Point{Dim,T}, B::Point{Dim,T}, C::Point{Dim,T}) where {D for i in 1:Dim, j in (i + 1):Dim u = Vec(AB[i], AB[j]) v = Vec(AC[i], AC[j]) - if !isapprox(u × v, zero(T), atol=atol(T)^2) + if !isapproxzero(u × v) result = false break end diff --git a/src/predicates/isconvex.jl b/src/predicates/isconvex.jl index 7a5bd82ac..75f14eb5a 100644 --- a/src/predicates/isconvex.jl +++ b/src/predicates/isconvex.jl @@ -60,7 +60,7 @@ isconvex(::Tetrahedron) = true isconvex(p::Polygon{2}) = Set(vertices(convexhull(p))) == Set(vertices(p)) -isconvex(m::Multi{Dim,T}) where {Dim,T} = isapprox(measure(convexhull(m)), measure(m), atol=atol(T)) +isconvex(m::Multi) = isapproxequal(measure(convexhull(m)), measure(m)) # -------------- # OPTIMIZATIONS diff --git a/src/predicates/iscoplanar.jl b/src/predicates/iscoplanar.jl index 5c9d36777..85dc93339 100644 --- a/src/predicates/iscoplanar.jl +++ b/src/predicates/iscoplanar.jl @@ -7,7 +7,4 @@ Tells whether or not the points `A`, `B`, `C` and `D` are coplanar. """ -function iscoplanar(A::Point{3,T}, B::Point{3,T}, C::Point{3,T}, D::Point{3,T}) where {T} - vol = volume(Tetrahedron(A, B, C, D)) - isapprox(vol, zero(T), atol=atol(T)) -end +iscoplanar(A::Point{3}, B::Point{3}, C::Point{3}, D::Point{3}) = isapproxzero(volume(Tetrahedron(A, B, C, D))) diff --git a/src/primitives.jl b/src/primitives.jl index ee8d1eae3..a0658071c 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -3,19 +3,20 @@ # ------------------------------------------------------------------ """ - Primitive{Dim,T} + Primitive{Dim} We say that a geometry is a primitive when it can be expressed as a single entity with no parts (a.k.a. atomic). For example, a sphere is a primitive described in terms of a mathematical expression involving a metric and a radius. See . """ -abstract type Primitive{Dim,T} <: Geometry{Dim,T} end +abstract type Primitive{Dim} <: Geometry{Dim} end function Base.show(io::IO, geom::Primitive) name = prettyname(geom) + ioctx = IOContext(io, :compact => true) print(io, "$name(") - printfields(io, geom, compact=true) + printfields(ioctx, geom, singleline=true) print(io, ")") end diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index fb6a87e1f..9fe6d7e68 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -9,26 +9,32 @@ A ball with `center` and `radius`. See also [`Sphere`](@ref). """ -struct Ball{Dim,T} <: Primitive{Dim,T} - center::Point{Dim,T} - radius::T +struct Ball{Dim,P<:Point{Dim},ℒ<:Len} <: Primitive{Dim} + center::P + radius::ℒ + Ball{Dim,P,ℒ}(center, radius) where {Dim,P<:Point{Dim},ℒ<:Len} = new(center, radius) end -Ball(center::Point{Dim,T}, radius) where {Dim,T} = Ball(center, T(radius)) +Ball(center::P, radius::ℒ) where {Dim,P<:Point{Dim},ℒ<:Len} = Ball{Dim,P,float(ℒ)}(center, radius) + +Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) Ball(center::Tuple, radius) = Ball(Point(center), radius) -Ball(center::Point{Dim,T}) where {Dim,T} = Ball(center, T(1)) +Ball(center::Point) = Ball(center, oneunit(lentype(center))) Ball(center::Tuple) = Ball(Point(center)) paramdim(::Type{<:Ball{Dim}}) where {Dim} = Dim +lentype(::Type{<:Ball{Dim,P}}) where {Dim,P} = lentype(P) + center(b::Ball) = b.center radius(b::Ball) = b.radius -function (b::Ball{2,T})(ρ, φ) where {T} +function (b::Ball{2})(ρ, φ) + T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, φ), "b(ρ, φ) is not defined for ρ, φ outside [0, 1]².")) end @@ -41,7 +47,8 @@ function (b::Ball{2,T})(ρ, φ) where {T} c + Vec(x, y) end -function (b::Ball{3,T})(ρ, θ, φ) where {T} +function (b::Ball{3})(ρ, θ, φ) + T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, θ, φ), "b(ρ, θ, φ) is not defined for ρ, θ, φ outside [0, 1]³.")) end @@ -56,5 +63,5 @@ function (b::Ball{3,T})(ρ, θ, φ) where {T} c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ball{Dim,T}}) where {Dim,T} = - Ball(rand(rng, Point{Dim,T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ball{Dim}}) where {Dim} = + Ball(rand(rng, Point{Dim}), rand(rng, Met{Float64})) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 6cc305570..221c22bd4 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -20,15 +20,17 @@ large number of points but less precise, can be used via BezierCurve(Point2[(0.,0.),(1.,-1.)]) ``` """ -struct BezierCurve{Dim,T,V<:AbstractVector{Point{Dim,T}}} <: Primitive{Dim,T} +struct BezierCurve{Dim,V<:AbstractVector{<:Point{Dim}}} <: Primitive{Dim} controls::V end BezierCurve(points::AbstractVector{<:Tuple}) = BezierCurve(Point.(points)) -BezierCurve(points::Vararg) = BezierCurve(collect(points)) +BezierCurve(points...) = BezierCurve(collect(points)) paramdim(::Type{<:BezierCurve}) = 1 +lentype(::Type{<:BezierCurve{Dim,V}}) where {Dim,V} = lentype(eltype(V)) + controls(b::BezierCurve) = b.controls ncontrols(b::BezierCurve) = length(b.controls) @@ -79,7 +81,8 @@ end # curve, aᵢ = binomial(n, i) * pᵢ * t̄ⁿ⁻ⁱ and t̄ = (1 - t). # Horner's rule recursively reconstructs B from a sequence bᵢ # with bₙ = aₙ and bᵢ₋₁ = aᵢ₋₁ + bᵢ * t until b₀ = B. -function (curve::BezierCurve{Dim,T})(t, ::Horner) where {Dim,T} +function (curve::BezierCurve)(t, ::Horner) + T = numtype(lentype(curve)) if t < 0 || t > 1 throw(DomainError(t, "b(t) is not defined for t outside [0, 1].")) end @@ -105,5 +108,24 @@ function (curve::BezierCurve{Dim,T})(t, ::Horner) where {Dim,T} Point(b₀) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{BezierCurve{Dim,T}}) where {Dim,T} = - BezierCurve(rand(rng, Point{Dim,T}, 5)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{BezierCurve{Dim}}) where {Dim} = + BezierCurve([rand(rng, Point{Dim}) for _ in 1:5]) + +# ----------- +# IO METHODS +# ----------- + +function Base.show(io::IO, b::BezierCurve) + ioctx = IOContext(io, :compact => true) + print(io, "BezierCurve(controls: [") + join(ioctx, b.controls, ", ") + print(io, "])") +end + +function Base.show(io::IO, ::MIME"text/plain", b::BezierCurve) + summary(io, b) + println(io) + print(io, "└─ controls: [") + join(io, b.controls, ", ") + print(io, "]") +end diff --git a/src/primitives/box.jl b/src/primitives/box.jl index da039b42a..b40c5d105 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -15,22 +15,24 @@ Box(Point(0, 0, 0), Point(1, 1, 1)) Box((0, 0), (1, 1)) ``` """ -struct Box{Dim,T} <: Primitive{Dim,T} - min::Point{Dim,T} - max::Point{Dim,T} +struct Box{Dim,P<:Point{Dim}} <: Primitive{Dim} + min::P + max::P - function Box{Dim,T}(min, max) where {Dim,T} + function Box{Dim,P}(min, max) where {Dim,P<:Point{Dim}} @assert min ⪯ max "`min` must be less than or equal to `max`" new(min, max) end end -Box(min::Point{Dim,T}, max::Point{Dim,T}) where {Dim,T} = Box{Dim,T}(min, max) +Box(min::P, max::P) where {Dim,P<:Point{Dim}} = Box{Dim,P}(min, max) Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) paramdim(::Type{<:Box{Dim}}) where {Dim} = Dim +lentype(::Type{<:Box{Dim,P}}) where {Dim,P} = lentype(P) + Base.minimum(b::Box) = b.min Base.maximum(b::Box) = b.max @@ -45,15 +47,15 @@ sides(b::Box) = Tuple(b.max - b.min) Base.isapprox(b₁::Box, b₂::Box) = b₁.min ≈ b₂.min && b₁.max ≈ b₂.max -function (b::Box{Dim,T})(uv...) where {Dim,T} - if !all(x -> zero(T) ≤ x ≤ one(T), uv) +function (b::Box)(uv...) + if !all(x -> 0 ≤ x ≤ 1, uv) throw(DomainError(uv, "b(u, v, ...) is not defined for u, v, ... outside [0, 1]ⁿ.")) end b.min + uv .* (b.max - b.min) end -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Box{Dim,T}}) where {Dim,T} - min = rand(rng, Point{Dim,T}) - max = min + rand(rng, Vec{Dim,T}) +function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Box{Dim}}) where {Dim} + min = rand(rng, Point{Dim}) + max = min + rand(rng, Vec{Dim,Met{Float64}}) Box(min, max) end diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 8dd91ef63..50c5ca311 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -10,11 +10,16 @@ given `plane` with given `radius`. See also [`Disk`](@ref). """ -struct Circle{T} <: Primitive{3,T} - plane::Plane{T} - radius::T +struct Circle{P<:Plane,ℒ<:Len} <: Primitive{3} + plane::P + radius::ℒ + Circle{P,ℒ}(plane, radius) where {P<:Plane,ℒ<:Len} = new(plane, radius) end +Circle(plane::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Circle{P,float(ℒ)}(plane, radius) + +Circle(plane::Plane, radius) = Circle(plane, addunit(radius, u"m")) + """ Circle(p1, p2, p3) @@ -29,34 +34,34 @@ function Circle(p1::Point{3}, p2::Point{3}, p3::Point{3}) F = coordinates(p1) ⋅ n⃗ M = transpose([n⃗ v12 v13]) u = [F, m12 ⋅ v12, m13 ⋅ v13] - O = Point(inv(M) * u) + O = Point(Vec(uinv(M) * u)) r = norm(p1 - O) Circle(Plane(O, n⃗), r) end Circle(p1::Tuple, p2::Tuple, p3::Tuple) = Circle(Point(p1), Point(p2), Point(p3)) -Circle(plane::Plane{T}, radius) where {T} = Circle(plane, T(radius)) - paramdim(::Type{<:Circle}) = 1 +lentype(::Type{<:Circle{P}}) where {P} = lentype(P) + plane(c::Circle) = c.plane center(c::Circle) = c.plane(0, 0) radius(c::Circle) = c.radius -function (c::Circle{T})(φ) where {T} +function (c::Circle)(φ) + T = numtype(lentype(c)) if (φ < 0 || φ > 1) throw(DomainError(φ, "c(φ) is not defined for φ outside [0, 1].")) end r = c.radius - l = T(r) + l = r sφ, cφ = sincospi(2 * T(φ)) - u = l * cφ - v = l * sφ + u = ustrip(l * cφ) + v = ustrip(l * sφ) c.plane(u, v) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Circle{T}}) where {T} = - Circle(rand(rng, Plane{T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Circle}) = Circle(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 21cd250a5..06df40c42 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -10,15 +10,17 @@ See . See also [`ConeSurface`](@ref). """ -struct Cone{T} <: Primitive{3,T} - base::Disk{T} - apex::Point{3,T} +struct Cone{D<:Disk,P<:Point{3}} <: Primitive{3} + base::D + apex::P end Cone(base::Disk, apex::Tuple) = Cone(base, Point(apex)) paramdim(::Type{<:Cone}) = 3 +lentype(::Type{<:Cone{D}}) where {D} = lentype(D) + base(c::Cone) = c.base apex(c::Cone) = c.apex @@ -27,5 +29,4 @@ height(c::Cone) = norm(center(base(c)) - apex(c)) halfangle(c::Cone) = atan(radius(base(c)), height(c)) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cone{T}}) where {T} = - Cone(rand(rng, Disk{T}), rand(rng, Point{3,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cone}) = Cone(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 6d5d6845f..0a2258b64 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -10,20 +10,23 @@ See . See also [`Cone`](@ref). """ -struct ConeSurface{T} <: Primitive{3,T} - base::Disk{T} - apex::Point{3,T} +struct ConeSurface{D<:Disk,P<:Point{3}} <: Primitive{3} + base::D + apex::P end ConeSurface(base::Disk, apex::Tuple) = ConeSurface(base, Point(apex)) paramdim(::Type{<:ConeSurface}) = 2 +lentype(::Type{<:ConeSurface{D}}) where {D} = lentype(D) + base(c::ConeSurface) = c.base apex(c::ConeSurface) = c.apex -function (c::ConeSurface{T})(φ, h) where {T} +function (c::ConeSurface)(φ, h) + T = numtype(lentype(c)) if (φ < 0 || φ > 1) || (h < 0 || h > 1) throw(DomainError((φ, h), "c(φ, h) is not defined for φ, h outside [0, 1]².")) end @@ -37,5 +40,5 @@ function (c::ConeSurface{T})(φ, h) where {T} s(T(φ)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ConeSurface{T}}) where {T} = - ConeSurface(rand(rng, Disk{T}), rand(rng, Point{3,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ConeSurface}) = + ConeSurface(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index deb2f5aff..a75237be3 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -24,29 +24,40 @@ Finally, construct a right vertical circular cylinder with given `radius`. See . """ -struct Cylinder{T} <: Primitive{3,T} - bot::Plane{T} - top::Plane{T} - radius::T +struct Cylinder{P<:Plane,ℒ<:Len} <: Primitive{3} + bot::P + top::P + radius::ℒ + Cylinder{P,ℒ}(bot, top, radius) where {P<:Plane,ℒ<:Len} = new(bot, top, radius) end -function Cylinder(start::Point{3,T}, finish::Point{3,T}, radius) where {T} +Cylinder(bot::P, top::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Cylinder{P,float(ℒ)}(bot, top, radius) + +Cylinder(bot::P, top::P, radius) where {P<:Plane} = Cylinder(bot, top, addunit(radius, u"m")) + +function Cylinder(start::Point{3}, finish::Point{3}, radius) dir = finish - start bot = Plane(start, dir) top = Plane(finish, dir) - Cylinder(bot, top, T(radius)) + Cylinder(bot, top, radius) end Cylinder(start::Tuple, finish::Tuple, radius) = Cylinder(Point(start), Point(finish), radius) -Cylinder(start::Point{3,T}, finish::Point{3,T}) where {T} = Cylinder(start, finish, T(1)) +Cylinder(start::Point{3}, finish::Point{3}) = Cylinder(start, finish, oneunit(lentype(start))) Cylinder(start::Tuple, finish::Tuple) = Cylinder(Point(start), Point(finish)) -Cylinder(radius::T) where {T} = Cylinder(Point(T(0), T(0), T(0)), Point(T(0), T(0), T(1)), radius) +function Cylinder(radius) + z = zero(radius) + o = oneunit(radius) + Cylinder(Point(z, z, z), Point(z, z, o), radius) +end paramdim(::Type{<:Cylinder}) = 3 +lentype(::Type{<:Cylinder{P}}) where {P} = lentype(P) + radius(c::Cylinder) = c.radius bottom(c::Cylinder) = c.bot @@ -63,7 +74,9 @@ hasintersectingplanes(c::Cylinder) = hasintersectingplanes(boundary(c)) Base.isapprox(c₁::Cylinder, c₂::Cylinder) = boundary(c₁) ≈ boundary(c₂) -function (c::Cylinder{T})(ρ, φ, z) where {T} +function (c::Cylinder)(ρ, φ, z) + ℒ = lentype(c) + T = numtype(ℒ) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) || (z < 0 || z > 1) throw(DomainError((ρ, φ, z), "c(ρ, φ, z) is not defined for ρ, φ, z outside [0, 1]³.")) end @@ -76,16 +89,16 @@ function (c::Cylinder{T})(ρ, φ, z) where {T} o = b(0, 0) # rotation to align z axis with cylinder axis - Q = rotation_between(Vec{3,T}(0, 0, 1), d) + Q = urotbetween(Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)), d) # project a parametric segment between the top and bottom planes lsφ, lcφ = T(ρ) * r .* sincospi(2 * T(φ)) - p₁ = o + Q * Vec(lcφ, lsφ, T(0)) + p₁ = o + Q * Vec(lcφ, lsφ, zero(ℒ)) p₂ = o + Q * Vec(lcφ, lsφ, h) l = Line(p₁, p₂) s = Segment(l ∩ b, l ∩ t) s(T(z)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cylinder{T}}) where {T} = - Cylinder(rand(rng, Plane{T}), rand(rng, Plane{T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cylinder}) = + Cylinder(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index aa0e28501..0fe29cda0 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -24,29 +24,40 @@ Finally, construct a right vertical circular cylinder surface with given `radius See . """ -struct CylinderSurface{T} <: Primitive{3,T} - bot::Plane{T} - top::Plane{T} - radius::T +struct CylinderSurface{P<:Plane,ℒ<:Len} <: Primitive{3} + bot::P + top::P + radius::ℒ + CylinderSurface{P,ℒ}(bot, top, radius) where {P<:Plane,ℒ<:Len} = new(bot, top, radius) end -function CylinderSurface(start::Point{3,T}, finish::Point{3,T}, radius) where {T} +CylinderSurface(bot::P, top::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = CylinderSurface{P,float(ℒ)}(bot, top, radius) + +CylinderSurface(bot::P, top::P, radius) where {P<:Plane} = CylinderSurface(bot, top, addunit(radius, u"m")) + +function CylinderSurface(start::Point{3}, finish::Point{3}, radius) dir = finish - start bot = Plane(start, dir) top = Plane(finish, dir) - CylinderSurface(bot, top, T(radius)) + CylinderSurface(bot, top, radius) end CylinderSurface(start::Tuple, finish::Tuple, radius) = CylinderSurface(Point(start), Point(finish), radius) -CylinderSurface(start::Point{3,T}, finish::Point{3,T}) where {T} = CylinderSurface(start, finish, T(1)) +CylinderSurface(start::Point{3}, finish::Point{3}) = CylinderSurface(start, finish, oneunit(lentype(start))) CylinderSurface(start::Tuple, finish::Tuple) = CylinderSurface(Point(start), Point(finish)) -CylinderSurface(radius::T) where {T} = CylinderSurface(Point(T(0), T(0), T(0)), Point(T(0), T(0), T(1)), radius) +function CylinderSurface(radius) + z = zero(radius) + o = oneunit(radius) + CylinderSurface(Point(z, z, z), Point(z, z, o), radius) +end paramdim(::Type{<:CylinderSurface}) = 2 +lentype(::Type{<:CylinderSurface{P}}) where {P} = lentype(P) + radius(c::CylinderSurface) = c.radius bottom(c::CylinderSurface) = c.bot @@ -61,22 +72,26 @@ end axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) -function isright(c::CylinderSurface{T}) where {T} +function isright(c::CylinderSurface) + ℒ = lentype(c) + T = numtype(ℒ) # cylinder is right if axis # is aligned with plane normals a = axis(c) d = a(T(1)) - a(T(0)) v = normal(c.bot) w = normal(c.top) - isparallelv = isapprox(norm(d × v), zero(T), atol=atol(T)) - isparallelw = isapprox(norm(d × w), zero(T), atol=atol(T)) + isparallelv = isapproxzero(norm(d × v)) + isparallelw = isapproxzero(norm(d × w)) isparallelv && isparallelw end -Base.isapprox(c₁::CylinderSurface{T}, c₂::CylinderSurface{T}) where {T} = - c₁.bot ≈ c₂.bot && c₁.top ≈ c₂.top && isapprox(c₁.radius, c₂.radius, atol=atol(T)) +Base.isapprox(c₁::CylinderSurface, c₂::CylinderSurface) = + c₁.bot ≈ c₂.bot && c₁.top ≈ c₂.top && isapproxequal(c₁.radius, c₂.radius) -function (c::CylinderSurface{T})(φ, z) where {T} +function (c::CylinderSurface)(φ, z) + ℒ = lentype(c) + T = numtype(ℒ) if (φ < 0 || φ > 1) || (z < 0 || z > 1) throw(DomainError((φ, z), "c(φ, z) is not defined for φ, z outside [0, 1]².")) end @@ -89,7 +104,7 @@ function (c::CylinderSurface{T})(φ, z) where {T} o = center(c) # rotation to align z axis with cylinder axis - Q = rotation_between(d, Vec{3,T}(0, 0, 1)) + Q = urotbetween(d, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) # new normals of planes in new rotated system nᵦ = Q * normal(b) @@ -108,8 +123,8 @@ function (c::CylinderSurface{T})(φ, z) where {T} o + Q' * coordinates(p) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{CylinderSurface{T}}) where {T} = - CylinderSurface(rand(rng, Plane{T}), rand(rng, Plane{T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{CylinderSurface}) = + CylinderSurface(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) function hasintersectingplanes(c::CylinderSurface) x = c.bot ∩ c.top diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index 08567b843..0b0b0e2f7 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -10,15 +10,20 @@ given `plane` with given `radius`. See also [`Circle`](@ref). """ -struct Disk{T} <: Primitive{3,T} - plane::Plane{T} - radius::T +struct Disk{P<:Plane,ℒ<:Len} <: Primitive{3} + plane::P + radius::ℒ + Disk{P,ℒ}(plane, radius) where {P<:Plane,ℒ<:Len} = new(plane, radius) end -Disk(plane::Plane{T}, radius) where {T} = Disk(plane, T(radius)) +Disk(plane::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Disk{P,float(ℒ)}(plane, radius) + +Disk(plane::Plane, radius) = Disk(plane, addunit(radius, u"m")) paramdim(::Type{<:Disk}) = 2 +lentype(::Type{<:Disk{P}}) where {P} = lentype(P) + plane(d::Disk) = d.plane center(d::Disk) = d.plane(0, 0) @@ -27,16 +32,17 @@ radius(d::Disk) = d.radius normal(d::Disk) = normal(d.plane) -function (d::Disk{T})(ρ, φ) where {T} +function (d::Disk)(ρ, φ) + T = numtype(lentype(d)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, φ), "d(ρ, φ) is not defined for ρ, φ outside [0, 1]².")) end r = d.radius l = T(ρ) * r sφ, cφ = sincospi(2 * T(φ)) - u = l * cφ - v = l * sφ + u = ustrip(l * cφ) + v = ustrip(l * sφ) d.plane(u, v) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Disk{T}}) where {T} = Disk(rand(rng, Plane{T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Disk}) = Disk(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index ab0292deb..5ea98a722 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -7,24 +7,34 @@ A 3D ellipsoid with given `radii`, `center` and `rotation`. """ -struct Ellipsoid{T,R} <: Primitive{3,T} - radii::NTuple{3,T} - center::Point{3,T} +struct Ellipsoid{ℒ<:Len,P<:Point{3},R} <: Primitive{3} + radii::NTuple{3,ℒ} + center::P rotation::R + Ellipsoid{ℒ,P,R}(radii, center, rotation) where {ℒ<:Len,P<:Point{3},R} = new(radii, center, rotation) end -Ellipsoid(radii::NTuple{3,T}, center=(T(0), T(0), T(0)), rotation::R=I) where {T,R} = - Ellipsoid{T,R}(radii, center, rotation) +Ellipsoid(radii::NTuple{3,ℒ}, center::P, rotation::R) where {ℒ<:Len,P<:Point{3},R} = + Ellipsoid{float(ℒ),P,R}(radii, center, rotation) + +Ellipsoid(radii::NTuple{3}, center::P, rotation::R) where {P<:Point{3},R} = + Ellipsoid(addunit.(radii, u"m"), center, rotation) + +Ellipsoid(radii::NTuple{3,T}, center=(zero(T), zero(T), zero(T)), rotation=I) where {T} = + Ellipsoid(radii, Point(center), rotation) paramdim(::Type{<:Ellipsoid}) = 2 +lentype(::Type{<:Ellipsoid{ℒ,P}}) where {ℒ,P} = lentype(P) + radii(e::Ellipsoid) = e.radii center(e::Ellipsoid) = e.center rotation(e::Ellipsoid) = e.rotation -function (e::Ellipsoid{T})(θ, φ) where {T} +function (e::Ellipsoid)(θ, φ) + T = numtype(lentype(e)) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "e(θ, φ) is not defined for θ, φ outside [0, 1]².")) end @@ -39,5 +49,8 @@ function (e::Ellipsoid{T})(θ, φ) where {T} c + R * Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ellipsoid{T}}) where {T} = - Ellipsoid((rand(rng, T), rand(rng, T), rand(rng, T)), rand(rng, Point{3,T}), rand(rng, QuatRotation)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ellipsoid}) = Ellipsoid( + (rand(rng, Met{Float64}), rand(rng, Met{Float64}), rand(rng, Met{Float64})), + rand(rng, Point{3}), + rand(rng, QuatRotation) +) diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index 6d7224a30..3e0c32cba 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -10,23 +10,26 @@ See . See also [`FrustumSurface`](@ref). """ -struct Frustum{T} <: Primitive{3,T} - bot::Disk{T} - top::Disk{T} +struct Frustum{D<:Disk} <: Primitive{3} + bot::D + top::D - function Frustum{T}(bot, top) where {T} + function Frustum{D}(bot, top) where {D} bn = normal(plane(bot)) tn = normal(plane(top)) - @assert bn ⋅ tn ≈ 1 "Bottom and top plane must be parallel" + a = bn ⋅ tn + @assert a ≈ oneunit(a) "Bottom and top plane must be parallel" @assert center(bot) ≉ center(top) "Bottom and top centers need to be distinct" new(bot, top) end end -Frustum(bot::Disk{T}, top::Disk{T}) where {T} = Frustum{T}(bot, top) +Frustum(bot::D, top::D) where {D<:Disk} = Frustum{D}(bot, top) paramdim(::Type{<:Frustum}) = 3 +lentype(::Type{<:Frustum{D}}) where {D} = lentype(D) + bottom(f::Frustum) = f.bot top(f::Frustum) = f.top @@ -35,10 +38,10 @@ height(f::Frustum) = height(boundary(f)) axis(f::Frustum) = axis(boundary(f)) -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Frustum{T}}) where {T} - bottom = rand(rng, Disk{T}) +function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Frustum}) + bottom = rand(rng, Disk) ax = normal(plane(bottom)) - topplane = Plane{T}(center(bottom) + rand(T) * ax, ax) - top = Disk{T}(topplane, rand(T)) + topplane = Plane(center(bottom) + rand() * ax, ax) + top = Disk(topplane, rand(Met{Float64})) Frustum(bottom, top) end diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index e43279900..5707b69c7 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -10,23 +10,26 @@ See . See also [`Frustum`](@ref). """ -struct FrustumSurface{T} <: Primitive{3,T} - bot::Disk{T} - top::Disk{T} +struct FrustumSurface{D<:Disk} <: Primitive{3} + bot::D + top::D - function FrustumSurface{T}(bot, top) where {T} + function FrustumSurface{D}(bot, top) where {D} bn = normal(plane(bot)) tn = normal(plane(top)) - @assert bn ⋅ tn ≈ 1 "Bottom and top plane must be parallel" + a = bn ⋅ tn + @assert a ≈ oneunit(a) "Bottom and top plane must be parallel" @assert center(bot) ≉ center(top) "Bottom and top centers need to be distinct" new(bot, top) end end -FrustumSurface(bot::Disk{T}, top::Disk{T}) where {T} = FrustumSurface{T}(bot, top) +FrustumSurface(bot::D, top::D) where {D<:Disk} = FrustumSurface{D}(bot, top) paramdim(::Type{<:FrustumSurface}) = 2 +lentype(::Type{<:FrustumSurface{D}}) where {D} = lentype(D) + bottom(f::FrustumSurface) = f.bot top(f::FrustumSurface) = f.top @@ -35,7 +38,9 @@ height(f::FrustumSurface) = norm(center(bottom(f)) - center(top(f))) axis(f::FrustumSurface) = Line(center(bottom(f)), center(top(f))) -function (f::FrustumSurface{T})(φ, z) where {T} +function (f::FrustumSurface)(φ, z) + ℒ = lentype(f) + T = numtype(ℒ) if (φ < 0 || φ > 1) || (z < 0 || z > 1) throw(DomainError((φ, z), "f(φ, z) is not defined for φ, z outside [0, 1]².")) end @@ -46,7 +51,7 @@ function (f::FrustumSurface{T})(φ, z) where {T} l = norm(d) # rotation to align z axis with cylinder axis - Q = rotation_between(d, Vec{3,T}(0, 0, 1)) + Q = urotbetween(d, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) # scale coordinates φₛ = 2T(π) * φ @@ -56,15 +61,15 @@ function (f::FrustumSurface{T})(φ, z) where {T} x = cos(φₛ) * (rb * (l - zₛ) + rt * zₛ) / l y = sin(φₛ) * (rb * (l - zₛ) + rt * zₛ) / l z = zₛ - p = Vec{3,T}(x, y, z) + p = Vec(x, y, z) center(bottom(f)) + Q' * p end -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{FrustumSurface{T}}) where {T} - bottom = rand(rng, Disk{T}) +function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{FrustumSurface}) + bottom = rand(rng, Disk) ax = normal(plane(bottom)) - topplane = Plane{T}(center(bottom) + rand(T) * ax, ax) - top = Disk{T}(topplane, rand(T)) + topplane = Plane(center(bottom) + rand() * ax, ax) + top = Disk(topplane, rand(Met{Float64})) FrustumSurface(bottom, top) end diff --git a/src/primitives/line.jl b/src/primitives/line.jl index fdae89530..b148e1f75 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -9,18 +9,20 @@ A line passing through points `a` and `b`. See also [`Segment`](@ref). """ -struct Line{Dim,T} <: Primitive{Dim,T} - a::Point{Dim,T} - b::Point{Dim,T} +struct Line{Dim,P<:Point{Dim}} <: Primitive{Dim} + a::P + b::P end Line(a::Tuple, b::Tuple) = Line(Point(a), Point(b)) paramdim(::Type{<:Line}) = 1 +lentype(::Type{<:Line{Dim,P}}) where {Dim,P} = lentype(P) + ==(l₁::Line, l₂::Line) = l₁.a ∈ l₂ && l₁.b ∈ l₂ && l₂.a ∈ l₁ && l₂.b ∈ l₁ (l::Line)(t) = l.a + t * (l.b - l.a) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Line{Dim,T}}) where {Dim,T} = - Line(rand(rng, Point{Dim,T}, 2)...) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Line{Dim}}) where {Dim} = + Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index c7da41f1f..b5a0abcc2 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -32,21 +32,29 @@ Same as above, but here the apex is at `Apex(0, 0, 0)`. See also . """ -struct ParaboloidSurface{T} <: Primitive{3,T} - apex::Point{3,T} - radius::T - focallength::T +struct ParaboloidSurface{P<:Point{3},ℒ<:Len} <: Primitive{3} + apex::P + radius::ℒ + focallength::ℒ + ParaboloidSurface{P,ℒ}(apex, radius, focallength) where {P<:Point{3},ℒ<:Len} = new(apex, radius, focallength) end -ParaboloidSurface(apex::Point{3,T}, radius, focallength) where {T} = ParaboloidSurface{T}(apex, radius, focallength) +ParaboloidSurface(apex::P, radius::ℒ, focallength::ℒ) where {P<:Point{3},ℒ<:Len} = + ParaboloidSurface{P,float(ℒ)}(apex, radius, focallength) + +ParaboloidSurface(apex::Point{3}, radius::Len, focallength::Len) = + ParaboloidSurface(apex, promote(radius, focallength)...) + +ParaboloidSurface(apex::Point{3}, radius, focallength) = + ParaboloidSurface(apex, addunit(radius, u"m"), addunit(focallength, u"m")) ParaboloidSurface(apex::Tuple, radius, focallength) = ParaboloidSurface(Point(apex), radius, focallength) -ParaboloidSurface(apex::Point{3,T}, radius) where {T} = ParaboloidSurface(apex, T(radius), T(1)) +ParaboloidSurface(apex::Point{3}, radius) = ParaboloidSurface(apex, radius, oneunit(radius)) ParaboloidSurface(apex::Tuple, radius) = ParaboloidSurface(Point(apex), radius) -ParaboloidSurface(apex::Point{3,T}) where {T} = ParaboloidSurface(apex, T(1)) +ParaboloidSurface(apex::Point{3}) = ParaboloidSurface(apex, oneunit(lentype(apex))) ParaboloidSurface(apex::Tuple) = ParaboloidSurface(Point(apex)) @@ -54,6 +62,8 @@ ParaboloidSurface() = ParaboloidSurface(Point(0, 0, 0)) paramdim(::Type{<:ParaboloidSurface}) = 2 +lentype(::Type{<:ParaboloidSurface{P}}) where {P} = lentype(P) + """ focallength(p::ParaboloidSurface) @@ -81,14 +91,13 @@ apex(p::ParaboloidSurface) = p.apex Return the focal axis, connecting the focus with the apex of the paraboloid. The axis is always aligned with the z direction. """ -axis(p::ParaboloidSurface{T}) where {T} = Line(p.apex, p.apex + Vec(T(0), T(0), p.focallength)) +axis(p::ParaboloidSurface{P,ℒ}) where {P,ℒ} = Line(p.apex, p.apex + Vec(ℒ(0), ℒ(0), p.focallength)) -Base.isapprox(p₁::ParaboloidSurface{T}, p₂::ParaboloidSurface{T}) where {T} = - p₁.apex ≈ p₂.apex && - isapprox(p₁.focallength, p₂.focallength, atol=atol(T)) && - isapprox(p₁.radius, p₂.radius, atol=atol(T)) +Base.isapprox(p₁::ParaboloidSurface, p₂::ParaboloidSurface) = + p₁.apex ≈ p₂.apex && isapproxequal(p₁.focallength, p₂.focallength) && isapproxequal(p₁.radius, p₂.radius) -function (p::ParaboloidSurface{T})(ρ, θ) where {T} +function (p::ParaboloidSurface)(ρ, θ) + T = numtype(lentype(p)) if (ρ < 0 || ρ > 1) throw(DomainError((ρ, θ), "p(ρ, θ) is not defined for ρ outside [0, 1].")) end @@ -103,5 +112,5 @@ function (p::ParaboloidSurface{T})(ρ, θ) where {T} c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ParaboloidSurface{T}}) where {T} = - ParaboloidSurface(rand(rng, Point{3,T}), rand(rng, T), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ParaboloidSurface}) = + ParaboloidSurface(rand(rng, Point{3}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 8dcb0887d..bb80f64f9 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -13,26 +13,24 @@ defined by non-parallel vectors `u` and `v`. Alternatively specify point `p` and a given normal vector `n` to the plane. """ -struct Plane{T} <: Primitive{3,T} - p::Point{3,T} - u::Vec{3,T} - v::Vec{3,T} +struct Plane{P<:Point{3},V<:Vec{3}} <: Primitive{3} + p::P + u::V + v::V end -function Plane{T}(p::Point{3,T}, n::Vec{3,T}) where {T} +function Plane(p::Point{3}, n::Vec{3}) u, v = householderbasis(n) - Plane{T}(p, u, v) + Plane(p, u, v) end -Plane(p::Point{3,T}, n::Vec{3,T}) where {T} = Plane{T}(p, n) - Plane(p::Tuple, u::Tuple, v::Tuple) = Plane(Point(p), Vec(u), Vec(v)) Plane(p::Tuple, n::Tuple) = Plane(Point(p), Vec(n)) -function Plane(p1::Point{3,T}, p2::Point{3,T}, p3::Point{3,T}) where {T} +function Plane(p1::Point{3}, p2::Point{3}, p3::Point{3}) t = Triangle(p1, p2, p3) - if isapprox(area(t), zero(T), atol=atol(T)) + if isapproxzero(area(t)) throw(ArgumentError("The three points are colinear.")) end Plane(p1, normal(t)) @@ -40,19 +38,19 @@ end paramdim(::Type{<:Plane}) = 2 -normal(p::Plane) = normalize(p.u × p.v) +lentype(::Type{<:Plane{P}}) where {P} = lentype(P) + +normal(p::Plane) = unormalize(ucross(p.u, p.v)) ==(p₁::Plane, p₂::Plane) = p₁(0, 0) ∈ p₂ && p₁(1, 0) ∈ p₂ && p₁(0, 1) ∈ p₂ && p₂(0, 0) ∈ p₁ && p₂(1, 0) ∈ p₁ && p₂(0, 1) ∈ p₁ -Base.isapprox(p₁::Plane{T}, p₂::Plane{T}) where {T} = - isapprox((p₁(0, 0) - p₂(0, 0)) ⋅ normal(p₂), zero(T), atol=atol(T)) && - isapprox((p₂(0, 0) - p₁(0, 0)) ⋅ normal(p₁), zero(T), atol=atol(T)) && - isapprox(_area(normal(p₁), normal(p₂)), zero(T), atol=atol(T)) - -_area(v₁::Vec, v₂::Vec) = norm(v₁ × v₂) +Base.isapprox(p₁::Plane, p₂::Plane) = + isapproxzero(udot(p₁(0, 0) - p₂(0, 0), normal(p₂))) && + isapproxzero(udot(p₂(0, 0) - p₁(0, 0), normal(p₁))) && + isapproxzero(norm(ucross(normal(p₁), normal(p₂)))) (p::Plane)(u, v) = p.p + u * p.u + v * p.v -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Plane{T}}) where {T} = - Plane(rand(rng, Point{3,T}), rand(rng, Vec{3,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Plane}) = + Plane(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}})) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 67a5a311f..427afd69c 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -5,68 +5,53 @@ """ Point(x₁, x₂, ..., xₙ) Point((x₁, x₂, ..., xₙ)) - Point{Dim,T}(x₁, x₂, ..., xₙ) - Point{Dim,T}((x₁, x₂, ..., xₙ)) -A point in `Dim`-dimensional space with coordinates of type `T`. +A point in `Dim`-dimensional space with coordinates in length units (default to meters). The coordinates of the point are given with respect to the canonical -Euclidean basis, and `Integer` coordinates are converted to `Float64`. +Euclidean basis, and integer coordinates are converted to float. ## Examples ```julia # 2D points -A = Point(0.0, 1.0) # double precision as expected -B = Point(0f0, 1f0) # single precision as expected -C = Point(0, 0) # Integer is converted to Float64 by design -D = Point2(0, 1) # explicitly ask for double precision -E = Point2f(0, 1) # explicitly ask for single precision +Point(1.0, 2.0) # add default units +Point(1.0u"m", 2.0u"m") # double precision as expected +Point(1f0u"km", 2f0u"km") # single precision as expected +Point(1u"m", 2u"m") # integer is converted to float by design # 3D points -F = Point(1.0, 2.0, 3.0) # double precision as expected -G = Point(1f0, 2f0, 3f0) # single precision as expected -H = Point(1, 2, 3) # Integer is converted to Float64 by design -I = Point3(1, 2, 3) # explicitly ask for double precision -J = Point3f(1, 2, 3) # explicitly ask for single precision +Point(1.0, 2.0, 3.0) # add default units +Point(1.0u"m", 2.0u"m", 3.0u"m") # double precision as expected +Point(1f0u"km", 2f0u"km", 3f0u"km") # single precision as expected +Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design ``` ### Notes -- Type aliases are `Point1`, `Point2`, `Point3`, `Point1f`, `Point2f`, `Point3f` -- `Integer` coordinates are not supported because most geometric processing - algorithms assume a continuous space. The conversion to `Float64` avoids +- Integer coordinates are not supported because most geometric processing + algorithms assume a continuous space. The conversion to float avoids `InexactError` and other unexpected results. """ -struct Point{Dim,T} <: Primitive{Dim,T} - coords::Vec{Dim,T} - Point(coords::Vec{Dim,T}) where {Dim,T} = new{Dim,T}(coords) +struct Point{Dim,C<:CRS} <: Primitive{Dim} + coords::C + Point(coords::C) where {C<:CRS} = new{CoordRefSystems.ndims(coords),C}(coords) end # convenience constructors -Point{Dim,T}(coords...) where {Dim,T} = Point(Vec{Dim,T}(coords...)) -Point(coords...) = Point(Vec(coords...)) +Point(coords...) = Point(Cartesian(coords...)) +Point(coords::Tuple) = Point(Cartesian(coords...)) +Point(coords::Vec) = Point(Cartesian(Tuple(coords))) -# coordinate type conversions -Base.convert(::Type{Point{Dim,T}}, coords) where {Dim,T} = Point{Dim,T}(coords) -Base.convert(::Type{Point{Dim,T}}, p::Point) where {Dim,T} = Point{Dim,T}(p.coords) -Base.convert(::Type{Point}, coords) = Point{length(coords),eltype(coords)}(coords) +paramdim(::Type{<:Point}) = 0 -# type aliases for convenience -const Point1 = Point{1,Float64} -const Point2 = Point{2,Float64} -const Point3 = Point{3,Float64} -const Point1f = Point{1,Float32} -const Point2f = Point{2,Float32} -const Point3f = Point{3,Float32} - -paramdim(::Type{Point{Dim,T}}) where {Dim,T} = 0 +lentype(::Type{<:Point{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) center(p::Point) = p ==(A::Point, B::Point) = A.coords == B.coords -Base.isapprox(A::Point{Dim,T}, B::Point{Dim,T}; atol=atol(T), kwargs...) where {Dim,T} = +Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) = isapprox(A.coords, B.coords; atol, kwargs...) """ @@ -75,7 +60,7 @@ Base.isapprox(A::Point{Dim,T}, B::Point{Dim,T}; atol=atol(T), kwargs...) where { Return the coordinates of the `point` with respect to the canonical Euclidean basis. """ -coordinates(A::Point) = A.coords +coordinates(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues(A.coords)) """ -(A::Point, B::Point) @@ -83,7 +68,7 @@ coordinates(A::Point) = A.coords Return the [`Vec`](@ref) associated with the direction from point `B` to point `A`. """ --(A::Point, B::Point) = A.coords - B.coords +-(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = coordinates(A) - coordinates(B) """ +(A::Point, v::Vec) @@ -92,8 +77,8 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point, v::Vec) = Point(A.coords + v) -+(v::Vec, A::Point) = A + v ++(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(A) + v) ++(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A + v """ -(A::Point, v::Vec) @@ -102,8 +87,8 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point, v::Vec) = Point(A.coords - v) --(v::Vec, A::Point) = A - v +-(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(A) - v) +-(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A - v """ ⪯(A::Point, B::Point) @@ -113,10 +98,10 @@ at a reference (or start) point `A`. Generalized inequality for non-negative orthant Rⁿ₊. """ -⪯(A::Point{Dim,T}, B::Point{Dim,T}) where {Dim,T} = all(≥(zero(T)), B - A) -⪰(A::Point{Dim,T}, B::Point{Dim,T}) where {Dim,T} = all(≥(zero(T)), A - B) -≺(A::Point{Dim,T}, B::Point{Dim,T}) where {Dim,T} = all(>(zero(T)), B - A) -≻(A::Point{Dim,T}, B::Point{Dim,T}) where {Dim,T} = all(>(zero(T)), A - B) +⪯(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(≥(0u"m"), B - A) +⪰(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(≥(0u"m"), A - B) +≺(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(>(0u"m"), B - A) +≻(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(>(0u"m"), A - B) """ ∠(A, B, C) @@ -137,14 +122,26 @@ See . ∠(A::P, B::P, C::P) where {P<:Point{2}} = ∠(A - B, C - B) ∠(A::P, B::P, C::P) where {P<:Point{3}} = ∠(A - B, C - B) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Point{Dim,T}}) where {Dim,T} = Point(rand(rng, Vec{Dim,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Point{Dim}}) where {Dim} = + Point(rand(rng, Cartesian{NoDatum,Dim})) + +# ----------- +# IO METHODS +# ----------- function Base.show(io::IO, point::Point) if get(io, :compact, false) - print(io, Tuple(point.coords)) + print(io, "(") else - print(io, "Point$(Tuple(point.coords))") + print(io, "Point(") end + cvalues = CoordRefSystems.cvalues(point.coords) + cnames = CoordRefSystems.cnames(point.coords) + printfields(io, cvalues, cnames, singleline=true) + print(io, ")") end -Base.show(io::IO, ::MIME"text/plain", point::Point) = show(io, point) +function Base.show(io::IO, mime::MIME"text/plain", point::Point) + print(io, "Point with ") + show(io, mime, point.coords) +end diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index bce7edb7d..3802074d1 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -9,15 +9,17 @@ A ray originating at point `p`, pointed in direction `v`. It can be called as `r(t)` with `t > 0` to cast it at `p + t * v`. """ -struct Ray{Dim,T} <: Primitive{Dim,T} - p::Point{Dim,T} - v::Vec{Dim,T} +struct Ray{Dim,P<:Point{Dim},V<:Vec{Dim}} <: Primitive{Dim} + p::P + v::V end Ray(p::Tuple, v::Tuple) = Ray(Point(p), Vec(v)) paramdim(::Type{<:Ray}) = 1 +lentype(::Type{<:Ray{Dim,P}}) where {Dim,P} = lentype(P) + ==(r₁::Ray, r₂::Ray) = (r₁.p ≈ r₂.p) && (r₁.p + r₁.v) ∈ r₂ && (r₂.p + r₂.v) ∈ r₁ function (r::Ray)(t) @@ -27,5 +29,5 @@ function (r::Ray)(t) r.p + t * r.v end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ray{Dim,T}}) where {Dim,T} = - Ray(rand(rng, Point{Dim,T}), rand(rng, Vec{Dim,T})) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ray{Dim}}) where {Dim} = + Ray(rand(rng, Point{Dim}), rand(rng, Vec{Dim,Met{Float64}})) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 9a046396a..5d6c4b60c 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -9,16 +9,19 @@ A sphere with `center` and `radius`. See also [`Ball`](@ref). """ -struct Sphere{Dim,T} <: Primitive{Dim,T} - center::Point{Dim,T} - radius::T +struct Sphere{Dim,P<:Point{Dim},ℒ<:Len} <: Primitive{Dim} + center::P + radius::ℒ + Sphere{Dim,P,ℒ}(center, radius) where {Dim,P<:Point{Dim},ℒ<:Len} = new(center, radius) end -Sphere(center::Point{Dim,T}, radius) where {Dim,T} = Sphere(center, T(radius)) +Sphere(center::P, radius::ℒ) where {Dim,P<:Point{Dim},ℒ<:Len} = Sphere{Dim,P,float(ℒ)}(center, radius) + +Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) Sphere(center::Tuple, radius) = Sphere(Point(center), radius) -Sphere(center::Point{Dim,T}) where {Dim,T} = Sphere(center, T(1)) +Sphere(center::Point) = Sphere(center, oneunit(lentype(center))) Sphere(center::Tuple) = Sphere(Point(center)) @@ -52,7 +55,7 @@ function Sphere(p1::Point{3}, p2::Point{3}, p3::Point{3}, p4::Point{3}) v3 = p3 - p4 V = volume(Tetrahedron(p1, p2, p3, p4)) r⃗ = ((v3 ⋅ v3) * (v1 × v2) + (v2 ⋅ v2) * (v3 × v1) + (v1 ⋅ v1) * (v2 × v3)) / 12V - center = p4 + r⃗ + center = p4 + Vec(r⃗) radius = norm(r⃗) Sphere(center, radius) end @@ -61,11 +64,14 @@ Sphere(p1::Tuple, p2::Tuple, p3::Tuple, p4::Tuple) = Sphere(Point(p1), Point(p2) paramdim(::Type{<:Sphere{Dim}}) where {Dim} = Dim - 1 +lentype(::Type{<:Sphere{Dim,P}}) where {Dim,P} = lentype(P) + center(s::Sphere) = s.center radius(s::Sphere) = s.radius -function (s::Sphere{2,T})(φ) where {T} +function (s::Sphere{2})(φ) + T = numtype(lentype(s)) if (φ < 0 || φ > 1) throw(DomainError(φ, "s(φ) is not defined for φ outside [0, 1].")) end @@ -77,7 +83,8 @@ function (s::Sphere{2,T})(φ) where {T} c + Vec(x, y) end -function (s::Sphere{3,T})(θ, φ) where {T} +function (s::Sphere{3})(θ, φ) + T = numtype(lentype(s)) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "s(θ, φ) is not defined for θ, φ outside [0, 1]².")) end @@ -91,5 +98,5 @@ function (s::Sphere{3,T})(θ, φ) where {T} c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Sphere{Dim,T}}) where {Dim,T} = - Sphere(rand(rng, Point{Dim,T}), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Sphere{Dim}}) where {Dim} = + Sphere(rand(rng, Point{Dim}), rand(rng, Met{Float64})) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 5b9fa8b75..794b1d7ce 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -9,14 +9,21 @@ A torus centered at `center` with axis of revolution directed by `normal` and with radii `major` and `minor`. """ -struct Torus{T} <: Primitive{3,T} - center::Point{3,T} - normal::Vec{3,T} - major::T - minor::T +struct Torus{P<:Point{3},V<:Vec{3},ℒ<:Len} <: Primitive{3} + center::P + normal::V + major::ℒ + minor::ℒ + Torus{P,V,ℒ}(center, normal, major, minor) where {P<:Point{3},V<:Vec{3},ℒ<:Len} = new(center, normal, major, minor) end -Torus(center::Point{3,T}, normal::Vec{3,T}, major, minor) where {T} = Torus(center, normal, T(major), T(minor)) +Torus(center::P, normal::V, major::ℒ, minor::ℒ) where {P<:Point{3},V<:Vec{3},ℒ<:Len} = + Torus{P,V,float(ℒ)}(center, normal, major, minor) + +Torus(center::Point{3}, normal::Vec{3}, major::Len, minor::Len) = Torus(center, normal, promote(major, minor)...) + +Torus(center::Point{3}, normal::Vec{3}, major, minor) = + Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(normal), major, minor) @@ -26,16 +33,20 @@ Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(nor The torus whose centerline passes through points `p1`, `p2` and `p3` and with minor radius `minor`. """ -function Torus(p1::Point{3,T}, p2::Point{3,T}, p3::Point{3,T}, minor) where {T} +function Torus(p1::Point{3}, p2::Point{3}, p3::Point{3}, minor::Len) c = Circle(p1, p2, p3) p = Plane(p1, p2, p3) - Torus(center(c), normal(p), radius(c), T(minor)) + Torus(center(c), normal(p), radius(c), minor) end +Torus(p1::Point{3}, p2::Point{3}, p3::Point{3}, minor) = Torus(p1, p2, p3, addunit(minor, u"m")) + Torus(p1::Tuple, p2::Tuple, p3::Tuple, minor) = Torus(Point(p1), Point(p2), Point(p3), minor) paramdim(::Type{<:Torus}) = 2 +lentype(::Type{<:Torus{P}}) where {P} = lentype(P) + center(t::Torus) = t.center normal(t::Torus) = t.normal @@ -44,14 +55,16 @@ radii(t::Torus) = (t.major, t.minor) axis(t::Torus) = Line(t.center, t.center + t.normal) -function (t::Torus{T})(θ, φ) where {T} +function (t::Torus)(θ, φ) + ℒ = lentype(t) + T = numtype(ℒ) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "t(θ, φ) is not defined for θ, φ outside [0, 1]².")) end c, n⃗ = t.center, t.normal R, r = t.major, t.minor - Q = rotation_between(Vec{3,T}(0, 0, 1), n⃗) + Q = urotbetween(Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)), n⃗) sθ, cθ = sincospi(2 * T(-θ)) sφ, cφ = sincospi(2 * T(φ)) @@ -59,8 +72,8 @@ function (t::Torus{T})(θ, φ) where {T} y = (R + r * cθ) * sφ z = r * sθ - c + Q * Vec{3,T}(x, y, z) + c + Q * Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Torus{T}}) where {T} = - Torus(rand(rng, Point{3,T}), rand(rng, Vec{3,T}), rand(rng, T), rand(rng, T)) +Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Torus}) = + Torus(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) diff --git a/src/projecting.jl b/src/projecting.jl index 2f65e1ffa..4bfa27b0b 100644 --- a/src/projecting.jl +++ b/src/projecting.jl @@ -34,6 +34,8 @@ function proj(points, basis) # project points map(points) do p d = p - c - Point(d ⋅ u, d ⋅ v) + x = udot(d, u) + y = udot(d, v) + Point(x, y) end end diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 7325efca8..793271446 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -47,10 +47,11 @@ function _refinedims(x, f) end function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) + T = numtype(lentype(grid)) fᵢ, fⱼ = factors sᵢ, sⱼ = size(grid) - us = 0:(1 / fᵢ):1 - vs = 0:(1 / fⱼ):1 + us = 0:T(1 / fᵢ):1 + vs = 0:T(1 / fⱼ):1 catᵢ(A...) = cat(A..., dims=Val(1)) catⱼ(A...) = cat(A..., dims=Val(2)) @@ -72,11 +73,12 @@ function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) end function _XYZ(grid::StructuredGrid{3}, factors::Dims{3}) + T = numtype(lentype(grid)) fᵢ, fⱼ, fₖ = factors sᵢ, sⱼ, sₖ = size(grid) - us = 0:(1 / fᵢ):1 - vs = 0:(1 / fⱼ):1 - ws = 0:(1 / fₖ):1 + us = 0:T(1 / fᵢ):1 + vs = 0:T(1 / fⱼ):1 + ws = 0:T(1 / fₖ):1 catᵢ(A...) = cat(A..., dims=Val(1)) catⱼ(A...) = cat(A..., dims=Val(2)) catₖ(A...) = cat(A..., dims=Val(3)) diff --git a/src/sampling/homogeneous.jl b/src/sampling/homogeneous.jl index 103b6204a..d10f8d390 100644 --- a/src/sampling/homogeneous.jl +++ b/src/sampling/homogeneous.jl @@ -21,7 +21,7 @@ function sample(rng::AbstractRNG, d::Domain, method::HomogeneousSampling) weights = isnothing(method.weights) ? measure.(d) : method.weights # sample elements with weights - w = WeightedSampling(size, weights, replace=true) + w = WeightedSampling(size, ustrip.(weights), replace=true) # within each element sample a single point h = HomogeneousSampling(1) @@ -29,9 +29,9 @@ function sample(rng::AbstractRNG, d::Domain, method::HomogeneousSampling) (first(sample(rng, e, h)) for e in sample(rng, d, w)) end -function sample(rng::AbstractRNG, geom::Geometry{Dim,T}, method::HomogeneousSampling) where {Dim,T} +function sample(rng::AbstractRNG, geom::Geometry, method::HomogeneousSampling) if isparametrized(geom) - randpoint() = geom(rand(rng, T, paramdim(geom))...) + randpoint() = geom(rand(rng, numtype(lentype(geom)), paramdim(geom))...) (randpoint() for _ in 1:(method.size)) else sample(rng, discretize(geom), method) @@ -42,32 +42,32 @@ end # SPECIAL CASES # -------------- -function sample(rng::AbstractRNG, triangle::Triangle{Dim,T}, method::HomogeneousSampling) where {Dim,T} +function sample(rng::AbstractRNG, triangle::Triangle, method::HomogeneousSampling) function randpoint() # sample barycentric coordinates - u₁, u₂ = rand(rng, T, 2) + u₁, u₂ = rand(rng, numtype(lentype(triangle)), 2) λ₁, λ₂ = 1 - √u₁, u₂ * √u₁ triangle(λ₁, λ₂) end (randpoint() for _ in 1:(method.size)) end -function sample(rng::AbstractRNG, tetrahedron::Tetrahedron{Dim,T}, method::HomogeneousSampling) where {Dim,T} +function sample(rng::AbstractRNG, tetrahedron::Tetrahedron, method::HomogeneousSampling) @error "not implemented" end -function sample(rng::AbstractRNG, ball::Ball{2,T}, method::HomogeneousSampling) where {T} +function sample(rng::AbstractRNG, ball::Ball{2}, method::HomogeneousSampling) function randpoint() - u₁, u₂ = rand(rng, T, 2) + u₁, u₂ = rand(rng, numtype(lentype(ball)), 2) ball(√u₁, u₂) end (randpoint() for _ in 1:(method.size)) end -function sample(rng::AbstractRNG, ball::Ball{3,T}, method::HomogeneousSampling) where {T} +function sample(rng::AbstractRNG, ball::Ball{3}, method::HomogeneousSampling) function randpoint() - u₁, u₂, u₃ = rand(rng, T, 3) - ball(∛u₁, acos(1 - 2u₂) / T(π), u₃) + u₁, u₂, u₃ = rand(rng, numtype(lentype(ball)), 3) + ball(∛u₁, acos(1 - 2u₂) / π, u₃) end (randpoint() for _ in 1:(method.size)) end diff --git a/src/sampling/mindistance.jl b/src/sampling/mindistance.jl index f411e3352..f87177992 100644 --- a/src/sampling/mindistance.jl +++ b/src/sampling/mindistance.jl @@ -48,7 +48,7 @@ function sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) N = 2V / √3 * (ρ / α)^2 # number of oversamples (Medeiros et al. 2014) - O = ceil(Int, δ * N) + O = ceil(Int, δ * ustrip(N)) # oversample the object points = sample(rng, d, HomogeneousSampling(O)) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 4ebe1b99c..9d8986b16 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -23,23 +23,20 @@ end RegularSampling(sizes::Vararg{Int,N}) where {N} = RegularSampling(sizes) -function sample(::AbstractRNG, geom::Geometry{Dim,T}, method::RegularSampling) where {Dim,T} - V = floattype(T) +function sample(::AbstractRNG, geom::Geometry, method::RegularSampling) + T = numtype(lentype(geom)) D = paramdim(geom) sz = fitdims(method.sizes, D) δₛ = firstoffset(geom) δₑ = lastoffset(geom) - tₛ = ntuple(i -> V(0 + δₛ[i](sz[i])), D) - tₑ = ntuple(i -> V(1 - δₑ[i](sz[i])), D) + tₛ = ntuple(i -> T(0 + δₛ[i](sz[i])), D) + tₑ = ntuple(i -> T(1 - δₑ[i](sz[i])), D) rs = (range(tₛ[i], stop=tₑ[i], length=sz[i]) for i in 1:D) iᵣ = (geom(uv...) for uv in Iterators.product(rs...)) iₚ = (p for p in extrapoints(geom)) Iterators.flatmap(identity, (iᵣ, iₚ)) end -floattype(T::Type{<:Quantity}) = floattype(Unitful.numtype(T)) -floattype(T::Type) = float(T) - firstoffset(g::Geometry) = ntuple(i -> (n -> zero(n)), paramdim(g)) lastoffset(g::Geometry) = ntuple(i -> (n -> isperiodic(g)[i] ? inv(n) : zero(n)), paramdim(g)) extrapoints(::Geometry) = () diff --git a/src/sets.jl b/src/sets.jl index c00d35956..545aead09 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -15,13 +15,15 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` """ -struct GeometrySet{Dim,T,G<:Geometry{Dim,T}} <: Domain{Dim,T} +struct GeometrySet{Dim,G<:Geometry{Dim}} <: Domain{Dim} geoms::Vector{G} end # constructor with iterator of geometries GeometrySet(geoms) = GeometrySet(map(identity, geoms)) +lentype(::Type{<:GeometrySet{Dim,G}}) where {Dim,G} = lentype(G) + element(d::GeometrySet, ind::Int) = d.geoms[ind] nelements(d::GeometrySet) = length(d.geoms) @@ -37,7 +39,7 @@ Base.vcat(d1::Domain, d2::GeometrySet) = GeometrySet(vcat(collect(d1), d2.geoms) # SPECIAL CASE: POINT SET # ------------------------ -const PointSet{Dim,T} = GeometrySet{Dim,T,Point{Dim,T}} +const PointSet{Dim,P<:Point{Dim}} = GeometrySet{Dim,P} """ PointSet(points) @@ -58,7 +60,7 @@ julia> PointSet([1,2,3], [4,5,6]) julia> PointSet([1 4; 2 5; 3 6]) ``` """ -PointSet(points::AbstractVector{P}) where {P<:Point} = PointSet{embeddim(P),coordtype(P)}(points) +PointSet(points::AbstractVector{P}) where {Dim,P<:Point{Dim}} = PointSet{Dim,P}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) diff --git a/src/sideof.jl b/src/sideof.jl index 9e2248f35..323679521 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -39,9 +39,9 @@ Possible results are `LEFT`, `RIGHT` or `ON` the `line`. * Assumes the orientation of `Segment(line(0), line(1))`. """ -function sideof(point::Point{2,T}, line::Line{2,T}) where {T} +function sideof(point::Point{2}, line::Line{2}) a = signarea(point, line(0), line(1)) - ifelse(a > atol(T), LEFT, ifelse(a < -atol(T), RIGHT, ON)) + ifelse(a > atol(a), LEFT, ifelse(a < -atol(a), RIGHT, ON)) end """ @@ -50,10 +50,7 @@ end Determines on which side the `point` is in relation to the `ring`. Possible results are `IN` or `OUT` the `ring`. """ -function sideof(point::Point{2,T}, ring::Ring{2,T}) where {T} - w = winding(point, ring) - ifelse(isapprox(w, zero(T), atol=atol(T)), OUT, IN) -end +sideof(point::Point{2}, ring::Ring{2}) = ifelse(isapproxzero(winding(point, ring)), OUT, IN) # ----- # MESH @@ -74,12 +71,11 @@ sideof(point::Point{3}, mesh::Mesh{3}) = sideof((point,), mesh) |> first sideof(points, line::Line{2}) = map(point -> sideof(point, line), points) function sideof(points, object::GeometryOrDomain) - T = coordtype(object) bbox = boundingbox(object) isin = tcollect(point ∈ bbox for point in points) inds = findall(isin) wind = winding(collectat(points, inds), object) side = fill(OUT, length(isin)) - side[inds] .= ifelse.(isapprox.(wind, zero(T), atol=atol(T)), OUT, IN) + side[inds] .= map(w -> ifelse(isapproxzero(w), OUT, IN), wind) side end diff --git a/src/simplification/douglaspeucker.jl b/src/simplification/douglaspeucker.jl index 9887c901a..ef744a8de 100644 --- a/src/simplification/douglaspeucker.jl +++ b/src/simplification/douglaspeucker.jl @@ -25,7 +25,11 @@ struct DouglasPeucker{T} <: SimplificationMethod maxiter::Int end -DouglasPeucker(ϵ=nothing; min=3, max=typemax(Int), maxiter=10) = DouglasPeucker(ϵ, min, max, maxiter) +DouglasPeucker(ϵ=nothing; min=3, max=typemax(Int), maxiter=10) = DouglasPeucker(_ϵ(ϵ), min, max, maxiter) + +_ϵ(ϵ::Nothing) = ϵ +_ϵ(ϵ::Number) = _ϵ(addunit(ϵ, u"m")) +_ϵ(ϵ::Len) = float(ϵ) function simplify(chain::Chain, method::DouglasPeucker) v = if isnothing(method.ϵ) @@ -39,11 +43,11 @@ function simplify(chain::Chain, method::DouglasPeucker) end # simplification by means of binary search -function βsimplify(v::AbstractVector{Point{Dim,T}}, min, max, maxiter) where {Dim,T} +function βsimplify(v::AbstractVector{P}, min, max, maxiter) where {P<:Point} i = 0 u = v n = length(u) - a = zero(T) + a = zero(lentype(P)) b = initeps(u) while !(min ≤ n ≤ max) && i < maxiter # midpoint candidate @@ -64,9 +68,9 @@ function βsimplify(v::AbstractVector{Point{Dim,T}}, min, max, maxiter) where {D end # initial ϵ guess for a given chain -function initeps(v::AbstractVector{Point{Dim,T}}) where {Dim,T} +function initeps(v::AbstractVector{P}) where {P<:Point} n = length(v) - ϵ = typemax(T) + ϵ = typemax(lentype(P)) l = Line(first(v), last(v)) d = [evaluate(Euclidean(), v[i], l) for i in 2:(n - 1)] ϵ = quantile(d, 0.25) @@ -74,11 +78,11 @@ function initeps(v::AbstractVector{Point{Dim,T}}) where {Dim,T} end # simplify chain assuming it is open -function ϵsimplify(v::AbstractVector{Point{Dim,T}}, ϵ) where {Dim,T} +function ϵsimplify(v::AbstractVector{P}, ϵ) where {P<:Point} # find vertex with maximum distance # to reference line l = Line(first(v), last(v)) - imax, dmax = 0, zero(T) + imax, dmax = 0, zero(lentype(P)) for i in 2:(length(v) - 1) d = evaluate(Euclidean(), v[i], l) if d > dmax diff --git a/src/simplification/selinger.jl b/src/simplification/selinger.jl index e16a7f9a1..3793ae04e 100644 --- a/src/simplification/selinger.jl +++ b/src/simplification/selinger.jl @@ -14,11 +14,17 @@ to the resulting segments based on deviation tolerance `ϵ`. * Selinger, P. 2003. [Potrace: A polygon-based tracing algorithm] (https://potrace.sourceforge.net/potrace.pdf) """ -struct Selinger{T} <: SimplificationMethod - ϵ::T +struct Selinger{ℒ<:Len} <: SimplificationMethod + ϵ::ℒ + Selinger(ϵ::ℒ) where {ℒ<:Len} = new{float(ℒ)}(ϵ) end -function simplify(chain::Chain{Dim,T}, method::Selinger) where {Dim,T} +Selinger(ϵ) = Selinger(addunit(ϵ, u"m")) + +function simplify(chain::Chain, method::Selinger) + ℒ = lentype(chain) + 𝒜 = typeof(zero(ℒ)^2) + # retrieve parameters ϵ = method.ϵ @@ -28,7 +34,7 @@ function simplify(chain::Chain{Dim,T}, method::Selinger) where {Dim,T} # penalty for each possible segment n = length(p) - P = Dict{Tuple{Int,Int},T}() + P = Dict{Tuple{Int,Int},𝒜}() for i in 1:n, o in 1:(n - 2) j = i + o i₊ = i + 1 @@ -38,7 +44,7 @@ function simplify(chain::Chain{Dim,T}, method::Selinger) where {Dim,T} δ = [evaluate(Euclidean(), p[k], l) for k in i₊:j₋] if all(<(ϵ), δ) dᵢⱼ = norm(p[j] - p[i]) - σᵢⱼ = o == 1 ? zero(T) : sqrt(sum(abs2, δ) / length(δ)) + σᵢⱼ = o == 1 ? zero(ℒ) : sqrt(sum(abs2, δ) / length(δ)) P[(i, jₙ)] = dᵢⱼ * σᵢⱼ end end diff --git a/src/sorting/direction.jl b/src/sorting/direction.jl index e5c881add..c9ea03f89 100644 --- a/src/sorting/direction.jl +++ b/src/sorting/direction.jl @@ -7,7 +7,7 @@ Sort geometric objects along a given `direction` vector. """ -struct DirectionSort{V} <: SortingMethod +struct DirectionSort{V<:Vec} <: SortingMethod direction::V end diff --git a/src/subdomains.jl b/src/subdomains.jl index 8e1742a50..1337ede85 100644 --- a/src/subdomains.jl +++ b/src/subdomains.jl @@ -11,7 +11,7 @@ A partial view of a `domain` containing only the elements at `indices`. """ -struct SubDomain{Dim,T,D<:Domain{Dim,T},I<:AbstractVector{Int}} <: Domain{Dim,T} +struct SubDomain{Dim,D<:Domain{Dim},I<:AbstractVector{Int}} <: Domain{Dim} domain::D inds::I end @@ -23,6 +23,8 @@ SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[ # DOMAIN INTERFACE # ----------------- +lentype(::Type{<:SubDomain{Dim,D}}) where {Dim,D} = lentype(D) + element(d::SubDomain, ind::Int) = element(d.domain, d.inds[ind]) nelements(d::SubDomain) = length(d.inds) @@ -70,10 +72,10 @@ Base.parentindices(d::SubDomain) = d.inds # IO METHODS # ----------- -function Base.summary(io::IO, d::SubDomain{Dim,T}) where {Dim,T} +function Base.summary(io::IO, d::SubDomain) name = prettyname(d.domain) nelm = length(d.inds) - print(io, "$nelm view(::$name{$Dim,$T}, ") + print(io, "$nelm view(::$name, ") printinds(io, d.inds) print(io, ")") end diff --git a/src/tolerances.jl b/src/tolerances.jl index 6025c455d..c6698859d 100644 --- a/src/tolerances.jl +++ b/src/tolerances.jl @@ -4,6 +4,7 @@ """ atol(T) + atol(x::T) Absolute tolerance used in algorithms for approximate comparison with numbers of type `T`. It is used in the @@ -13,6 +14,9 @@ source code in calls to the [`isapprox`](@ref) function: isapprox(a::T, b::T, atol=atol(T)) ``` """ -atol(::Type{Float64}) = 1e-10 +atol(x) = atol(typeof(x)) +atol(::Type{Float64}) = 1.0e-10 atol(::Type{Float32}) = 1.0f-5 -atol(::Type{Q}) where {Q<:AbstractQuantity} = atol(numtype(Q)) * unit(Q) +atol(ℒ::Type{<:Len}) = atol(numtype(ℒ)) * unit(ℒ) +atol(𝒜::Type{<:Area}) = atol(numtype(𝒜))^2 * unit(𝒜) +atol(𝒱::Type{<:Vol}) = atol(numtype(𝒱))^3 * unit(𝒱) diff --git a/src/trajecs.jl b/src/trajecs.jl index 94197e2f6..9b21638c1 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -7,26 +7,38 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. """ -struct CylindricalTrajectory{T} <: Domain{3,T} - centroids::Vector{Point{3,T}} - radius::T +struct CylindricalTrajectory{P<:Point{3},ℒ<:Len} <: Domain{3} + centroids::Vector{P} + radius::ℒ + CylindricalTrajectory{P,ℒ}(centroids, radius) where {P<:Point{3},ℒ<:Len} = new(centroids, radius) end -CylindricalTrajectory(centroids::AbstractVector{Point{3,T}}, radius) where {T} = - CylindricalTrajectory(centroids, T(radius)) +CylindricalTrajectory(centroids::Vector{P}, radius::ℒ) where {P<:Point{3},ℒ<:Len} = + CylindricalTrajectory{P,float(ℒ)}(centroids, radius) -CylindricalTrajectory(centroids) = CylindricalTrajectory(centroids, 1) +CylindricalTrajectory(centroids, radius::Len) = CylindricalTrajectory(collect(centroids), radius) + +CylindricalTrajectory(centroids, radius) = CylindricalTrajectory(centroids, addunit(radius, u"m")) + +CylindricalTrajectory(centroids::Vector{P}) where {P<:Point{3}} = CylindricalTrajectory(centroids, oneunit(lentype(P))) + +CylindricalTrajectory(centroids) = CylindricalTrajectory(collect(centroids)) + +lentype(::Type{<:CylindricalTrajectory{P}}) where {P} = lentype(P) topology(t::CylindricalTrajectory) = GridTopology(length(t.centroids)) -function element(t::CylindricalTrajectory{T}, ind::Int) where {T} +function element(t::CylindricalTrajectory, ind::Int) + ℒ = lentype(t) + T = numtype(ℒ) + u = unit(ℒ) c = t.centroids r = t.radius n = length(c) if n == 1 # single vertical cylinder - p₁ = c[1] - Vec{3,T}(0, 0, 0.5) - p₂ = c[1] + Vec{3,T}(0, 0, 0.5) + p₁ = c[1] - Vec(T(0) * u, T(0) * u, T(0.5) * u) + p₂ = c[1] + Vec(T(0) * u, T(0) * u, T(0.5) * u) return Cylinder(p₁, p₂, r) end diff --git a/src/transforms.jl b/src/transforms.jl index bad0b5b58..419a8773e 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -69,6 +69,9 @@ applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} = # stop recursion at non-geometric types applycoord(::CoordinateTransform, x) = x +# special treatment for Point +applycoord(t::CoordinateTransform, p::Point) = Point(applycoord(t, coordinates(p))) + # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index f4c3c351a..5ccdd16cd 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -15,11 +15,18 @@ Affine(Angle2d(π / 2), SVector(2, -2)) Affine([0 -1; 1 0], [-2, 2]) ``` """ -struct Affine{Dim,M<:StaticMatrix{Dim,Dim},V<:StaticVector{Dim}} <: CoordinateTransform +struct Affine{Dim,M<:StaticMatrix{Dim,Dim},V<:StaticVector{Dim,<:Len}} <: CoordinateTransform A::M b::V + function Affine(A::StaticMatrix{Dim,Dim}, b::StaticVector{Dim,<:Len}) where {Dim} + fA = float(A) + fb = float(b) + new{Dim,typeof(fA),typeof(fb)}(fA, fb) + end end +Affine(A::StaticMatrix{Dim,Dim}, b::StaticVector{Dim}) where {Dim} = Affine(A, addunit(b, u"m")) + function Affine(A::AbstractMatrix, b::AbstractVector) sz = size(A) if !allequal(sz) @@ -38,10 +45,7 @@ isaffine(::Type{<:Affine}) = true isrevertible(t::Affine) = isinvertible(t) -function isinvertible(t::Affine) - d = det(t.A) - !isapprox(d, zero(d), atol=atol(typeof(d))) -end +isinvertible(t::Affine) = !isapproxzero(det(t.A)) function inverse(t::Affine) A = inv(t.A) diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index d192178a8..467940a6a 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -13,20 +13,25 @@ via bridges of given width `δ` as described in Held 1998. * Held. 1998. [FIST: Fast Industrial-Strength Triangulation of Polygons] (https://link.springer.com/article/10.1007/s00453-001-0028-4) """ -struct Bridge{T} <: GeometricTransform - δ::T +struct Bridge{ℒ<:Len} <: GeometricTransform + δ::ℒ + Bridge(δ::ℒ) where {ℒ<:Len} = new{float(ℒ)}(δ) end -Bridge() = Bridge(0) +Bridge(δ) = Bridge(addunit(δ, u"m")) + +Bridge() = Bridge(0.0u"m") parameters(t::Bridge) = (; δ=t.δ) -function apply(transform::Bridge, poly::PolyArea{Dim,T}) where {Dim,T} +function apply(transform::Bridge, poly::PolyArea) + ℒ = lentype(poly) + # sort rings lexicographically rpoly, rinds = apply(Repair{9}(), poly) # retrieve bridge width - δ = T(transform.δ) + δ = convert(ℒ, transform.δ) ring, dups = if hasholes(rpoly) bridge(rings(rpoly), rinds, δ) @@ -45,7 +50,7 @@ function bridge(rings, rinds, δ) vinds = rinds # retrieve coordinate type - T = coordtype(first(rings)) + ℒ = lentype(first(rings)) # initialize outer boundary outer = verts[1] @@ -60,7 +65,7 @@ function bridge(rings, rinds, δ) # connecting outer and inner rings omax = 0 imax = 0 - dmin = typemax(T) + dmin = typemax(ℒ) for jₒ in 1:length(outer), jᵢ in 1:length(inner) d = sum(abs, outer[jₒ] - inner[jᵢ]) if d < dmin @@ -75,15 +80,15 @@ function bridge(rings, rinds, δ) # direction and normal to segment A--B v = B - A u = Vec(-v[2], v[1]) - n = u / norm(u) + n = norm(u) # the point A is split into A′ and A′′ and # the point B is split into B′ and B′′ based # on a given bridge width δ - A′ = A + (δ / 2) * n - A′′ = A - (δ / 2) * n - B′ = B + (δ / 2) * n - B′′ = B - (δ / 2) * n + A′ = A + (δ / 2n) * u + A′′ = A - (δ / 2n) * u + B′ = B + (δ / 2n) * u + B′′ = B - (δ / 2n) * u # insert hole at closest vertex outer = [ diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 086bc8e0f..33cbaac7f 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -99,12 +99,13 @@ repair8(v) = repair8(collect(v)) repair8(v::AbstractVector) = repair8(CircularVector(v)) -function repair8(v::CircularVector{Point{Dim,T}}) where {Dim,T} +function repair8(v::CircularVector{<:Point}) n = length(v) keep = Int[] for i in 1:n t = Triangle(v[i - 1], v[i], v[i + 1]) - area(t) > atol(T)^2 && push!(keep, i) + a = area(t) + a > atol(a) && push!(keep, i) end isempty(keep) ? v[begin] : v[keep] end @@ -167,4 +168,7 @@ apply(::Repair{10}, poly::Ngon) = poly, nothing revert(::Repair{10}, poly::Ngon, cache) = poly -_stretch10(g::Geometry{Dim,T}) where {Dim,T} = Stretch(ntuple(i -> T(1) + 10atol(T), Dim)) +function _stretch10(g::Geometry{Dim}) where {Dim} + T = numtype(lentype(g)) + Stretch(ntuple(i -> one(T) + 10atol(T), Dim)) +end diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 45414ca48..cdcfd37e1 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -33,7 +33,7 @@ vector `u` to the plane passing through the origin with normal vector `v`. Rotate(Vec(1, 0, 0), Vec(1, 1, 1)) ``` """ -Rotate(u::Vec, v::Vec) = Rotate(rotation_between(u, v)) +Rotate(u::Vec, v::Vec) = Rotate(urotbetween(u, v)) Rotate(u::Tuple, v::Tuple) = Rotate(Vec(u), Vec(v)) @@ -61,7 +61,7 @@ isinvertible(::Type{<:Rotate}) = true inverse(t::Rotate) = Rotate(inv(t.rot)) -applycoord(t::Rotate, v::Vec) = t.rot * v +applycoord(t::Rotate, v::Vec) = urotapply(t.rot, v) # -------------- # SPECIAL CASES diff --git a/src/transforms/stdcoords.jl b/src/transforms/stdcoords.jl index c01ac39b7..aabd29281 100644 --- a/src/transforms/stdcoords.jl +++ b/src/transforms/stdcoords.jl @@ -35,6 +35,6 @@ reapply(t::StdCoords, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) function _stdcoords(t, g) b = boundingbox(g) t = Translate(coordinates(center(b))...) - s = Scale(sides(b)) + s = Scale(ustrip.(sides(b))) inverse(t) → inverse(s) end diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index b48b7de77..334d5ff00 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -8,10 +8,15 @@ Translate coordinates of geometry or mesh by given offsets `o₁, o₂, ...`. """ -struct Translate{Dim,T} <: CoordinateTransform - offsets::NTuple{Dim,T} +struct Translate{Dim,ℒ<:Len} <: CoordinateTransform + offsets::NTuple{Dim,ℒ} + Translate(offsets::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} = new{Dim,float(ℒ)}(offsets) end +Translate(offsets::NTuple{Dim,Len}) where {Dim} = Translate(promote(offsets...)) + +Translate(offsets::Tuple) = Translate(addunit.(offsets, u"m")) + Translate(offsets...) = Translate(offsets) parameters(t::Translate) = (; offsets=t.offsets) diff --git a/src/traversing/source.jl b/src/traversing/source.jl index 47cdf821f..106599545 100644 --- a/src/traversing/source.jl +++ b/src/traversing/source.jl @@ -23,7 +23,8 @@ function traverse(domain, path::SourcePath) @assert length(sources) ≤ nelements(domain) "more sources than points in object" # fit search tree - kdtree = KDTree(coordinates.([centroid(domain, s) for s in sources])) + xs = [ustrip.(coordinates(centroid(domain, s))) for s in sources] + kdtree = KDTree(xs) # other locations that are not sources others = setdiff(1:nelements(domain), sources) @@ -34,7 +35,7 @@ function traverse(domain, path::SourcePath) # compute distances to sources dists = [] for batch in batches - coords = coordinates.([centroid(domain, b) for b in batch]) + coords = [ustrip.(coordinates(centroid(domain, b))) for b in batch] _, ds = knn(kdtree, coords, length(sources), true) append!(dists, ds) end diff --git a/src/units.jl b/src/units.jl new file mode 100644 index 000000000..87a63afd3 --- /dev/null +++ b/src/units.jl @@ -0,0 +1,19 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +# helper type alias +const Len{T} = Quantity{T,u"𝐋"} +const Area{T} = Quantity{T,u"𝐋^2"} +const Vol{T} = Quantity{T,u"𝐋^3"} +const Met{T} = Quantity{T,u"𝐋",typeof(u"m")} + +""" + addunit(x, u) + +Adds the unit only if the argument is not a quantity, otherwise an error is thrown. +""" +addunit(x::Number, u) = x * u +addunit(x::AbstractArray{<:Number}, u) = x * u +addunit(::Quantity, _) = throw(ArgumentError("invalid units, please check the documentation")) +addunit(::AbstractArray{<:Quantity}, _) = throw(ArgumentError("invalid units, please check the documentation")) diff --git a/src/utils.jl b/src/utils.jl index de171601f..a887a3e77 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -49,11 +49,11 @@ such that `u`, `v`, and `n` form a right-hand orthogonal system. to find orthogonal vectors based on the Householder transformation"] (https://doi.org/10.1016/j.cad.2012.11.003) """ -function householderbasis(n::Vec{3,T}) where {T} +function householderbasis(n::Vec{3,ℒ}) where {ℒ} n̂ = norm(n) i = argmax(n .+ n̂) - eᵢ = Vec(ntuple(j -> j == i ? T(1) : T(0), 3)) - h = n + n̂ * eᵢ + n̂ᵢ = Vec(ntuple(j -> j == i ? n̂ : zero(ℒ), 3)) + h = n + n̂ᵢ H = I - 2h * transpose(h) / (transpose(h) * h) u, v = [H[:, j] for j in 1:3 if j != i] i == 2 && ((u, v) = (v, u)) @@ -68,15 +68,16 @@ using the singular value decomposition (SVD). See . """ -function svdbasis(p::AbstractVector{Point{3,T}}) where {T} +function svdbasis(p::AbstractVector{<:Point{3}}) + ℒ = lentype(eltype(p)) X = reduce(hcat, coordinates.(p)) μ = sum(X, dims=2) / size(X, 2) Z = X .- μ - U = svd(Z).U + U = usvd(Z).U u = Vec(U[:, 1]...) v = Vec(U[:, 2]...) - n = Vec{3,T}(0, 0, 1) - (u × v) ⋅ n < 0 ? (v, u) : (u, v) + n = Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)) + (u × v) ⋅ n < zero(ℒ)^3 ? (v, u) : (u, v) end """ @@ -109,9 +110,10 @@ calculated in order to identify the intersection type: - No intersection and parallel: r == 1, rₐ == 2 - No intersection, skew lines: r == 2, rₐ == 3 """ -function intersectparameters(a::Point{Dim,T}, b::Point{Dim,T}, c::Point{Dim,T}, d::Point{Dim,T}) where {Dim,T} - A = [(b - a) (c - d)] - y = c - a +function intersectparameters(a::Point{Dim}, b::Point{Dim}, c::Point{Dim}, d::Point{Dim}) where {Dim} + A = ustrip.([(b - a) (c - d)]) + y = ustrip.(c - a) + T = eltype(A) # calculate the rank of the augmented matrix by checking # the zero entries of the diagonal of R @@ -154,3 +156,30 @@ Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. end Expr(:tuple, exprs...) end + +isapproxequal(x, y) = isapprox(x, y, atol=atol(x)) +isapproxzero(x) = isapprox(x, zero(x), atol=atol(x)) +isapproxone(x) = isapprox(x, oneunit(x), atol=atol(x)) + +# Function wrappers that handle units +# The result units of some operations, such as dot and cross, +# are treated in a special way to handle Meshes.jl use cases + +function usvd(A) + u = unit(eltype(A)) + F = svd(ustrip.(A)) + SVD(F.U * u, F.S * u, F.Vt * u) +end + +uinv(A) = inv(ustrip.(A)) * unit(eltype(A))^-1 + +unormalize(a::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(normalize(a) * unit(ℒ)) + +udot(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = ustrip(a ⋅ b) * unit(ℒ) + +ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b) * unit(ℒ)) +ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}, c::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b × c) * unit(ℒ)) + +urotbetween(u::Vec, v::Vec) = rotation_between(ustrip.(u), ustrip.(v)) + +urotapply(R::Rotation, v::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(R * ustrip.(v) * unit(ℒ)) diff --git a/src/vectors.jl b/src/vectors.jl index d5bfc9596..aeb1f968d 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -2,15 +2,12 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -const Continuous = Union{AbstractFloat,Quantity{<:AbstractFloat}} - """ Vec(x₁, x₂, ..., xₙ) Vec((x₁, x₂, ..., xₙ)) - Vec{Dim,T}(x₁, x₂, ..., xₙ) - Vec{Dim,T}((x₁, x₂, ..., xₙ)) -A geometric vector in `Dim`-dimensional space with coordinates of type `T` for linear algebra. +A geometric vector in `Dim`-dimensional space with coordinates +in length units (default to meters) for linear algebra. By default, integer coordinates are converted to float. @@ -24,55 +21,32 @@ B = Point(1.0, 0.0) v = B - A # 2D vectors -Vec(0.0, 1.0) # double precision as expected -Vec(0f0, 1f0) # single precision as expected -Vec(0, 0) # integer is converted to float by design -Vec2(0, 1) # explicitly ask for double precision -Vec2f(0, 1) # explicitly ask for single precision +Vec(1.0, 2.0) # add default units +Vec(1.0u"m", 2.0u"m") # double precision as expected +Vec(1f0u"km", 2f0u"km") # single precision as expected +Vec(1u"m", 2u"m") # integer is converted to float by design # 3D vectors -Vec(1.0, 2.0, 3.0) # double precision as expected -Vec(1f0, 2f0, 3f0) # single precision as expected -Vec(1, 2, 3) # integer is converted to float by design -Vec3(1, 2, 3) # explicitly ask for double precision -Vec3f(1, 2, 3) # explicitly ask for single precision +Vec(1.0, 2.0, 3.0) # add default units +Vec(1.0u"m", 2.0u"m", 3.0u"m") # double precision as expected +Vec(1f0u"km", 2f0u"km", 3f0u"km") # single precision as expected +Vec(1u"m", 2u"m", 3u"m") # integer is converted to float by design ``` ### Notes - A `Vec` is a subtype of `StaticVector` from StaticArrays.jl -- Type aliases are `Vec1`, `Vec2`, `Vec3`, `Vec1f`, `Vec2f`, `Vec3f` """ -struct Vec{Dim,T<:Continuous} <: StaticVector{Dim,T} - coords::NTuple{Dim,T} - Vec{Dim,T}(coords::NTuple{Dim,T}) where {Dim,T<:Continuous} = new(coords) +struct Vec{Dim,ℒ<:Len} <: StaticVector{Dim,ℒ} + coords::NTuple{Dim,ℒ} + Vec{Dim,ℒ}(coords::NTuple{Dim}) where {Dim,ℒ<:Len} = new(coords) end -# convenience constructors -Vec{Dim,T}(coords...) where {Dim,T<:Continuous} = Vec{Dim,T}(coords) -function Vec{Dim,T}(coords::Union{Tuple,AbstractVector}) where {Dim,T<:Continuous} - if Dim ≠ length(coords) - throw(DimensionMismatch("the number of coordinates must be equal to the number of dimensions")) - end - Vec{Dim,T}(NTuple{Dim,T}(coords)) -end - -Vec(coords...) = Vec(coords) -Vec(coords::Tuple) = Vec(promote(coords...)) -Vec(coords::NTuple{Dim,T}) where {Dim,T} = Vec(float.(coords)) -Vec(coords::NTuple{Dim,T}) where {Dim,T<:Continuous} = Vec{Dim,T}(coords) - -# StaticVector constructors -Vec(coords::StaticVector{Dim,T}) where {Dim,T<:Continuous} = Vec{Dim,T}(coords) -Vec{Dim,T}(coords::StaticVector) where {Dim,T<:Continuous} = Vec{Dim,T}(Tuple(coords)) +Vec(coords::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} = Vec{Dim,float(ℒ)}(coords) +Vec(coords::NTuple{Dim,Len}) where {Dim} = Vec(promote(coords...)) +Vec(coords::NTuple{Dim,Number}) where {Dim} = Vec(addunit.(coords, u"m")) -# type aliases for convenience -const Vec1 = Vec{1,Float64} -const Vec2 = Vec{2,Float64} -const Vec3 = Vec{3,Float64} -const Vec1f = Vec{1,Float32} -const Vec2f = Vec{2,Float32} -const Vec3f = Vec{3,Float32} +Vec(coords::Number...) = Vec(coords) # StaticVector interface Base.Tuple(v::Vec) = getfield(v, :coords) @@ -80,7 +54,7 @@ Base.getindex(v::Vec, i::Int) = getindex(getfield(v, :coords), i) function StaticArrays.similar_type(::Type{<:Vec}, ::Type{T}, ::Size{S}) where {T,S} L = prod(S) N = length(S) - isone(N) && T <: Continuous ? Vec{L,T} : SArray{Tuple{S...},T,N,L} + isone(N) && T <: Len ? Vec{L,T} : SArray{Tuple{S...},T,N,L} end """ @@ -100,8 +74,7 @@ See . """ function ∠(u::Vec{2}, v::Vec{2}) # preserve sign θ = atan(u × v, u ⋅ v) - T = typeof(θ) - θ == -T(π) ? -θ : θ + θ == oftype(θ, -π) ? -θ : θ end ∠(u::Vec{3}, v::Vec{3}) = atan(norm(u × v), u ⋅ v) # discard sign diff --git a/src/winding.jl b/src/winding.jl index d9b5f72bc..e1ad0549f 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -18,19 +18,22 @@ Generalized winding number of `points` with respect to the geometric `object`. """ function winding end -function winding(points, ring::Ring{2,T}) where {T} +function winding(points, ring::Ring{2}) v = vertices(ring) n = nvertices(ring) - w(p) = sum(∠(v[i], p, v[i + 1]) for i in 1:n) / T(2π) + function w(p) + ∑ = sum(∠(v[i], p, v[i + 1]) for i in 1:n) + ∑ / oftype(∑, 2π) + end tcollect(w(p) for p in points) end -winding(point::Point{2,T}, ring::Ring{2,T}) where {T} = winding((point,), ring) |> first +winding(point::Point{2}, ring::Ring{2}) = winding((point,), ring) |> first # Jacobson et al 2013. -function winding(points, mesh::Mesh{3,T}) where {T} +function winding(points, mesh::Mesh{3}) @assert paramdim(mesh) == 2 "winding number only defined for surface meshes" (eltype(mesh) <: Triangle) || return winding(points, simplexify(mesh)) @@ -47,10 +50,10 @@ function winding(points, mesh::Mesh{3,T}) where {T} d = a * b * c + (a⃗ ⋅ b⃗) * c + (b⃗ ⋅ c⃗) * a + (c⃗ ⋅ a⃗) * b 2atan(n, d) end - ∑ / T(4π) + ∑ / oftype(∑, 4π) end tcollect(w(p) for p in points) end -winding(point::Point{3,T}, mesh::Mesh{3,T}) where {T} = winding((point,), mesh) |> first +winding(point::Point{3}, mesh::Mesh{3}) = winding((point,), mesh) |> first diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 6981115de..924b965b2 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -1,121 +1,121 @@ @testset "Bounding boxes" begin - p = P2(0, 0) + p = point(0, 0) @test boundingbox(p) == Box(p, p) @test @allocated(boundingbox(p)) < 50 - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) @test boundingbox(b) == b @test @allocated(boundingbox(b)) < 50 - r = Ray(P2(0, 0), V2(1, 0)) - @test boundingbox(r) == Box(P2(0, 0), P2(T(Inf), 0)) + r = Ray(point(0, 0), vector(1, 0)) + @test boundingbox(r) == Box(point(0, 0), point(T(Inf), 0)) @test @allocated(boundingbox(r)) < 50 - r = Ray(P2(1, 1), V2(0, 1)) - @test boundingbox(r) == Box(P2(1, 1), P2(1, T(Inf))) + r = Ray(point(1, 1), vector(0, 1)) + @test boundingbox(r) == Box(point(1, 1), point(1, T(Inf))) @test @allocated(boundingbox(r)) < 50 - r = Ray(P2(1, 1), V2(-1, -1)) - @test boundingbox(r) == Box(P2(T(-Inf), T(-Inf)), P2(1, 1)) + r = Ray(point(1, 1), vector(-1, -1)) + @test boundingbox(r) == Box(point(T(-Inf), T(-Inf)), point(1, 1)) @test @allocated(boundingbox(r)) < 50 - r = Ray(P2(-1, 1), V2(1, -1)) - @test boundingbox(r) == Box(P2(-1, T(-Inf)), P2(T(Inf), 1)) + r = Ray(point(-1, 1), vector(1, -1)) + @test boundingbox(r) == Box(point(-1, T(-Inf)), point(T(Inf), 1)) @test @allocated(boundingbox(r)) < 50 - b = Ball(P2(0, 0), T(1)) - @test boundingbox(b) == Box(P2(-1, -1), P2(1, 1)) + b = Ball(point(0, 0), T(1)) + @test boundingbox(b) == Box(point(-1, -1), point(1, 1)) @test @allocated(boundingbox(b)) < 50 - b = Ball(P2(1, 1), T(1)) - @test boundingbox(b) == Box(P2(0, 0), P2(2, 2)) + b = Ball(point(1, 1), T(1)) + @test boundingbox(b) == Box(point(0, 0), point(2, 2)) @test @allocated(boundingbox(b)) < 50 - s = Sphere(P2(0, 0), T(1)) - @test boundingbox(s) == Box(P2(-1, -1), P2(1, 1)) + s = Sphere(point(0, 0), T(1)) + @test boundingbox(s) == Box(point(-1, -1), point(1, 1)) @test @allocated(boundingbox(s)) < 50 - s = Sphere(P2(1, 1), T(1)) - @test boundingbox(s) == Box(P2(0, 0), P2(2, 2)) + s = Sphere(point(1, 1), T(1)) + @test boundingbox(s) == Box(point(0, 0), point(2, 2)) @test @allocated(boundingbox(s)) < 50 c = Cylinder(T(1)) b = boundingbox(c) - @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + @test b == Box(point(-1, -1, 0), point(1, 1, 1)) c = CylinderSurface(T(1)) b = boundingbox(c) - @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + @test b == Box(point(-1, -1, 0), point(1, 1, 1)) - c = Cone(Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)), P3(0, 0, 1)) + c = Cone(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) b = boundingbox(c) - @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + @test b == Box(point(-1, -1, 0), point(1, 1, 1)) - c = ConeSurface(Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)), P3(0, 0, 1)) + c = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) b = boundingbox(c) - @test b == Box(P3(-1, -1, 0), P3(1, 1, 1)) + @test b == Box(point(-1, -1, 0), point(1, 1, 1)) - b = Box(P2(-3, -1), P2(0.5, 0.5)) - s = Sphere(P2(0, 0), T(2)) + b = Box(point(-3, -1), point(0.5, 0.5)) + s = Sphere(point(0, 0), T(2)) m = Multi([b, s]) d = GeometrySet([b, s]) - @test boundingbox(m) == Box(P2(-3, -2), P2(2, 2)) - @test boundingbox(d) == Box(P2(-3, -2), P2(2, 2)) + @test boundingbox(m) == Box(point(-3, -2), point(2, 2)) + @test boundingbox(d) == Box(point(-3, -2), point(2, 2)) @test @allocated(boundingbox(m)) < 2500 @test @allocated(boundingbox(d)) < 2500 - b1 = Box(P2(0, 0), P2(1, 1)) - b2 = Box(P2(-1, -1), P2(0.5, 0.5)) + b1 = Box(point(0, 0), point(1, 1)) + b2 = Box(point(-1, -1), point(0.5, 0.5)) m = Multi([b1, b2]) d = GeometrySet([b1, b2]) - @test boundingbox(m) == Box(P2(-1, -1), P2(1, 1)) - @test boundingbox(d) == Box(P2(-1, -1), P2(1, 1)) + @test boundingbox(m) == Box(point(-1, -1), point(1, 1)) + @test boundingbox(d) == Box(point(-1, -1), point(1, 1)) @test @allocated(boundingbox(m)) < 50 @test @allocated(boundingbox(d)) < 50 d = PointSet(T[0 1 2; 0 2 1]) - @test boundingbox(d) == Box(P2(0, 0), P2(2, 2)) + @test boundingbox(d) == Box(point(0, 0), point(2, 2)) @test @allocated(boundingbox(d)) < 50 d = PointSet(T[1 2; 2 1]) - @test boundingbox(d) == Box(P2(1, 1), P2(2, 2)) + @test boundingbox(d) == Box(point(1, 1), point(2, 2)) @test @allocated(boundingbox(d)) < 50 - d = CartesianGrid{T}(10, 10) - @test boundingbox(d) == Box(P2(0, 0), P2(10, 10)) + d = cartgrid(10, 10) + @test boundingbox(d) == Box(point(0, 0), point(10, 10)) @test @allocated(boundingbox(d)) < 50 - d = CartesianGrid{T}(100, 200) - @test boundingbox(d) == Box(P2(0, 0), P2(100, 200)) + d = cartgrid(100, 200) + @test boundingbox(d) == Box(point(0, 0), point(100, 200)) @test @allocated(boundingbox(d)) < 50 d = CartesianGrid((10, 10), T.((1, 1)), T.((1, 1))) - @test boundingbox(d) == Box(P2(1, 1), P2(11, 11)) + @test boundingbox(d) == Box(point(1, 1), point(11, 11)) @test @allocated(boundingbox(d)) < 50 d = PointSet(T[0 1 2; 0 2 1]) v = view(d, 1:2) - @test boundingbox(v) == Box(P2(0, 0), P2(1, 2)) + @test boundingbox(v) == Box(point(0, 0), point(1, 2)) @test @allocated(boundingbox(v)) < 50 - d = CartesianGrid{T}(10, 10) + d = cartgrid(10, 10) v = view(d, 1:2) - @test boundingbox(v) == Box(P2(0, 0), P2(2, 1)) + @test boundingbox(v) == Box(point(0, 0), point(2, 1)) @test @allocated(boundingbox(v)) < 9000 - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) d = convert(RectilinearGrid, g) - @test boundingbox(d) == Box(P2(0, 0), P2(10, 10)) + @test boundingbox(d) == Box(point(0, 0), point(10, 10)) @test @allocated(boundingbox(d)) < 50 - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) d = TransformedGrid(g, Rotate(T(π / 2))) - @test boundingbox(d) ≈ Box(P2(-10, 0), P2(0, 10)) - @test @allocated(boundingbox(d)) < 2300 + @test boundingbox(d) ≈ Box(point(-10, 0), point(0, 10)) + @test @allocated(boundingbox(d)) < 3000 - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) rg = convert(RectilinearGrid, g) d = TransformedGrid(rg, Rotate(T(π / 2))) - @test boundingbox(d) ≈ Box(P2(-10, 0), P2(0, 10)) - @test @allocated(boundingbox(d)) < 2300 + @test boundingbox(d) ≈ Box(point(-10, 0), point(0, 10)) + @test @allocated(boundingbox(d)) < 3000 - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) m = convert(SimpleMesh, g) - @test boundingbox(m) == Box(P2(0, 0), P2(10, 10)) + @test boundingbox(m) == Box(point(0, 0), point(10, 10)) @test @allocated(boundingbox(m)) < 50 - p = ParaboloidSurface{T}(P3(1, 2, 3), T(5), T(4)) - @test boundingbox(p) ≈ Box(P3(-4, -3, 3), P3(6, 7, 73 / 16)) + p = ParaboloidSurface(point(1, 2, 3), T(5), T(4)) + @test boundingbox(p) ≈ Box(point(-4, -3, 3), point(6, 7, 73 / 16)) end diff --git a/test/clamping.jl b/test/clamping.jl index d185d58ff..9e573ebdf 100644 --- a/test/clamping.jl +++ b/test/clamping.jl @@ -1,13 +1,13 @@ @testset "Clamping" begin box = Box((zero(T), zero(T)), (one(T), one(T))) - @test clamp(P2(0.5, 0.5), box) == P2(0.5, 0.5) - @test clamp(P2(-1, 0.5), box) == P2(0, 0.5) - @test clamp(P2(0.5, -1), box) == P2(0.5, 0) - @test clamp(P2(2, 0.5), box) == P2(1, 0.5) - @test clamp(P2(0.5, 2), box) == P2(0.5, 1) - @test clamp(P2(2, 2), box) == P2(1, 1) - @test clamp(P2(-1, -1), box) == P2(0, 0) + @test clamp(point(0.5, 0.5), box) == point(0.5, 0.5) + @test clamp(point(-1, 0.5), box) == point(0, 0.5) + @test clamp(point(0.5, -1), box) == point(0.5, 0) + @test clamp(point(2, 0.5), box) == point(1, 0.5) + @test clamp(point(0.5, 2), box) == point(0.5, 1) + @test clamp(point(2, 2), box) == point(1, 1) + @test clamp(point(-1, -1), box) == point(0, 0) - points = PointSet(P2(0.5, 0.5), P2(-1, 0.5), P2(0.5, 2)) - @test clamp(points, box) == PointSet(P2(0.5, 0.5), P2(0, 0.5), P2(0.5, 1)) + points = PointSet(point(0.5, 0.5), point(-1, 0.5), point(0.5, 2)) + @test clamp(points, box) == PointSet(point(0.5, 0.5), point(0, 0.5), point(0.5, 1)) end diff --git a/test/clipping.jl b/test/clipping.jl index 8377751df..83606de87 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -1,60 +1,79 @@ @testset "Clipping" begin @testset "SutherlandHodgman" begin # triangle - poly = Triangle(P2(6, 2), P2(3, 5), P2(0, 2)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Triangle(point(6, 2), point(3, 5), point(0, 2)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) - @test all(vertices(clipped) .≈ [P2(5, 3), P2(4, 4), P2(2, 4), P2(0, 2), P2(5, 2)]) + @test all(vertices(clipped) .≈ [point(5, 3), point(4, 4), point(2, 4), point(0, 2), point(5, 2)]) # octagon - poly = Octagon(P2(8, -2), P2(8, 5), P2(2, 5), P2(4, 3), P2(6, 3), P2(4, 1), P2(2, 1), P2(2, -2)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = + Octagon(point(8, -2), point(8, 5), point(2, 5), point(4, 3), point(6, 3), point(4, 1), point(2, 1), point(2, -2)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test !issimple(clipped) @test all( - vertices(clipped) .≈ [P2(3, 4), P2(4, 3), P2(5, 3), P2(5, 2), P2(4, 1), P2(2, 1), P2(2, 0), P2(5, 0), P2(5, 4)] + vertices(clipped) .≈ [ + point(3, 4), + point(4, 3), + point(5, 3), + point(5, 2), + point(4, 1), + point(2, 1), + point(2, 0), + point(5, 0), + point(5, 4) + ] ) # inside - poly = Quadrangle(P2(1, 0), P2(1, 1), P2(0, 1), P2(0, 0)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Quadrangle(point(1, 0), point(1, 1), point(0, 1), point(0, 0)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(poly)) # outside - poly = Quadrangle(P2(7, 6), P2(7, 7), P2(6, 7), P2(6, 6)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Quadrangle(point(7, 6), point(7, 7), point(6, 7), point(6, 6)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test isnothing(clipped) # surrounded - poly = Hexagon(P2(0, 2), P2(-2, 2), P2(-2, 0), P2(0, -2), P2(2, -2), P2(2, 0)) - other = Hexagon(P2(1, 0), P2(0, 1), P2(-1, 1), P2(-1, 0), P2(0, -1), P2(1, -1)) + poly = Hexagon(point(0, 2), point(-2, 2), point(-2, 0), point(0, -2), point(2, -2), point(2, 0)) + other = Hexagon(point(1, 0), point(0, 1), point(-1, 1), point(-1, 0), point(0, -1), point(1, -1)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(other)) # PolyArea with box - outer = Ring(P2(8, 0), P2(4, 8), P2(2, 8), P2(-2, 0), P2(0, 0), P2(1, 2), P2(5, 2), P2(6, 0)) - inner = Ring(P2(4, 4), P2(2, 4), P2(3, 6)) + outer = + Ring(point(8, 0), point(4, 8), point(2, 8), point(-2, 0), point(0, 0), point(1, 2), point(5, 2), point(6, 0)) + inner = Ring(point(4, 4), point(2, 4), point(3, 6)) poly = PolyArea([outer, inner]) - other = Box(P2(0, 1), P2(3, 7)) + other = Box(point(0, 1), point(3, 7)) clipped = clip(poly, other, SutherlandHodgman()) crings = rings(clipped) @test !issimple(clipped) @test all( - vertices(crings[1]) .≈ - [P2(1.5, 7.0), P2(0.0, 4.0), P2(0.0, 1.0), P2(0.5, 1.0), P2(1.0, 2.0), P2(3.0, 2.0), P2(3.0, 7.0)] + vertices(crings[1]) .≈ [ + point(1.5, 7.0), + point(0.0, 4.0), + point(0.0, 1.0), + point(0.5, 1.0), + point(1.0, 2.0), + point(3.0, 2.0), + point(3.0, 7.0) + ] ) - @test all(vertices(crings[2]) .≈ [P2(3.0, 4.0), P2(2.0, 4.0), P2(3.0, 6.0)]) + @test all(vertices(crings[2]) .≈ [point(3.0, 4.0), point(2.0, 4.0), point(3.0, 6.0)]) # PolyArea with outer ring outside and inner ring inside - outer = Ring(P2(8, 0), P2(2, 6), P2(-4, 0)) - inner = Ring(P2(1, 3), P2(3, 3), P2(3, 1), P2(1, 1)) + outer = Ring(point(8, 0), point(2, 6), point(-4, 0)) + inner = Ring(point(1, 3), point(3, 3), point(3, 1), point(1, 1)) poly = PolyArea([outer, inner]) - other = Quadrangle(P2(4, 4), P2(0, 4), P2(0, 0), P2(4, 0)) + other = Quadrangle(point(4, 4), point(0, 4), point(0, 0), point(4, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test !issimple(clipped) crings = rings(clipped) @@ -62,16 +81,16 @@ @test all(vertices(crings[2]) .≈ vertices(inner)) # PolyArea with one inner ring inside `other` and another inner ring outside `other` - outer = Ring(P2(6, 4), P2(6, 7), P2(1, 6), P2(1, 1), P2(5, 2)) - inner₁ = Ring(P2(3, 3), P2(3, 4), P2(4, 3)) - inner₂ = Ring(P2(2, 5), P2(2, 6), P2(3, 5)) + outer = Ring(point(6, 4), point(6, 7), point(1, 6), point(1, 1), point(5, 2)) + inner₁ = Ring(point(3, 3), point(3, 4), point(4, 3)) + inner₂ = Ring(point(2, 5), point(2, 6), point(3, 5)) poly = PolyArea([outer, inner₁, inner₂]) - other = PolyArea(Ring(P2(6, 1), P2(7, 2), P2(6, 5), P2(0, 2), P2(1, 1))) + other = PolyArea(Ring(point(6, 1), point(7, 2), point(6, 5), point(0, 2), point(1, 1))) clipped = clip(poly, other, SutherlandHodgman()) crings = rings(clipped) @test !issimple(clipped) @test length(crings) == 2 - @test all(vertices(crings[1]) .≈ [P2(6, 4), P2(6, 5), P2(1, 2.5), P2(1, 1), P2(5, 2)]) - @test all(vertices(crings[2]) .≈ [P2(3.0, 3.0), P2(3.0, 3.5), P2(10 / 3, 11 / 3), P2(4.0, 3.0)]) + @test all(vertices(crings[1]) .≈ [point(6, 4), point(6, 5), point(1, 2.5), point(1, 1), point(5, 2)]) + @test all(vertices(crings[2]) .≈ [point(3.0, 3.0), point(3.0, 3.5), point(10 / 3, 11 / 3), point(4.0, 3.0)]) end end diff --git a/test/coarsening.jl b/test/coarsening.jl index 11e6047ca..145241ebc 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -1,8 +1,8 @@ @testset "Coarsening" begin @testset "RegularCoarsening" begin # 2D grids - grid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(10, 10)) + grid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(10, 10)) @test coarsen(grid, RegularCoarsening(2)) == tgrid rgrid = convert(RectilinearGrid, grid) trgrid = convert(RectilinearGrid, tgrid) @@ -11,12 +11,12 @@ tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid - grid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(P2(0.0, 0.0), P2(10.0, 10.0), dims=(10, 5)) + grid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(10, 5)) @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid # 3D grids - grid = CartesianGrid{T}(100, 100, 100) + grid = cartgrid(100, 100, 100) tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(50, 25, 20)) @test coarsen(grid, RegularCoarsening(2, 4, 5)) == tgrid rgrid = convert(RectilinearGrid, grid) diff --git a/test/complement.jl b/test/complement.jl index 566a23ac2..d8ccc8759 100644 --- a/test/complement.jl +++ b/test/complement.jl @@ -1,33 +1,33 @@ @testset "complement" begin τ = atol(T) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) p = !t r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(P2[(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)]) - @test r[2] == Ring(P2[(0, 0), (1, 1), (1, 0)]) + @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(point.([(0, 0), (1, 1), (1, 0)])) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) p = !q r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(P2[(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)]) - @test r[2] == Ring(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) + @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) - p = PolyArea(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + p = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) n = !p r = rings(n) @test n isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(P2[(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)]) - @test r[2] == Ring(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) + @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) - o = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - i1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - i2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + o = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + i1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + i2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) p = PolyArea([o, i1, i2]) m = !p r = rings(m) @@ -35,23 +35,23 @@ @test length(r) == 4 g = parent(m) @test length(g) == 3 - @test rings(g[1])[1] ≈ Ring(P2[(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)]) - @test rings(g[1])[2] == Ring(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) - @test rings(g[2]) == [Ring(P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])] - @test rings(g[3]) == [Ring(P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])] + @test rings(g[1])[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test rings(g[1])[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test rings(g[2]) == [Ring(point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]))] + @test rings(g[3]) == [Ring(point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]))] - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) p = !b r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(P2[(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)]) - @test r[2] == Ring(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) + @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) - b = Ball(P2(0, 0), T(1)) + b = Ball(point(0, 0), T(1)) p = !b r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(P2[(-1 - τ, -1 - τ), (1 + τ, -1 - τ), (1 + τ, 1 + τ), (-1 - τ, 1 + τ)]) + @test r[1] ≈ Ring(point.([(-1 - τ, -1 - τ), (1 + τ, -1 - τ), (1 + τ, 1 + τ), (-1 - τ, 1 + τ)])) end diff --git a/test/connectivities.jl b/test/connectivities.jl index c615d91ea..7a207987a 100644 --- a/test/connectivities.jl +++ b/test/connectivities.jl @@ -5,7 +5,7 @@ @test paramdim(c) == 2 @test issimplex(c) @test indices(c) == (1, 2, 3) - @test materialize(c, P2[(0, 0), (1, 0), (0, 1)]) == Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + @test materialize(c, point.([(0, 0), (1, 0), (0, 1)])) == Triangle(point(0, 0), point(1, 0), point(0, 1)) # tuple from other collections c = connect(Tuple([1, 2, 3]), Triangle) @@ -13,7 +13,7 @@ @test paramdim(c) == 2 @test issimplex(c) @test indices(c) == (1, 2, 3) - @test materialize(c, P2[(0, 0), (1, 0), (0, 1)]) == Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + @test materialize(c, point.([(0, 0), (1, 0), (0, 1)])) == Triangle(point(0, 0), point(1, 0), point(0, 1)) # incorrect number of vertices for polytope @test_throws AssertionError connect((1, 2, 3, 4), Triangle) @@ -44,6 +44,6 @@ @test paramdim(c) == 3 @test issimplex(c) @test indices(c) == (1, 2, 3, 4) - points = P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)] + points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) @test materialize(c, points) == Tetrahedron(points...) end diff --git a/test/discretization.jl b/test/discretization.jl index a2de49f6f..12cdd2c40 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -1,6 +1,6 @@ @testset "Discretization" begin @testset "FanTriangulation" begin - pts = P2[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)] + pts = point.([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)]) tris = [Triangle(pts[1], pts[i], pts[i + 1]) for i in 2:(length(pts) - 1)] hex = Hexagon(pts...) mesh = discretize(hex, FanTriangulation()) @@ -12,14 +12,14 @@ end @testset "RegularDiscretization" begin - bezier = BezierCurve([P2(0, 0), P2(1, 0), P2(1, 1)]) + bezier = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) mesh = discretize(bezier, RegularDiscretization(10)) @test nvertices(mesh) == 11 @test nelements(mesh) == 10 @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] - box = Box(P2(0, 0), P2(2, 2)) + box = Box(point(0, 0), point(2, 2)) mesh = discretize(box, RegularDiscretization(10)) @test mesh isa CartesianGrid @test nvertices(mesh) == 121 @@ -27,14 +27,14 @@ @test eltype(mesh) <: Quadrangle @test nvertices.(mesh) ⊆ [4] - sphere = Sphere(P2(0, 0), T(1)) + sphere = Sphere(point(0, 0), T(1)) mesh = discretize(sphere, RegularDiscretization(10)) @test nvertices(mesh) == 10 @test nelements(mesh) == 10 @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] - sphere = Sphere(P3(0, 0, 0), T(1)) + sphere = Sphere(point(0, 0, 0), T(1)) mesh = discretize(sphere, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @@ -48,44 +48,44 @@ @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - ball = Ball(P2(0, 0), T(1)) + ball = Ball(point(0, 0), T(1)) mesh = discretize(ball, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 1 @test nelements(mesh) == 10 * 10 + 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - disk = Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)) + disk = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)) mesh = discretize(disk, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 1 @test nelements(mesh) == 10 * 10 + 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - cylsurf = CylinderSurface(Plane(P3(0, 0, 0), V3(0, 0, 1)), Plane(P3(1, 1, 1), V3(0, 0, 1)), T(1)) + cylsurf = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) mesh = discretize(cylsurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * 11 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - consurf = ConeSurface(Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(1)), P3(0, 0, 1)) + consurf = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) mesh = discretize(consurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * 11 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - parsurf = rand(ParaboloidSurface{T}) + parsurf = rand(ParaboloidSurface) mesh = discretize(parsurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * (10 + 1) @test nelements(mesh) == 10 * 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - poly = PolyArea(P2[(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)]) + poly = PolyArea(point.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) mesh = discretize(poly, RegularDiscretization(50)) - @test mesh isa SubGrid{2,T} + @test mesh isa SubGrid{2} grid = parent(mesh) @test grid isa CartesianGrid @test eltype(mesh) <: Quadrangle @@ -94,14 +94,14 @@ @testset "Dehn1899" begin octa = Octagon( - P2(0.2, 0.2), - P2(0.5, -0.5), - P2(0.8, 0.2), - P2(1.5, 0.5), - P2(0.8, 0.8), - P2(0.5, 1.5), - P2(0.2, 0.8), - P2(-0.5, 0.5) + point(0.2, 0.2), + point(0.5, -0.5), + point(0.8, 0.2), + point(1.5, 0.5), + point(0.8, 0.8), + point(0.5, 1.5), + point(0.2, 0.8), + point(-0.5, 0.5) ) mesh = discretize(octa, Dehn1899()) @test nvertices(mesh) == 8 @@ -109,14 +109,14 @@ @test eltype(mesh) <: Triangle octa = Octagon( - P3(0.2, 0.2, 0.0), - P3(0.5, -0.5, 0.0), - P3(0.8, 0.2, 0.0), - P3(1.5, 0.5, 0.0), - P3(0.8, 0.8, 0.0), - P3(0.5, 1.5, 0.0), - P3(0.2, 0.8, 0.0), - P3(-0.5, 0.5, 0.0) + point(0.2, 0.2, 0.0), + point(0.5, -0.5, 0.0), + point(0.8, 0.2, 0.0), + point(1.5, 0.5, 0.0), + point(0.8, 0.8, 0.0), + point(0.5, 1.5, 0.0), + point(0.2, 0.8, 0.0), + point(-0.5, 0.5, 0.0) ) mesh = discretize(octa, Dehn1899()) @test nvertices(mesh) == 8 @@ -125,20 +125,20 @@ end @testset "FIST" begin - 𝒫 = Ring(P2[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) + 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4, 5] - 𝒫 = Ring(P2[(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)]) + 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4] - 𝒫 = Ring(P2[(0, 0), (1, 0), (1, 1), (1, 2)]) + 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4] - 𝒫 = Ring(P2[(0, 0), (1, 1), (1, 2)]) + 𝒫 = Ring(point.([(0, 0), (1, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [] 𝒫 = Ring( - P2[ + point.([ (0.443339268495331, 0.283757618605357), (0.497822414616971, 0.398142813114205), (0.770343126156527, 0.201815462842808), @@ -153,11 +153,11 @@ (0.209843417261654, 0.590030658255966), (0.27993878548962, 0.525162463476181), (0.385557753911967, 0.322338556632868) - ] + ]) ) @test Meshes.earsccw(𝒫) == [1, 3, 5, 6, 8, 10, 12, 14] - points = P2[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)] + points = point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) connec = connect.([(4, 5, 6), (3, 4, 6), (3, 6, 1), (1, 2, 3)], Triangle) target = SimpleMesh(points, connec) poly = PolyArea(points) @@ -168,7 +168,7 @@ # https://github.com/JuliaGeometry/Meshes.jl/issues/675 poly = PolyArea( - P2[ + point.([ (1.1794224993e7, 1.7289506814e7), (1.1794045018e7, 1.7289446822e7), (1.1793985026e7, 1.7289486817e7), @@ -185,7 +185,7 @@ (1.1794624937e7, 1.7289806772e7), (1.1794564946e7, 1.7289706786e7), (1.1794424965e7, 1.7289626797e7) - ] + ]) ) rng = StableRNG(123) mesh = discretize(poly, FIST(rng)) @@ -194,13 +194,13 @@ # https://github.com/JuliaGeometry/Meshes.jl/issues/738 poly = PolyArea( - P2[ + point.([ (-0.5, 0.3296139), (-0.19128194, -0.5), (-0.37872985, 0.29592824), (0.21377224, -0.0076110554), (-0.20127837, 0.24671146) - ] + ]) ) rng = StableRNG(123) mesh = discretize(poly, FIST(rng)) @@ -211,20 +211,20 @@ @testset "Miscellaneous" begin rng = StableRNG(123) for method in [FIST(rng), Dehn1899()] - triangle = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + triangle = Triangle(point(0, 0), point(1, 0), point(0, 1)) mesh = discretize(triangle, method) - @test vertices(mesh) == [P2(0, 0), P2(1, 0), P2(0, 1)] + @test vertices(mesh) == [point(0, 0), point(1, 0), point(0, 1)] @test collect(elements(mesh)) == [triangle] - quadrangle = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + quadrangle = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) mesh = discretize(quadrangle, method) elms = collect(elements(mesh)) - @test vertices(mesh) == [P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)] + @test vertices(mesh) == [point(0, 0), point(1, 0), point(1, 1), point(0, 1)] @test eltype(elms) <: Triangle @test length(elms) == 2 - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - t = Triangle(P2(1, 0), P2(2, 1), P2(1, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + t = Triangle(point(1, 0), point(2, 1), point(1, 1)) m = Multi([q, t]) mesh = discretize(m, method) elms = collect(elements(mesh)) @@ -235,25 +235,25 @@ @test eltype(elms) <: Triangle @test length(elms) == 3 - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) bpoly = poly |> Bridge(T(0.01)) mesh = discretizewithin(boundary(bpoly), method) @test nvertices(mesh) == 16 @test nelements(mesh) == 14 - @test all(t -> area(t) > zero(T), mesh) + @test all(t -> area(t) > zero(ℳ)^2, mesh) # 3D chains - chain = Ring(P3[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)]) + chain = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)])) mesh = discretizewithin(chain, method) @test vertices(mesh) == vertices(chain) @test eltype(mesh) <: Triangle @test nelements(mesh) == 2 # preserves order of vertices - poly = Quadrangle(P3(0, 1, 0), P3(1, 1, 0), P3(1, 0, 0), P3(0, 0, 0)) + poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) mesh = simplexify(poly) @test pointify(mesh) == pointify(poly) end @@ -346,7 +346,7 @@ if T == Float64 poly = PolyArea( - P2[ + point.([ (-48.03012478813999, -18.323912004531923), (-48.030125176275845, -18.323904748608573), (-48.03017873307118, -18.323925747019675), @@ -355,7 +355,7 @@ (-48.03017951837907, -18.323938343610457), (-48.030124261780436, -18.32392184444903), (-48.0301218833633, -18.323910661117687) - ] + ]) ) mesh = discretize(poly) @test nvertices(mesh) == 8 @@ -363,16 +363,16 @@ end # degenerate triangle - poly = PolyArea(P2[(0, 0), (1, 1), (1, 1)]) + poly = PolyArea(point.([(0, 0), (1, 1), (1, 1)])) mesh = discretize(poly) @test nvertices(mesh) == 3 @test nelements(mesh) == 1 - @test vertices(mesh) == [P2(0, 0), P2(0, 0), P2(0, 0)] - @test mesh[1] == Triangle(P2(0, 0), P2(0, 0), P2(0, 0)) + @test vertices(mesh) == [point(0, 0), point(0, 0), point(0, 0)] + @test mesh[1] == Triangle(point(0, 0), point(0, 0), point(0, 0)) end @testset "Tetrahedralization" begin - box = Box(P3(0, 0, 0), P3(1, 1, 1)) + box = Box(point(0, 0, 0), point(1, 1, 1)) hexa = Hexahedron(pointify(box)...) bmesh = discretize(box, Tetrahedralization()) hmesh = discretize(hexa, Tetrahedralization()) @@ -382,13 +382,13 @@ end @testset "Discretize" begin - ball = Ball(P2(0, 0), T(1)) + ball = Ball(point(0, 0), T(1)) mesh = discretize(ball) @test !(eltype(mesh) <: Triangle) @test !(eltype(mesh) <: Quadrangle) @test nelements(mesh) == 2550 - sphere = Sphere(P3(0, 0, 0), T(1)) + sphere = Sphere(point(0, 0, 0), T(1)) mesh = discretize(sphere) @test !(eltype(mesh) <: Triangle) @test !(eltype(mesh) <: Quadrangle) @@ -403,7 +403,7 @@ grid = CartesianGrid(10) @test discretize(grid) == grid - mesh = SimpleMesh(rand(P2, 3), connect.([(1, 2, 3)])) + mesh = SimpleMesh(randpoint2(3), connect.([(1, 2, 3)])) @test discretize(mesh) == mesh end @@ -411,50 +411,50 @@ # simplexify is a helper function that calls an # appropriate discretization method depending on # the geometry type that is given to it - box = Box(P1(0), P1(1)) + box = Box(point(0), point(1)) msh = simplexify(box) @test eltype(msh) <: Segment @test topology(msh) == GridTopology(1) @test nvertices(msh) == 2 @test nelements(msh) == 1 - @test msh[1] == Segment(P1(0), P1(1)) + @test msh[1] == Segment(point(0), point(1)) - seg = Segment(P1(0), P1(1)) + seg = Segment(point(0), point(1)) msh = simplexify(seg) @test eltype(msh) <: Segment @test topology(msh) == GridTopology(1) @test nvertices(msh) == 2 @test nelements(msh) == 1 - @test msh[1] == Segment(P1(0), P1(1)) + @test msh[1] == Segment(point(0), point(1)) - chn = Rope(P2[(0, 0), (1, 0), (1, 1)]) + chn = Rope(point.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(chn) @test eltype(msh) <: Segment @test nvertices(msh) == 3 @test nelements(msh) == 2 - @test msh[1] == Segment(P2(0, 0), P2(1, 0)) - @test msh[2] == Segment(P2(1, 0), P2(1, 1)) - chn = Ring(P2[(0, 0), (1, 0), (1, 1)]) + @test msh[1] == Segment(point(0, 0), point(1, 0)) + @test msh[2] == Segment(point(1, 0), point(1, 1)) + chn = Ring(point.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(chn) @test eltype(msh) <: Segment @test nvertices(msh) == 3 @test nelements(msh) == 3 - @test msh[1] == Segment(P2(0, 0), P2(1, 0)) - @test msh[2] == Segment(P2(1, 0), P2(1, 1)) - @test msh[3] == Segment(P2(1, 1), P2(0, 0)) + @test msh[1] == Segment(point(0, 0), point(1, 0)) + @test msh[2] == Segment(point(1, 0), point(1, 1)) + @test msh[3] == Segment(point(1, 1), point(0, 0)) - sph = Sphere(P2(0, 0), T(1)) + sph = Sphere(point(0, 0), T(1)) msh = simplexify(sph) @test eltype(msh) <: Segment @test nvertices(msh) == nelements(msh) - bez = BezierCurve(P2[(0, 0), (1, 0), (1, 1)]) + bez = BezierCurve(point.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(bez) @test eltype(msh) <: Segment @test nvertices(msh) == nelements(msh) + 1 - box = Box(P2(0, 0), P2(1, 1)) - ngon = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + box = Box(point(0, 0), point(1, 1)) + ngon = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) poly = readpoly(T, joinpath(datadir, "taubin.line")) for geom in [box, ngon, poly] bound = boundary(geom) @@ -464,38 +464,38 @@ end # triangulation of multi geometries - box1 = Box(P2(0, 0), P2(1, 1)) - box2 = Box(P2(1, 1), P2(2, 2)) + box1 = Box(point(0, 0), point(1, 1)) + box2 = Box(point(1, 1), point(2, 2)) multi = Multi([box1, box2]) mesh = simplexify(multi) @test nvertices(mesh) == 8 @test nelements(mesh) == 4 # triangulation of spheres - sphere = Sphere(P3(0, 0, 0), T(1)) + sphere = Sphere(point(0, 0, 0), T(1)) mesh = simplexify(sphere) @test eltype(mesh) <: Triangle xs = coordinates.(vertices(mesh)) - @test all(x -> norm(x) ≈ T(1), xs) + @test all(x -> norm(x) ≈ oneunit(ℳ), xs) # triangulation of cylinder surfaces cylsurf = CylinderSurface(T(1)) mesh = simplexify(cylsurf) @test eltype(mesh) <: Triangle xs = coordinates.(vertices(mesh)) - @test all(x -> T(-1) ≤ x[1] ≤ T(1), xs) - @test all(x -> T(-1) ≤ x[2] ≤ T(1), xs) - @test all(x -> T(0) ≤ x[3] ≤ T(1), xs) + @test all(x -> -oneunit(ℳ) ≤ x[1] ≤ oneunit(ℳ), xs) + @test all(x -> -oneunit(ℳ) ≤ x[2] ≤ oneunit(ℳ), xs) + @test all(x -> zero(ℳ) ≤ x[3] ≤ oneunit(ℳ), xs) # triangulation of balls - ball = Ball(P2(0, 0), T(1)) + ball = Ball(point(0, 0), T(1)) mesh = simplexify(ball) @test eltype(mesh) <: Triangle xs = coordinates.(vertices(mesh)) - @test all(x -> norm(x) ≤ T(1) + eps(T), xs) + @test all(x -> norm(x) ≤ oneunit(ℳ) + eps(T) * u"m", xs) # triangulation of meshes - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) mesh = simplexify(grid) gpts = vertices(grid) mpts = vertices(mesh) @@ -506,12 +506,12 @@ @test measure(mesh) == measure(grid) # https://github.com/JuliaGeometry/Meshes.jl/issues/499 - quad = Quadrangle(P3(0, 1, -1), P3(0, 1, 1), P3(0, -1, 1), P3(0, -1, -1)) + quad = Quadrangle(point(0, 1, -1), point(0, 1, 1), point(0, -1, 1), point(0, -1, -1)) mesh = simplexify(quad) @test vertices(mesh) == pointify(quad) if visualtests - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) mesh = simplexify(grid) fig = Mke.Figure(size=(600, 300)) viz(fig[1, 1], grid, showsegments=true) @@ -520,7 +520,7 @@ end # tetrahedralization - box = Box(P3(0, 0, 0), P3(1, 1, 1)) + box = Box(point(0, 0, 0), point(1, 1, 1)) hex = Hexahedron(pointify(box)...) bmesh = simplexify(box) hmesh = simplexify(hex) diff --git a/test/distances.jl b/test/distances.jl index 5b628e1cc..eeda7b015 100644 --- a/test/distances.jl +++ b/test/distances.jl @@ -1,23 +1,23 @@ @testset "Distances" begin - p = P2(0, 1) - l = Line(P2(0, 0), P2(1, 0)) - @test evaluate(Euclidean(), p, l) == T(1) - @test evaluate(Euclidean(), l, p) == T(1) + p = point(0, 1) + l = Line(point(0, 0), point(1, 0)) + @test evaluate(Euclidean(), p, l) == T(1) * u"m" + @test evaluate(Euclidean(), l, p) == T(1) * u"m" - p1, p2 = P2(1, 0), P2(0, 1) - @test evaluate(Chebyshev(), p1, p2) == T(1) + p1, p2 = point(1, 0), point(0, 1) + @test evaluate(Chebyshev(), p1, p2) == T(1) * u"m" - p = P2(68, 259) - l = Line(P2(68, 260), P2(69, 261)) - @test evaluate(Euclidean(), p, l) ≤ T(0.8) + p = point(68, 259) + l = Line(point(68, 260), point(69, 261)) + @test evaluate(Euclidean(), p, l) ≤ T(0.8) * u"m" - line1 = Line(P3(-1, 0, 0), P3(1, 0, 0)) - line2 = Line(P3(0, -1, 1), P3(0, 1, 1)) # line2 ⟂ line1, z++ - line3 = Line(P3(-1, 1, 0), P3(1, 1, 0)) # line3 ∥ line1 - line4 = Line(P3(-2, 0, 0), P3(2, 0, 0)) # line4 colinear with line1 - line5 = Line(P3(0, -1, 0), P3(0, 1, 0)) # line5 intersects line1 - @test evaluate(Euclidean(), line1, line2) ≈ T(1) - @test evaluate(Euclidean(), line1, line3) ≈ T(1) - @test evaluate(Euclidean(), line1, line4) ≈ T(0) - @test evaluate(Euclidean(), line1, line5) ≈ T(0) + line1 = Line(point(-1, 0, 0), point(1, 0, 0)) + line2 = Line(point(0, -1, 1), point(0, 1, 1)) # line2 ⟂ line1, z++ + line3 = Line(point(-1, 1, 0), point(1, 1, 0)) # line3 ∥ line1 + line4 = Line(point(-2, 0, 0), point(2, 0, 0)) # line4 colinear with line1 + line5 = Line(point(0, -1, 0), point(0, 1, 0)) # line5 intersects line1 + @test evaluate(Euclidean(), line1, line2) ≈ T(1) * u"m" + @test evaluate(Euclidean(), line1, line3) ≈ T(1) * u"m" + @test evaluate(Euclidean(), line1, line4) ≈ T(0) * u"m" + @test evaluate(Euclidean(), line1, line5) ≈ T(0) * u"m" end diff --git a/test/domains.jl b/test/domains.jl index 1e33d0f77..5d56259f6 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -1,39 +1,39 @@ @testset "Domain" begin # basic properties - dom = DummyDomain(P2(0, 0)) + dom = DummyDomain(point(0, 0)) @test embeddim(dom) == 2 - @test coordtype(dom) == T + @test Meshes.lentype(dom) == ℳ @test !isparametrized(dom) # indexable/iterable interface - dom = DummyDomain(P2(0, 0)) - @test dom[begin] == Ball(P2(1, 1), T(1)) - @test dom[end] == Ball(P2(3, 3), T(1)) - @test eltype(dom) <: Ball{2,T} + dom = DummyDomain(point(0, 0)) + @test dom[begin] == Ball(point(1, 1), T(1)) + @test dom[end] == Ball(point(3, 3), T(1)) + @test eltype(dom) <: Ball{2} @test length(dom) == 3 @test keys(dom) == 1:3 - @test collect(dom) == [Ball(P2(i, i), T(1)) for i in 1:3] - @test dom[1:2] == [Ball(P2(i, i), T(1)) for i in 1:2] + @test collect(dom) == [Ball(point(i, i), T(1)) for i in 1:3] + @test dom[1:2] == [Ball(point(i, i), T(1)) for i in 1:2] # coordinates of centroids - dom = DummyDomain(P2(1, 1)) + dom = DummyDomain(point(1, 1)) pts = centroid.(Ref(dom), 1:3) - @test pts == P2[(2, 2), (3, 3), (4, 4)] + @test pts == point.([(2, 2), (3, 3), (4, 4)]) # concatenation - dom1 = DummyDomain(P2(0, 0)) - dom2 = DummyDomain(P2(3, 3)) - dom3 = PointSet(rand(P2, 3)) + dom1 = DummyDomain(point(0, 0)) + dom2 = DummyDomain(point(3, 3)) + dom3 = PointSet(randpoint2(3)) @test vcat(dom1, dom2) == GeometrySet([collect(dom1); collect(dom2)]) @test vcat(dom2, dom3) == GeometrySet([collect(dom2); collect(dom3)]) @test vcat(dom3, dom1) == GeometrySet([collect(dom3); collect(dom1)]) @test vcat(dom1, dom2, dom3) == GeometrySet([collect(dom1); collect(dom2); collect(dom3)]) - dom = DummyDomain(P2(0, 0)) - @test sprint(show, dom) == "3 DummyDomain{2,$T}" + dom = DummyDomain(point(0, 0)) + @test sprint(show, dom) == "3 DummyDomain" @test sprint(show, MIME"text/plain"(), dom) == """ - 3 DummyDomain{2,$T} - ├─ Ball(center: (1.0, 1.0), radius: 1.0) - ├─ Ball(center: (2.0, 2.0), radius: 1.0) - └─ Ball(center: (3.0, 3.0), radius: 1.0)""" + 3 DummyDomain + ├─ Ball(center: (x: 1.0 m, y: 1.0 m), radius: 1.0 m) + ├─ Ball(center: (x: 2.0 m, y: 2.0 m), radius: 1.0 m) + └─ Ball(center: (x: 3.0 m, y: 3.0 m), radius: 1.0 m)""" end diff --git a/test/dummy.jl b/test/dummy.jl index 79711a1c5..01b217851 100644 --- a/test/dummy.jl +++ b/test/dummy.jl @@ -1,10 +1,16 @@ # dummy type implementing the Domain trait -struct DummyDomain{Dim,T} <: Domain{Dim,T} - origin::Point{Dim,T} +struct DummyDomain{Dim,P<:Point{Dim}} <: Domain{Dim} + origin::P end -function Meshes.element(domain::DummyDomain{Dim,T}, ind::Int) where {Dim,T} - c = domain.origin + Vec(ntuple(i -> T(ind), Dim)) - r = one(T) + +Meshes.lentype(::Type{<:DummyDomain{Dim,P}}) where {Dim,P} = Meshes.lentype(P) + +function Meshes.element(domain::DummyDomain{Dim}, ind::Int) where {Dim} + ℒ = Meshes.lentype(domain) + T = Unitful.numtype(ℒ) + c = domain.origin + Vec(ntuple(i -> T(ind) * unit(ℒ), Dim)) + r = oneunit(ℒ) Ball(c, r) end + Meshes.nelements(d::DummyDomain) = 3 diff --git a/test/hulls.jl b/test/hulls.jl index 2d5314378..f9c34be91 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -2,68 +2,69 @@ @testset "Basic" begin for method in [GrahamScan(), JarvisMarch()] # basic test - pts = rand(P2, 100) + pts = randpoint2(100) chul = hull(pts, method) @test all(pts .∈ Ref(chul)) # duplicated points - pts = [rand(P2, 100); rand(P2, 100)] + pts = [randpoint2(100); randpoint2(100)] chul = hull(pts, method) @test all(pts .∈ Ref(chul)) # corner cases - pts = P2[(0, 0)] + pts = point.([(0, 0)]) chul = hull(pts, method) - @test chul == P2(0, 0) - pts = P2[(0, 1), (1, 0)] + @test chul == point(0, 0) + pts = point.([(0, 1), (1, 0)]) chul = hull(pts, method) - @test chul == Segment(P2(0, 1), P2(1, 0)) - pts = P2[(1, 0), (0, 0), (0, 1)] + @test chul == Segment(point(0, 1), point(1, 0)) + pts = point.([(1, 0), (0, 0), (0, 1)]) chul = hull(pts, method) - @test vertices(chul) == P2[(0, 0), (1, 0), (0, 1)] + @test vertices(chul) == point.([(0, 0), (1, 0), (0, 1)]) # original point set is already in hull - pts = P2[(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)] + pts = point.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) chul = hull(pts, method) verts = vertices(chul) - @test verts == P2[(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)] + @test verts == point.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) # random points in interior do not affect result - p1 = P2[(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)] - p2 = P2[0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10] + p1 = point.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) + p2 = point.([0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10]) pts = [p1; p2] chul = hull(pts, method) verts = vertices(chul) - @test verts == P2[(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)] - - pts = P2[ - (0, 5), - (1, 5), - (1, 4), - (2, 4), - (2, 3), - (3, 3), - (4, 3), - (5, 3), - (5, 4), - (6, 4), - (6, 5), - (7, 5), - (7, 6), - (7, 7), - (6, 7), - (6, 8), - (5, 8), - (5, 9), - (4, 9), - (3, 9), - (2, 9), - (2, 8), - (1, 8), - (1, 7), - (0, 7), - (0, 6) - ] + @test verts == point.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) + + pts = + point.([ + (0, 5), + (1, 5), + (1, 4), + (2, 4), + (2, 3), + (3, 3), + (4, 3), + (5, 3), + (5, 4), + (6, 4), + (6, 5), + (7, 5), + (7, 6), + (7, 7), + (6, 7), + (6, 8), + (5, 8), + (5, 9), + (4, 9), + (3, 9), + (2, 9), + (2, 8), + (1, 8), + (1, 7), + (0, 7), + (0, 6) + ]) chul = hull(pts, method) @test nvertices(chul) < length(pts) @@ -74,67 +75,67 @@ if method == GrahamScan() # simplifying rectangular hull / triangular - points = [P2(i - 1, j - 1) for i in 1:11 for j in 1:11] + points = [point(i - 1, j - 1) for i in 1:11 for j in 1:11] chull = hull(points, method) - @test vertices(chull) == [P2(0, 0), P2(10, 0), P2(10, 10), P2(0, 10)] + @test vertices(chull) == [point(0, 0), point(10, 0), point(10, 10), point(0, 10)] for _ in 1:100 # test presence of interior points doesn't affect the result - push!(points, P2(10 * rand(), 10 * rand())) + push!(points, point(10 * rand(), 10 * rand())) end chull = hull(points, method) - @test vertices(chull) == [P2(0, 0), P2(10, 0), P2(10, 10), P2(0, 10)] + @test vertices(chull) == [point(0, 0), point(10, 0), point(10, 10), point(0, 10)] - points = [P2(-1, 0), P2(0, 0), P2(1, 0), P2(0, 2)] + points = [point(-1, 0), point(0, 0), point(1, 0), point(0, 2)] chull = hull(points, method) - @test vertices(chull) == [P2(-1, 0), P2(1, 0), P2(0, 2)] + @test vertices(chull) == [point(-1, 0), point(1, 0), point(0, 2)] # degenerate cases - points = [P2(0, 0), P2(1, 0), P2(2, 0)] + points = [point(0, 0), point(1, 0), point(2, 0)] chull = hull(points, method) - @test vertices(chull) == (P2(0, 0), P2(2, 0)) + @test vertices(chull) == (point(0, 0), point(2, 0)) - points = [P2(0, 0), P2(1, 0), P2(2, 0), P2(10, 0), P2(100, 0)] + points = [point(0, 0), point(1, 0), point(2, 0), point(10, 0), point(100, 0)] chull = hull(points, method) - @test vertices(chull) == (P2(0, 0), P2(100, 0)) + @test vertices(chull) == (point(0, 0), point(100, 0)) # partially collinear points = [ - P2(2, 0), - P2(4, 0), - P2(6, 0), - P2(10, 0), - P2(12, 1), - P2(14, 3), - P2(14, 6), - P2(14, 9), - P2(13, 10), - P2(11, 11), - P2(8, 12), - P2(3, 11), - P2(0, 8), - P2(0, 7), - P2(0, 6), - P2(0, 5), - P2(0, 4), - P2(0, 3), - P2(0, 2), - P2(1, 0) + point(2, 0), + point(4, 0), + point(6, 0), + point(10, 0), + point(12, 1), + point(14, 3), + point(14, 6), + point(14, 9), + point(13, 10), + point(11, 11), + point(8, 12), + point(3, 11), + point(0, 8), + point(0, 7), + point(0, 6), + point(0, 5), + point(0, 4), + point(0, 3), + point(0, 2), + point(1, 0) ] chull = hull(points, method) truth = [ - P2(0, 2), - P2(1, 0), - P2(10, 0), - P2(12, 1), - P2(14, 3), - P2(14, 9), - P2(13, 10), - P2(11, 11), - P2(8, 12), - P2(3, 11), - P2(0, 8) + point(0, 2), + point(1, 0), + point(10, 0), + point(12, 1), + point(14, 3), + point(14, 9), + point(13, 10), + point(11, 11), + point(8, 12), + point(3, 11), + point(0, 8) ] @test vertices(chull) == truth - push!(points, P2(4, 8), P2(2, 6), P2(6, 2), P2(10, 8), P2(8, 8), P2(10, 6)) + push!(points, point(4, 8), point(2, 6), point(6, 2), point(10, 8), point(8, 8), point(10, 6)) chull = hull(points, method) @test vertices(chull) == truth end @@ -142,25 +143,26 @@ end @testset "convexhull" begin - @test convexhull(P2(0, 0)) == P2(0, 0) + @test convexhull(point(0, 0)) == point(0, 0) - @test convexhull(Box(P2(0, 0), P2(1, 1))) == Box(P2(0, 0), P2(1, 1)) + @test convexhull(Box(point(0, 0), point(1, 1))) == Box(point(0, 0), point(1, 1)) - @test convexhull(Ball(P2(0, 0), T(1))) == Ball(P2(0, 0), T(1)) - @test convexhull(Ball(P2(1, 1), T(1))) == Ball(P2(1, 1), T(1)) + @test convexhull(Ball(point(0, 0), T(1))) == Ball(point(0, 0), T(1)) + @test convexhull(Ball(point(1, 1), T(1))) == Ball(point(1, 1), T(1)) - @test convexhull(Sphere(P2(0, 0), T(1))) == Ball(P2(0, 0), T(1)) - @test convexhull(Sphere(P2(1, 1), T(1))) == Ball(P2(1, 1), T(1)) + @test convexhull(Sphere(point(0, 0), T(1))) == Ball(point(0, 0), T(1)) + @test convexhull(Sphere(point(1, 1), T(1))) == Ball(point(1, 1), T(1)) - b1 = Box(P2(0, 0), P2(1, 1)) - b2 = Box(P2(-1, -1), P2(0.5, 0.5)) - @test convexhull(Multi([b1, b2])) == PolyArea(P2[(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)]) - @test convexhull(GeometrySet([b1, b2])) == PolyArea(P2[(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)]) + b1 = Box(point(0, 0), point(1, 1)) + b2 = Box(point(-1, -1), point(0.5, 0.5)) + @test convexhull(Multi([b1, b2])) == PolyArea(point.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + @test convexhull(GeometrySet([b1, b2])) == + PolyArea(point.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) - b1 = Ball(P2(0, 0), T(1)) - b2 = Box(P2(-1, -1), P2(0, 0)) + b1 = Ball(point(0, 0), T(1)) + b2 = Box(point(-1, -1), point(0, 0)) h = convexhull(Multi([b1, b2])) - @test P2(-0.8, -0.8) ∈ h - @test P2(0.2, 0.2) ∈ h + @test point(-0.8, -0.8) ∈ h + @test point(0.2, 0.2) ∈ h end end diff --git a/test/intersections.jl b/test/intersections.jl index d9c8b1060..2778c5225 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -11,9 +11,9 @@ end @testset "Points" begin - p = P2(0, 0) - q = P2(-1, -1) - b = Box(P2(0, 0), P2(1, 1)) + p = point(0, 0) + q = point(-1, -1) + b = Box(point(0, 0), point(1, 1)) @test p ∩ p == p @test q ∩ q == q @test p ∩ b == b ∩ p == p @@ -23,153 +23,153 @@ @testset "Segments" begin # segments in 2D - s1 = Segment(P2(0, 0), P2(1, 0)) - s2 = Segment(P2(0.5, 0.0), P2(2, 0)) - @test s1 ∩ s2 ≈ Segment(P2(0.5, 0.0), P2(1, 0)) - @test s2 ∩ s1 ≈ Segment(P2(0.5, 0.0), P2(1, 0)) - - s1 = Segment(P2(0, 0), P2(1, -1)) - s2 = Segment(P2(0.5, -0.5), P2(1.5, -1.5)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(P2(0.5, -0.5), P2(1, -1)) - - s1 = Segment(P2(0, 0), P2(1, 0)) - s2 = Segment(P2(0, 0), P2(0, 1)) - @test s1 ∩ s2 ≈ P2(0, 0) - @test s2 ∩ s1 ≈ P2(0, 0) - - s1 = Segment(P2(0, 0), P2(1, 0)) - s2 = Segment(P2(0, 0), P2(-1, 0)) - @test s1 ∩ s2 ≈ P2(0, 0) - @test s2 ∩ s1 ≈ P2(0, 0) - - s1 = Segment(P2(0, 0), P2(0, 1)) - s2 = Segment(P2(0, 0), P2(0, -1)) - @test s1 ∩ s2 ≈ P2(0, 0) - @test s2 ∩ s1 ≈ P2(0, 0) - - s1 = Segment(P2(1, 1), P2(1, 2)) - s2 = Segment(P2(1, 1), P2(1, 0)) - @test s1 ∩ s2 ≈ P2(1, 1) - @test s2 ∩ s1 ≈ P2(1, 1) - - s1 = Segment(P2(1, 1), P2(2, 1)) - s2 = Segment(P2(1, 0), P2(3, 0)) + s1 = Segment(point(0, 0), point(1, 0)) + s2 = Segment(point(0.5, 0.0), point(2, 0)) + @test s1 ∩ s2 ≈ Segment(point(0.5, 0.0), point(1, 0)) + @test s2 ∩ s1 ≈ Segment(point(0.5, 0.0), point(1, 0)) + + s1 = Segment(point(0, 0), point(1, -1)) + s2 = Segment(point(0.5, -0.5), point(1.5, -1.5)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0.5, -0.5), point(1, -1)) + + s1 = Segment(point(0, 0), point(1, 0)) + s2 = Segment(point(0, 0), point(0, 1)) + @test s1 ∩ s2 ≈ point(0, 0) + @test s2 ∩ s1 ≈ point(0, 0) + + s1 = Segment(point(0, 0), point(1, 0)) + s2 = Segment(point(0, 0), point(-1, 0)) + @test s1 ∩ s2 ≈ point(0, 0) + @test s2 ∩ s1 ≈ point(0, 0) + + s1 = Segment(point(0, 0), point(0, 1)) + s2 = Segment(point(0, 0), point(0, -1)) + @test s1 ∩ s2 ≈ point(0, 0) + @test s2 ∩ s1 ≈ point(0, 0) + + s1 = Segment(point(1, 1), point(1, 2)) + s2 = Segment(point(1, 1), point(1, 0)) + @test s1 ∩ s2 ≈ point(1, 1) + @test s2 ∩ s1 ≈ point(1, 1) + + s1 = Segment(point(1, 1), point(2, 1)) + s2 = Segment(point(1, 0), point(3, 0)) @test s1 ∩ s2 === nothing @test s2 ∩ s1 === nothing - s1 = Segment(P2(0.181429364026879, 0.546811355144474), P2(0.38282226144778, 0.107781953228536)) - s2 = Segment(P2(0.412498700935005, 0.212081819871479), P2(0.395936725690311, 0.252041094122474)) + s1 = Segment(point(0.181429364026879, 0.546811355144474), point(0.38282226144778, 0.107781953228536)) + s2 = Segment(point(0.412498700935005, 0.212081819871479), point(0.395936725690311, 0.252041094122474)) @test s1 ∩ s2 === nothing @test s2 ∩ s1 === nothing - s1 = Segment(P2(1, 2), P2(1, 0)) - s2 = Segment(P2(1, 0), P2(1, 1)) - @test s1 ∩ s2 ≈ Segment(P2(1, 1), P2(1, 0)) - @test s2 ∩ s1 ≈ Segment(P2(1, 0), P2(1, 1)) + s1 = Segment(point(1, 2), point(1, 0)) + s2 = Segment(point(1, 0), point(1, 1)) + @test s1 ∩ s2 ≈ Segment(point(1, 1), point(1, 0)) + @test s2 ∩ s1 ≈ Segment(point(1, 0), point(1, 1)) - s1 = Segment(P2(0, 0), P2(2, 0)) - s2 = Segment(P2(-2, 0), P2(-1, 0)) - s3 = Segment(P2(-1, 0), P2(-2, 0)) + s1 = Segment(point(0, 0), point(2, 0)) + s2 = Segment(point(-2, 0), point(-1, 0)) + s3 = Segment(point(-1, 0), point(-2, 0)) @test s1 ∩ s2 === s2 ∩ s1 === nothing @test s1 ∩ s3 === s3 ∩ s1 === nothing - s1 = Segment(P2(-1, 0), P2(0, 0)) - s2 = Segment(P2(0, 0), P2(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ P2(0, 0) + s1 = Segment(point(-1, 0), point(0, 0)) + s2 = Segment(point(0, 0), point(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(0, 0) - s1 = Segment(P2(-1, 0), P2(1, 0)) - s2 = Segment(P2(0, 0), P2(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(P2(0, 0), P2(1, 0)) + s1 = Segment(point(-1, 0), point(1, 0)) + s2 = Segment(point(0, 0), point(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0, 0), point(1, 0)) - s1 = Segment(P2(0, 0), P2(1, 0)) - s2 = Segment(P2(0, 0), P2(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(P2(0, 0), P2(1, 0)) + s1 = Segment(point(0, 0), point(1, 0)) + s2 = Segment(point(0, 0), point(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0, 0), point(1, 0)) - s1 = Segment(P2(0, 0), P2(3, 0)) - s2 = Segment(P2(1, 0), P2(2, 0)) + s1 = Segment(point(0, 0), point(3, 0)) + s2 = Segment(point(1, 0), point(2, 0)) @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - s1 = Segment(P2(0, 0), P2(2, 0)) - s2 = Segment(P2(1, 0), P2(2, 0)) + s1 = Segment(point(0, 0), point(2, 0)) + s2 = Segment(point(1, 0), point(2, 0)) @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - s1 = Segment(P2(0, 0), P2(2, 0)) - s2 = Segment(P2(1, 0), P2(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(P2(1, 0), P2(2, 0)) + s1 = Segment(point(0, 0), point(2, 0)) + s2 = Segment(point(1, 0), point(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(1, 0), point(2, 0)) - s1 = Segment(P2(0, 0), P2(2, 0)) - s2 = Segment(P2(2, 0), P2(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ P2(2, 0) + s1 = Segment(point(0, 0), point(2, 0)) + s2 = Segment(point(2, 0), point(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(2, 0) - s1 = Segment(P2(0, 0), P2(2, 0)) - s2 = Segment(P2(3, 0), P2(4, 0)) + s1 = Segment(point(0, 0), point(2, 0)) + s2 = Segment(point(3, 0), point(4, 0)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - s1 = Segment(P2(2, 1), P2(1, 2)) - s2 = Segment(P2(1, 0), P2(1, 1)) + s1 = Segment(point(2, 1), point(1, 2)) + s2 = Segment(point(1, 0), point(1, 1)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - s1 = Segment(P2(1.5, 1.5), P2(3.0, 1.5)) - s2 = Segment(P2(3.0, 1.0), P2(2.0, 2.0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ P2(2.5, 1.5) - - s1 = Segment(P2(0.94495744, 0.53224397), P2(0.94798386, 0.5344541)) - s2 = Segment(P2(0.94798386, 0.5344541), P2(0.9472896, 0.5340202)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ P2(0.94798386, 0.5344541) - - s₁ = Segment(P2(0, 0), P2(3, 4)) - s₂ = Segment(P2(1, 2), P2(3, -2)) - s₃ = Segment(P2(2, 0), P2(-2, 0)) - s₄ = Segment(P2(0, 0), P2(1, 2)) - s₅ = Segment(P2(1, 2), P2(3, 4)) - s₆ = Segment(P2(-1, -4 / 3), P2(0, 0)) - s₇ = Segment(P2(1, 2), P2(0, 4)) - s₈ = Segment(P2(4, 16 / 3), P2(3, 4)) - - s₉ = Segment(P2(-1, 5), P2(1, 4)) - s₁₀ = Segment(P2(1, 4), P2(-1, 5)) - s₁₁ = Segment(P2(-2, 5.5), P2(-0.8, 4.9)) - s₁₂ = Segment(P2(-0.8, 4.9), P2(-2, 5.5)) - s₁₃ = Segment(P2(-0.5, 4.75), P2(0.2, 4.4)) - s₁₄ = Segment(P2(0.2, 4.4), P2(-0.5, 4.75)) - s₁₅ = Segment(P2(0.5, 4.25), P2(1, 4)) - s₁₆ = Segment(P2(1, 4), P2(0.5, 4.25)) - s₁₇ = Segment(P2(2, 3.5), P2(1.5, 3.75)) - s₁₈ = Segment(P2(1.5, 3.75), P2(2, 3.5)) - - @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ P2(1.2, 1.6) # CASE 1: Crossing Segments + s1 = Segment(point(1.5, 1.5), point(3.0, 1.5)) + s2 = Segment(point(3.0, 1.0), point(2.0, 2.0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(2.5, 1.5) + + s1 = Segment(point(0.94495744, 0.53224397), point(0.94798386, 0.5344541)) + s2 = Segment(point(0.94798386, 0.5344541), point(0.9472896, 0.5340202)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(0.94798386, 0.5344541) + + s₁ = Segment(point(0, 0), point(3, 4)) + s₂ = Segment(point(1, 2), point(3, -2)) + s₃ = Segment(point(2, 0), point(-2, 0)) + s₄ = Segment(point(0, 0), point(1, 2)) + s₅ = Segment(point(1, 2), point(3, 4)) + s₆ = Segment(point(-1, -4 / 3), point(0, 0)) + s₇ = Segment(point(1, 2), point(0, 4)) + s₈ = Segment(point(4, 16 / 3), point(3, 4)) + + s₉ = Segment(point(-1, 5), point(1, 4)) + s₁₀ = Segment(point(1, 4), point(-1, 5)) + s₁₁ = Segment(point(-2, 5.5), point(-0.8, 4.9)) + s₁₂ = Segment(point(-0.8, 4.9), point(-2, 5.5)) + s₁₃ = Segment(point(-0.5, 4.75), point(0.2, 4.4)) + s₁₄ = Segment(point(0.2, 4.4), point(-0.5, 4.75)) + s₁₅ = Segment(point(0.5, 4.25), point(1, 4)) + s₁₆ = Segment(point(1, 4), point(0.5, 4.25)) + s₁₇ = Segment(point(2, 3.5), point(1.5, 3.75)) + s₁₈ = Segment(point(1.5, 3.75), point(2, 3.5)) + + @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ point(1.2, 1.6) # CASE 1: Crossing Segments @test intersection(s₁, s₂) |> type == Crossing @test intersection(s₂, s₁) |> type == Crossing - @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ P2(0, 0) # CASE 2: EdgeTouching (s₁(0)) + @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ point(0, 0) # CASE 2: EdgeTouching (s₁(0)) @test intersection(s₁, s₃) |> type == EdgeTouching @test intersection(s₃, s₁) |> type == EdgeTouching - @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ P2(2, 0) # CASE 2: EdgeTouching (s₃(1)) + @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ point(2, 0) # CASE 2: EdgeTouching (s₃(1)) @test intersection(s₂, s₃) |> type == EdgeTouching @test intersection(s₃, s₂) |> type == EdgeTouching - @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ P2(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) + @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ point(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) @test intersection(s₁, s₄) |> type == CornerTouching @test intersection(s₄, s₁) |> type == CornerTouching - @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ P2(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) + @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ point(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) @test intersection(s₂, s₄) |> type == CornerTouching @test intersection(s₄, s₂) |> type == CornerTouching - @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ P2(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) + @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ point(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) @test intersection(s₂, s₄) |> type == CornerTouching @test intersection(s₄, s₂) |> type == CornerTouching - @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ P2(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear + @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ point(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear @test intersection(s₁, s₆) |> type == CornerTouching @test intersection(s₆, s₁) |> type == CornerTouching - @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ P2(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear + @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ point(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear @test intersection(s₂, s₇) |> type == CornerTouching @test intersection(s₇, s₂) |> type == CornerTouching - @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ P2(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear + @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ point(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear @test intersection(s₁, s₈) |> type == CornerTouching @test intersection(s₈, s₁) |> type == CornerTouching @@ -181,17 +181,17 @@ @test intersection(s₉, s₁₀) |> type == Overlapping @test intersection(s₁₀, s₉) |> type == Overlapping - @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(P2(-1, 5), P2(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) + @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) @test intersection(s₉, s₁₁) |> type == Overlapping @test intersection(s₁₁, s₉) |> type == Overlapping - @test s₉ ∩ s₁₂ ≈ Segment(P2(-1, 5), P2(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) - @test s₁₂ ∩ s₉ ≈ Segment(P2(-0.8, 4.9), P2(-1, 5)) # flipped Points in Segment + @test s₉ ∩ s₁₂ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) + @test s₁₂ ∩ s₉ ≈ Segment(point(-0.8, 4.9), point(-1, 5)) # flipped Points in Segment @test intersection(s₉, s₁₂) |> type == Overlapping @test intersection(s₁₂, s₉) |> type == Overlapping - @test s₁₀ ∩ s₁₁ ≈ Segment(P2(-0.8, 4.9), P2(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) - @test s₁₁ ∩ s₁₀ ≈ Segment(P2(-1, 5), P2(-0.8, 4.9)) # flipped Points in Segment + @test s₁₀ ∩ s₁₁ ≈ Segment(point(-0.8, 4.9), point(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) + @test s₁₁ ∩ s₁₀ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # flipped Points in Segment @test intersection(s₁₀, s₁₁) |> type == Overlapping @test intersection(s₁₁, s₁₀) |> type == Overlapping @@ -251,29 +251,29 @@ @test intersection(s₃, s₁₀) |> type == NotIntersecting # segments in 3D - s1 = Segment(P3(0.0, 0.0, 0.0), P3(1.0, 0.0, 0.0)) - s2 = Segment(P3(0.5, 1.0, 0.0), P3(0.5, -1.0, 0.0)) - s3 = Segment(P3(0.5, 0.0, 0.0), P3(1.5, 0.0, 0.0)) - s4 = Segment(P3(0.0, 1.0, 0.0), P3(0.0, -2.0, 0.0)) - s5 = Segment(P3(-1.0, 1.0, 0.0), P3(2.0, -2.0, 0.0)) - s6 = Segment(P3(0.0, 0.0, 0.0), P3(0.0, 1.0, 0.0)) - s7 = Segment(P3(-1.0, 1.0, 0.0), P3(-1.0, -1.0, 0.0)) - s8 = Segment(P3(-1.0, 1.0, 1.0), P3(-1.0, -1.0, 1.0)) - s9 = Segment(P3(0.5, 1.0, 1.0), P3(0.5, -1.0, 1.0)) - s10 = Segment(P3(0.0, 1.0, 0.0), P3(1.0, 1.0, 0.0)) - s11 = Segment(P3(1.5, 0.0, 0.0), P3(2.5, 0.0, 0.0)) - s12 = Segment(P3(1.0, 0.0, 0.0), P3(2.0, 0.0, 0.0)) + s1 = Segment(point(0.0, 0.0, 0.0), point(1.0, 0.0, 0.0)) + s2 = Segment(point(0.5, 1.0, 0.0), point(0.5, -1.0, 0.0)) + s3 = Segment(point(0.5, 0.0, 0.0), point(1.5, 0.0, 0.0)) + s4 = Segment(point(0.0, 1.0, 0.0), point(0.0, -2.0, 0.0)) + s5 = Segment(point(-1.0, 1.0, 0.0), point(2.0, -2.0, 0.0)) + s6 = Segment(point(0.0, 0.0, 0.0), point(0.0, 1.0, 0.0)) + s7 = Segment(point(-1.0, 1.0, 0.0), point(-1.0, -1.0, 0.0)) + s8 = Segment(point(-1.0, 1.0, 1.0), point(-1.0, -1.0, 1.0)) + s9 = Segment(point(0.5, 1.0, 1.0), point(0.5, -1.0, 1.0)) + s10 = Segment(point(0.0, 1.0, 0.0), point(1.0, 1.0, 0.0)) + s11 = Segment(point(1.5, 0.0, 0.0), point(2.5, 0.0, 0.0)) + s12 = Segment(point(1.0, 0.0, 0.0), point(2.0, 0.0, 0.0)) @test intersection(s1, s2) |> type == Crossing - @test s1 ∩ s2 ≈ P3(0.5, 0.0, 0.0) + @test s1 ∩ s2 ≈ point(0.5, 0.0, 0.0) @test intersection(s1, s3) |> type == Overlapping - @test s1 ∩ s3 ≈ Segment(P3(0.5, 0.0, 0.0), P3(1.0, 0.0, 0.0)) + @test s1 ∩ s3 ≈ Segment(point(0.5, 0.0, 0.0), point(1.0, 0.0, 0.0)) @test intersection(s1, s4) |> type == EdgeTouching - @test s1 ∩ s4 ≈ P3(0.0, 0.0, 0.0) + @test s1 ∩ s4 ≈ point(0.0, 0.0, 0.0) @test intersection(s1, s5) |> type == EdgeTouching - @test s1 ∩ s5 ≈ P3(0.0, 0.0, 0.0) + @test s1 ∩ s5 ≈ point(0.0, 0.0, 0.0) @test intersection(s1, s6) |> type == CornerTouching - @test s1 ∩ s6 ≈ P3(0.0, 0.0, 0.0) + @test s1 ∩ s6 ≈ point(0.0, 0.0, 0.0) @test intersection(s1, s7) |> type == NotIntersecting @test isnothing(s1 ∩ s7) @test intersection(s1, s8) |> type == NotIntersecting @@ -285,38 +285,38 @@ @test intersection(s1, s11) |> type == NotIntersecting @test isnothing(s1 ∩ s11) @test intersection(s1, s12) |> type == CornerTouching - @test s1 ∩ s12 ≈ P3(1.0, 0.0, 0.0) + @test s1 ∩ s12 ≈ point(1.0, 0.0, 0.0) # precision test - s1 = Segment(P2(2.0, 2.0), P2(3.0, 1.0)) - s2 = Segment(P2(2.12505, 1.87503), P2(50000.0, 30000.0)) - s3 = Segment(P2(2.125005, 1.875003), P2(50000.0, 30000.0)) - s4 = Segment(P2(2.125005, 1.875003), P2(50002.125005, 30001.875003)) + s1 = Segment(point(2.0, 2.0), point(3.0, 1.0)) + s2 = Segment(point(2.12505, 1.87503), point(50000.0, 30000.0)) + s3 = Segment(point(2.125005, 1.875003), point(50000.0, 30000.0)) + s4 = Segment(point(2.125005, 1.875003), point(50002.125005, 30001.875003)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? P2(2.125005, 1.875003) : nothing) - @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? P2(2.125005, 1.875003) : nothing) + @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? point(2.125005, 1.875003) : nothing) + @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? point(2.125005, 1.875003) : nothing) # type stability tests - s1 = Segment(P2(0, 0), P2(1, 0)) - s2 = Segment(P2(0.5, 0.0), P2(2, 0)) + s1 = Segment(point(0, 0), point(1, 0)) + s2 = Segment(point(0.5, 0.0), point(2, 0)) @inferred someornone(s1, s2) - s1 = Segment(P3(0.0, 0.0, 0.0), P3(1.0, 0.0, 0.0)) - s2 = Segment(P3(0.5, 1.0, 0.0), P3(0.5, -1.0, 0.0)) + s1 = Segment(point(0.0, 0.0, 0.0), point(1.0, 0.0, 0.0)) + s2 = Segment(point(0.5, 1.0, 0.0), point(0.5, -1.0, 0.0)) @inferred someornone(s1, s2) # rays and segments in 2D - r₁ = Ray(P2(1, 0), V2(2, 1)) - s₁ = Segment(P2(0, 2), P2(2, -1)) # Crossing - s₂ = Segment(P2(0, 2), P2(1, 0.5)) # NotIntersecting - s₃ = Segment(P2(0, 2), P2(0.5, -0.5)) # NotIntersecting - s₄ = Segment(P2(0.5, 1), P2(1.5, -1)) # EdgeTouching - s₅ = Segment(P2(1.5, 0.25), P2(1.5, 2)) # EdgeTouching - s₆ = Segment(P2(1, 0), P2(1, -1)) # CornerTouching - s₇ = Segment(P2(0.5, -1), P2(1, 0)) # CornerTouching + r₁ = Ray(point(1, 0), vector(2, 1)) + s₁ = Segment(point(0, 2), point(2, -1)) # Crossing + s₂ = Segment(point(0, 2), point(1, 0.5)) # NotIntersecting + s₃ = Segment(point(0, 2), point(0.5, -0.5)) # NotIntersecting + s₄ = Segment(point(0.5, 1), point(1.5, -1)) # EdgeTouching + s₅ = Segment(point(1.5, 0.25), point(1.5, 2)) # EdgeTouching + s₆ = Segment(point(1, 0), point(1, -1)) # CornerTouching + s₇ = Segment(point(0.5, -1), point(1, 0)) # CornerTouching @test intersection(r₁, s₁) |> type == Crossing #CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ P2(1.25, 0.125) + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ point(1.25, 0.125) @test intersection(r₁, s₂) |> type == NotIntersecting # CASE 5 @test r₁ ∩ s₂ === s₂ ∩ r₁ === nothing @test intersection(r₁, s₃) |> type == NotIntersecting # CASE 5 @@ -324,22 +324,22 @@ @test intersection(r₁, s₄) |> type == EdgeTouching # CASE 2 @test r₁ ∩ s₄ ≈ s₄ ∩ r₁ ≈ r₁(0) @test intersection(r₁, s₅) |> type == EdgeTouching # CASE 2 - @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ P2(1.5, 0.25) + @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ point(1.5, 0.25) @test intersection(r₁, s₆) |> type == CornerTouching # CASE 3 @test r₁ ∩ s₆ ≈ s₆ ∩ r₁ ≈ r₁(0) @test intersection(r₁, s₇) |> type == CornerTouching # CASE 3 @test r₁ ∩ s₇ ≈ s₇ ∩ r₁ ≈ r₁(0) - r₂ = Ray(P2(3, 2), V2(1, 1)) - s₈ = Segment(P2(4, 3), P2(5, 4)) # Overlapping - s₉ = Segment(P2(2.5, 1.5), P2(3.3, 2.3)) # Overlapping s(1) - s₁₀ = Segment(P2(3.6, 2.6), P2(2.6, 1.6)) # Overlapping s(0) - s₁₁ = Segment(P2(2.2, 1.2), P2(3, 2)) # CornerTouching, colinear, s(1) - s₁₂ = Segment(P2(3, 2), P2(2.4, 1.4)) # CornerTouching, colinear, s(0) - s₁₃ = Segment(P2(3, 2), P2(3.1, 2.1)) # Overlapping s(0) = r(0) - s₁₄ = Segment(P2(3.2, 2.2), P2(3, 2)) # Overlapping s(1) = r(0) - s₁₅ = Segment(P2(2, 1), P2(1.6, 0.6)) # No Intersection, colinear - s₁₆ = Segment(P2(3, 1), P2(4, 2)) # No Intersection, parallel + r₂ = Ray(point(3, 2), vector(1, 1)) + s₈ = Segment(point(4, 3), point(5, 4)) # Overlapping + s₉ = Segment(point(2.5, 1.5), point(3.3, 2.3)) # Overlapping s(1) + s₁₀ = Segment(point(3.6, 2.6), point(2.6, 1.6)) # Overlapping s(0) + s₁₁ = Segment(point(2.2, 1.2), point(3, 2)) # CornerTouching, colinear, s(1) + s₁₂ = Segment(point(3, 2), point(2.4, 1.4)) # CornerTouching, colinear, s(0) + s₁₃ = Segment(point(3, 2), point(3.1, 2.1)) # Overlapping s(0) = r(0) + s₁₄ = Segment(point(3.2, 2.2), point(3, 2)) # Overlapping s(1) = r(0) + s₁₅ = Segment(point(2, 1), point(1.6, 0.6)) # No Intersection, colinear + s₁₆ = Segment(point(3, 1), point(4, 2)) # No Intersection, parallel @test intersection(r₂, s₈) |> type == Overlapping # CASE 4 @test r₂ ∩ s₈ === s₈ ∩ r₂ === s₈ @test intersection(r₂, s₉) |> type == Overlapping # CASE 4 @@ -360,42 +360,42 @@ @test r₂ ∩ s₁₆ === s₁₆ ∩ r₂ === nothing # type stability tests - r₁ = Ray(P2(0, 0), V2(1, 0)) - s₁ = Segment(P2(-1, -1), P2(-1, 1)) + r₁ = Ray(point(0, 0), vector(1, 0)) + s₁ = Segment(point(-1, -1), point(-1, 1)) @inferred someornone(r₁, s₁) # 3D test - r₁ = Ray(P3(1, 2, 3), V3(1, 2, 3)) - s₁ = Segment(P3(1, 3, 5), P3(3, 5, 7)) + r₁ = Ray(point(1, 2, 3), vector(1, 2, 3)) + s₁ = Segment(point(1, 3, 5), point(3, 5, 7)) @test intersection(r₁, s₁) |> type === Crossing # CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ P3(2, 4, 6) + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ point(2, 4, 6) - s₂ = Segment(P3(0, 1, 2), P3(2, 3, 4)) + s₂ = Segment(point(0, 1, 2), point(2, 3, 4)) @test intersection(r₁, s₂) |> type === EdgeTouching # CASE 2 @test r₁ ∩ s₂ == s₂ ∩ r₁ == r₁(0) - s₃ = Segment(P3(0.23, 1, 2.3), P3(1, 2, 3)) + s₃ = Segment(point(0.23, 1, 2.3), point(1, 2, 3)) @test intersection(r₁, s₃) |> type === CornerTouching # CASE 3 @test r₁ ∩ s₃ == s₃ ∩ r₁ == r₁(0) - s₄ = Segment(P3(0, 0, 0), P3(2, 4, 6)) + s₄ = Segment(point(0, 0, 0), point(2, 4, 6)) @test intersection(r₁, s₄) |> type === Overlapping # CASE 4 - @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(P3(1, 2, 3), P3(2, 4, 6)) + @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(point(1, 2, 3), point(2, 4, 6)) - s₅ = Segment(P3(0, 0, 0), P3(0.5, 1, 1.5)) + s₅ = Segment(point(0, 0, 0), point(0.5, 1, 1.5)) @test intersection(r₁, s₅) |> type === NotIntersecting # CASE 5 @test r₁ ∩ s₅ === s₅ ∩ r₁ === nothing - l₁ = Line(P2(1, 0), P2(3, 1)) - s₁ = Segment(P2(0, 2), P2(2, -1)) # Crossing - s₂ = Segment(P2(0.5, 1), P2(0, 0)) # NotIntersecting - s₃ = Segment(P2(0, 2), P2(-2, 1)) # NotIntersecting - s₄ = Segment(P2(0.5, -1), P2(1, 0)) # Touching - s₅ = Segment(P2(1.5, 0.25), P2(1.5, 2)) # Touching - s₆ = Segment(P2(-3, -2), P2(4, 1.5)) # Overlapping + l₁ = Line(point(1, 0), point(3, 1)) + s₁ = Segment(point(0, 2), point(2, -1)) # Crossing + s₂ = Segment(point(0.5, 1), point(0, 0)) # NotIntersecting + s₃ = Segment(point(0, 2), point(-2, 1)) # NotIntersecting + s₄ = Segment(point(0.5, -1), point(1, 0)) # Touching + s₅ = Segment(point(1.5, 0.25), point(1.5, 2)) # Touching + s₆ = Segment(point(-3, -2), point(4, 1.5)) # Overlapping @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ P2(1.25, 0.125) + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ point(1.25, 0.125) @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 @@ -412,17 +412,17 @@ @inferred someornone(l₁, s₂) # 3d tests - l₁ = Line(P3(1, 0, 1), P3(3, 1, 1)) - s₁ = Segment(P3(0, 2, 1), P3(2, -1, 1)) # Crossing - s₂ = Segment(P3(0.5, 1, 1), P3(0, 0, 1)) # NotIntersecting - s₃ = Segment(P3(0, 2, 1), P3(-2, 1, 1)) # NotIntersecting - s₄ = Segment(P3(0.5, -1, 1), P3(1, 0, 1)) # Touching - s₅ = Segment(P3(1.5, 0.25, 1), P3(1.5, 2, 1)) # Touching - s₆ = Segment(P3(-3, -2, 1), P3(4, 1.5, 1)) # Overlapping - s₇ = Segment(P3(0, 2, 1), P3(2, -1, 1.1)) # NotIntersecting + l₁ = Line(point(1, 0, 1), point(3, 1, 1)) + s₁ = Segment(point(0, 2, 1), point(2, -1, 1)) # Crossing + s₂ = Segment(point(0.5, 1, 1), point(0, 0, 1)) # NotIntersecting + s₃ = Segment(point(0, 2, 1), point(-2, 1, 1)) # NotIntersecting + s₄ = Segment(point(0.5, -1, 1), point(1, 0, 1)) # Touching + s₅ = Segment(point(1.5, 0.25, 1), point(1.5, 2, 1)) # Touching + s₆ = Segment(point(-3, -2, 1), point(4, 1.5, 1)) # Overlapping + s₇ = Segment(point(0, 2, 1), point(2, -1, 1.1)) # NotIntersecting @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ P3(1.25, 0.125, 1) + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ point(1.25, 0.125, 1) @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 @@ -437,9 +437,9 @@ @test l₁ ∩ s₇ === s₇ ∩ l₁ === nothing # degenerate segments - A = P2(0.0, 0.0) - B = P2(0.5, 0.0) - C = P2(1.0, 0.0) + A = point(0.0, 0.0) + B = point(0.5, 0.0) + C = point(1.0, 0.0) s₀ = Segment(A, C) s₁ = Segment(A, A) s₂ = Segment(B, B) @@ -471,16 +471,16 @@ @testset "Rays" begin # rays in 2D - r₁ = Ray(P2(1, 0), V2(2, 1)) - r₂ = Ray(P2(0, 2), V2(2, -3)) - r₃ = Ray(P2(0.5, 1), V2(1, -2)) - r₄ = Ray(P2(0, 2), V2(1, -3)) - r₅ = Ray(P2(4, 1.5), V2(4, 2)) - r₆ = Ray(P2(2, 0.5), V2(-0.5, -0.25)) - r₇ = Ray(P2(4, 0), V2(0, 1)) + r₁ = Ray(point(1, 0), vector(2, 1)) + r₂ = Ray(point(0, 2), vector(2, -3)) + r₃ = Ray(point(0.5, 1), vector(1, -2)) + r₄ = Ray(point(0, 2), vector(1, -3)) + r₅ = Ray(point(4, 1.5), vector(4, 2)) + r₆ = Ray(point(2, 0.5), vector(-0.5, -0.25)) + r₇ = Ray(point(4, 0), vector(0, 1)) @test intersection(r₁, r₂) |> type == Crossing #CASE 1 - @test r₁ ∩ r₂ ≈ P2(1.25, 0.125) - @test r₁ ∩ r₇ ≈ P2(4, 1.5) + @test r₁ ∩ r₂ ≈ point(1.25, 0.125) + @test r₁ ∩ r₇ ≈ point(4, 1.5) @test intersection(r₁, r₃) |> type == EdgeTouching #CASE 2 @test r₁ ∩ r₃ ≈ r₁(0) # origin of first ray @test r₅ ∩ r₇ ≈ r₅(0) @@ -499,14 +499,14 @@ @test r₁ ∩ r₄ === r₄ ∩ r₁ === nothing # lines and rays in 2D - l₁ = Line(P2(0, 0), P2(4, 5)) - r₁ = Ray(P2(3, 4), V2(1, -2)) # crossing ray - r₂ = Ray(P2(1, 1.25), V2(1, 0.3)) # touching ray - r₃ = Ray(P2(-1, -1.25), V2(-1, -1.25)) # overlapping ray - r₄ = Ray(P2(1, 3), V2(1, 1.25)) # parallel ray - r₅ = Ray(P2(1, 1), V2(1, -1)) # no Intersection - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ P2(3.0769230769230766, 3.846153846153846) # CASE 1 + l₁ = Line(point(0, 0), point(4, 5)) + r₁ = Ray(point(3, 4), vector(1, -2)) # crossing ray + r₂ = Ray(point(1, 1.25), vector(1, 0.3)) # touching ray + r₃ = Ray(point(-1, -1.25), vector(-1, -1.25)) # overlapping ray + r₄ = Ray(point(1, 3), vector(1, 1.25)) # parallel ray + r₅ = Ray(point(1, 1), vector(1, -1)) # no Intersection + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ point(3.0769230769230766, 3.846153846153846) # CASE 1 @test intersection(l₁, r₁) |> type === Crossing @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 @@ -527,15 +527,15 @@ # 3D tests # lines and rays in 3D - l₁ = Line(P3(0, 0, 0.1), P3(4, 5, 0.1)) - r₁ = Ray(P3(3, 4, 0.1), V3(1, -2, 0)) # crossing ray - r₂ = Ray(P3(1, 1.25, 0.1), V3(1, 0.3, 0)) # touching ray - r₃ = Ray(P3(-1, -1.25, 0.1), V3(-1, -1.25, 0)) # overlapping ray - r₄ = Ray(P3(1, 3, 0.1), V3(1, 1.25, 0)) # parallel ray - r₅ = Ray(P3(1, 1, 0.1), V3(1, -1, 0)) # no Intersection - r₆ = Ray(P3(3, 4, 0), V3(1, -2, 1)) # crossing ray - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ P3(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 + l₁ = Line(point(0, 0, 0.1), point(4, 5, 0.1)) + r₁ = Ray(point(3, 4, 0.1), vector(1, -2, 0)) # crossing ray + r₂ = Ray(point(1, 1.25, 0.1), vector(1, 0.3, 0)) # touching ray + r₃ = Ray(point(-1, -1.25, 0.1), vector(-1, -1.25, 0)) # overlapping ray + r₄ = Ray(point(1, 3, 0.1), vector(1, 1.25, 0)) # parallel ray + r₅ = Ray(point(1, 1, 0.1), vector(1, -1, 0)) # no Intersection + r₆ = Ray(point(3, 4, 0), vector(1, -2, 1)) # crossing ray + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ point(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 @test intersection(l₁, r₁) |> type === Crossing @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 @@ -556,59 +556,59 @@ @testset "Lines" begin # lines in 2D - l1 = Line(P2(0, 0), P2(1, 0)) - l2 = Line(P2(-1, -1), P2(-1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ P2(-1, 0) + l1 = Line(point(0, 0), point(1, 0)) + l2 = Line(point(-1, -1), point(-1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ point(-1, 0) - l1 = Line(P2(0, 0), P2(1, 0)) - l2 = Line(P2(0, 1), P2(1, 1)) + l1 = Line(point(0, 0), point(1, 0)) + l2 = Line(point(0, 1), point(1, 1)) @test l1 ∩ l2 === l2 ∩ l1 === nothing - l1 = Line(P2(0, 0), P2(1, 0)) - l2 = Line(P2(1, 0), P2(2, 0)) + l1 = Line(point(0, 0), point(1, 0)) + l2 = Line(point(1, 0), point(2, 0)) @test l1 == l2 @test l1 ∩ l2 == l2 ∩ l1 == l1 # rounding errors - l1 = Line(P2(3.0, 1.0), P2(2.0, 2.0)) + l1 = Line(point(3.0, 1.0), point(2.0, 2.0)) for k in 1:1000 Δ = k * atol(T) - l2 = Line(P2(1.5, 1.5 + Δ), P2(3.0, 1.5 + Δ)) - p = P2(2.5 - Δ, 1.5 + Δ) + l2 = Line(point(1.5, 1.5 + Δ), point(3.0, 1.5 + Δ)) + p = point(2.5 - Δ, 1.5 + Δ) @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ p end # lines in 3D # not in same plane - l1 = Line(P3(0, 0, 0), P3(1, 0, 0)) - l2 = Line(P3(1, 1, 1), P3(1, 2, 1)) + l1 = Line(point(0, 0, 0), point(1, 0, 0)) + l2 = Line(point(1, 1, 1), point(1, 2, 1)) @test l1 ∩ l2 == l2 ∩ l1 === nothing # in same plane but parallel - l1 = Line(P3(0, 0, 0), P3(1, 0, 0)) - l2 = Line(P3(0, 1, 1), P3(1, 1, 1)) + l1 = Line(point(0, 0, 0), point(1, 0, 0)) + l2 = Line(point(0, 1, 1), point(1, 1, 1)) @test l1 ∩ l2 == l2 ∩ l1 === nothing # in same plane and colinear - l1 = Line(P3(0, 0, 0), P3(1, 0, 0)) - l2 = Line(P3(2, 0, 0), P3(3, 0, 0)) + l1 = Line(point(0, 0, 0), point(1, 0, 0)) + l2 = Line(point(2, 0, 0), point(3, 0, 0)) @test l1 ∩ l2 == l2 ∩ l1 == l1 # crossing in one point - l1 = Line(P3(1, 2, 3), P3(2, 1, 0)) - l2 = Line(P3(1, 2, 3), P3(1, 1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ P3(1, 2, 3) + l1 = Line(point(1, 2, 3), point(2, 1, 0)) + l2 = Line(point(1, 2, 3), point(1, 1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ point(1, 2, 3) # type stability tests - l1 = Line(P2(0, 0), P2(1, 0)) - l2 = Line(P2(-1, -1), P2(-1, 1)) + l1 = Line(point(0, 0), point(1, 0)) + l2 = Line(point(-1, -1), point(-1, 1)) @inferred someornone(l1, l2) end @testset "Chains" begin # https://github.com/JuliaGeometry/Meshes.jl/issues/644 - r = Rope(P2(0, 0), P2(1, 1)) - @test r ∩ r == GeometrySet([Segment(P2(0, 0), P2(1, 1))]) + r = Rope(point(0, 0), point(1, 1)) + @test r ∩ r == GeometrySet([Segment(point(0, 0), point(1, 1))]) @inferred someornone(r, r) end @@ -617,236 +617,236 @@ # SEGMENTS # --------- - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting segment and plane - s = Segment(P3(0, 0, 0), P3(0, 2, 2)) + s = Segment(point(0, 0, 0), point(0, 2, 2)) @test intersection(s, p) |> type == Crossing - @test s ∩ p == P3(0, 1, 1) + @test s ∩ p == point(0, 1, 1) # intersecting segment and plane with λ ≈ 0 - s = Segment(P3(0, 0, 1), P3(0, 2, 2)) + s = Segment(point(0, 0, 1), point(0, 2, 2)) @test intersection(s, p) |> type == Touching - @test s ∩ p == P3(0, 0, 1) + @test s ∩ p == point(0, 0, 1) # intersecting segment and plane with λ ≈ 1 - s = Segment(P3(0, 0, 2), P3(0, 2, 1)) + s = Segment(point(0, 0, 2), point(0, 2, 1)) @test intersection(s, p) |> type == Touching - @test s ∩ p == P3(0, 2, 1) + @test s ∩ p == point(0, 2, 1) # segment contained within plane - s = Segment(P3(0, 0, 1), P3(0, -2, 1)) + s = Segment(point(0, 0, 1), point(0, -2, 1)) @test intersection(s, p) |> type == Overlapping @test s ∩ p == s # segment below plane, non-intersecting - s = Segment(P3(0, 0, 0), P3(0, -2, -2)) + s = Segment(point(0, 0, 0), point(0, -2, -2)) @test intersection(s, p) |> type == NotIntersecting @test isnothing(s ∩ p) # segment parallel to plane, offset, non-intersecting - s = Segment(P3(0, 0, -1), P3(0, -2, -1)) + s = Segment(point(0, 0, -1), point(0, -2, -1)) @test intersection(s, p) |> type == NotIntersecting @test isnothing(s ∩ p) # plane as first argument - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) - s = Segment(P3(0, 0, 0), P3(0, 2, 2)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + s = Segment(point(0, 0, 0), point(0, 2, 2)) @test intersection(p, s) |> type == Crossing - @test s ∩ p == p ∩ s == P3(0, 1, 1) + @test s ∩ p == p ∩ s == point(0, 1, 1) # type stability tests - s = Segment(P3(0, 0, 0), P3(0, 2, 2)) - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) + s = Segment(point(0, 0, 0), point(0, 2, 2)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) @inferred someornone(s, p) # ----- # RAYS # ----- - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting ray and plane - r = Ray(P3(0, 0, 0), V3(0, 2, 2)) + r = Ray(point(0, 0, 0), vector(0, 2, 2)) @test intersection(r, p) |> type == Crossing - @test r ∩ p == P3(0, 1, 1) + @test r ∩ p == point(0, 1, 1) # intersecting ray and plane with λ ≈ 0 - r = Ray(P3(0, 0, 1), V3(0, 2, 1)) + r = Ray(point(0, 0, 1), vector(0, 2, 1)) @test intersection(r, p) |> type == Touching - @test r ∩ p == P3(0, 0, 1) + @test r ∩ p == point(0, 0, 1) # intersecting ray and plane with λ ≈ 1 (only case where Ray different to Segment) - r = Ray(P3(0, 0, 2), V3(0, 2, -1)) + r = Ray(point(0, 0, 2), vector(0, 2, -1)) @test intersection(r, p) |> type == Crossing - @test r ∩ p == P3(0, 2, 1) + @test r ∩ p == point(0, 2, 1) # ray contained within plane - r = Ray(P3(0, 0, 1), V3(0, -2, 0)) + r = Ray(point(0, 0, 1), vector(0, -2, 0)) @test intersection(r, p) |> type == Overlapping @test r ∩ p == r # ray below plane, non-intersecting - r = Ray(P3(0, 0, 0), V3(0, -2, -2)) + r = Ray(point(0, 0, 0), vector(0, -2, -2)) @test intersection(r, p) |> type == NotIntersecting @test isnothing(r ∩ p) # ray parallel to plane, offset, non-intersecting - r = Ray(P3(0, 0, -1), V3(0, -2, 0)) + r = Ray(point(0, 0, -1), vector(0, -2, 0)) @test intersection(r, p) |> type == NotIntersecting @test isnothing(r ∩ p) # plane as first argument - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) - r = Ray(P3(0, 0, 0), V3(0, 2, 2)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + r = Ray(point(0, 0, 0), vector(0, 2, 2)) @test intersection(p, r) |> type == Crossing - @test r ∩ p == p ∩ r == P3(0, 1, 1) + @test r ∩ p == p ∩ r == point(0, 1, 1) # ------ # LINES # ------ - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting line and plane - l = Line(P3(0, 0, 0), P3(0, 2, 2)) + l = Line(point(0, 0, 0), point(0, 2, 2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == P3(0, 1, 1) + @test l ∩ p == point(0, 1, 1) # intersecting line and plane with λ ≈ 0 - l = Line(P3(0, 0, 1), P3(0, 2, 2)) + l = Line(point(0, 0, 1), point(0, 2, 2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == P3(0, 0, 1) + @test l ∩ p == point(0, 0, 1) # intersecting line and plane with λ ≈ 1 - l = Line(P3(0, 0, 2), P3(0, 2, 1)) + l = Line(point(0, 0, 2), point(0, 2, 1)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == P3(0, 2, 1) + @test l ∩ p == point(0, 2, 1) # line contained within plane - l = Line(P3(0, 0, 1), P3(0, -2, 1)) + l = Line(point(0, 0, 1), point(0, -2, 1)) @test intersection(l, p) |> type == Overlapping @test l ∩ p == l # line below plane, non-intersecting - l = Line(P3(0, 0, 0), P3(0, -2, -2)) + l = Line(point(0, 0, 0), point(0, -2, -2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == P3(0, 1, 1) + @test l ∩ p == point(0, 1, 1) # line parallel to plane, offset, non-intersecting - l = Line(P3(0, 0, -1), P3(0, -2, -1)) + l = Line(point(0, 0, -1), point(0, -2, -1)) @test intersection(l, p) |> type == NotIntersecting @test isnothing(l ∩ p) # plane as first argument - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) - l = Line(P3(0, 0, 0), P3(0, 2, 2)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + l = Line(point(0, 0, 0), point(0, 2, 2)) @test intersection(p, l) |> type == Crossing - @test l ∩ p == p ∩ l == P3(0, 1, 1) + @test l ∩ p == p ∩ l == point(0, 1, 1) # type stability tests - l = Line(P3(0, 0, 0), P3(0, 2, 2)) - p = Plane(P3(0, 0, 1), V3(1, 0, 0), V3(0, 1, 0)) + l = Line(point(0, 0, 0), point(0, 2, 2)) + p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) @inferred someornone(l, p) # ------ # PLANES # ------ - p1 = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p1 = Plane(point(0, 0, 0), vector(0, 0, 1)) # p1 parallel to p2 - p2 = Plane(P3(0, 0, 1), V3(0, 0, 1)) + p2 = Plane(point(0, 0, 1), vector(0, 0, 1)) @test intersection(p1, p2) |> type == NotIntersecting @test isnothing(p1 ∩ p2) # p1 intersects p2 - p2 = Plane(P3(0, 0, 1), V3(1 / sqrt(2), 0, 1 / sqrt(2))) + p2 = Plane(point(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) @test intersection(p1, p2) |> type == Intersecting - @test p1 ∩ p2 == Line(P3(1, 0, 0), P3(1, 1, 0)) + @test p1 ∩ p2 == Line(point(1, 0, 0), point(1, 1, 0)) end @testset "Boxes" begin - b1 = Box(P2(0, 0), P2(1, 1)) - b2 = Box(P2(0.5, 0.5), P2(2, 2)) - b3 = Box(P2(2, 2), P2(3, 3)) - b4 = Box(P2(1, 1), P2(2, 2)) - b5 = Box(P2(1.0, 0.5), P2(2, 2)) - b6 = Box(P2(0, 2), P2(1, 3)) - b7 = Box(P2(0, 1), P2(1, 2)) - b8 = Box(P2(0, -1), P2(1, 0)) - b9 = Box(P2(1, 0), P2(2, 1)) - b10 = Box(P2(-1, 0), P2(0, 1)) + b1 = Box(point(0, 0), point(1, 1)) + b2 = Box(point(0.5, 0.5), point(2, 2)) + b3 = Box(point(2, 2), point(3, 3)) + b4 = Box(point(1, 1), point(2, 2)) + b5 = Box(point(1.0, 0.5), point(2, 2)) + b6 = Box(point(0, 2), point(1, 3)) + b7 = Box(point(0, 1), point(1, 2)) + b8 = Box(point(0, -1), point(1, 0)) + b9 = Box(point(1, 0), point(2, 1)) + b10 = Box(point(-1, 0), point(0, 1)) @test intersection(b1, b2) |> type == Overlapping - @test b1 ∩ b2 == Box(P2(0.5, 0.5), P2(1, 1)) + @test b1 ∩ b2 == Box(point(0.5, 0.5), point(1, 1)) @test intersection(b1, b3) |> type == NotIntersecting @test isnothing(b1 ∩ b3) @test intersection(b1, b4) |> type == CornerTouching - @test b1 ∩ b4 == P2(1, 1) + @test b1 ∩ b4 == point(1, 1) @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(P2(1.0, 0.5), P2(1, 1)) + @test b1 ∩ b5 == Box(point(1.0, 0.5), point(1, 1)) @test intersection(b1, b6) |> type == NotIntersecting @test isnothing(b1 ∩ b6) @test intersection(b1, b7) |> type == Touching - @test b1 ∩ b7 == Box(P2(0, 1), P2(1, 1)) + @test b1 ∩ b7 == Box(point(0, 1), point(1, 1)) @test intersection(b1, b8) |> type == Touching - @test b1 ∩ b8 == Box(P2(0, 0), P2(1, 0)) + @test b1 ∩ b8 == Box(point(0, 0), point(1, 0)) @test intersection(b1, b9) |> type == Touching - @test b1 ∩ b9 == Box(P2(1, 0), P2(1, 1)) + @test b1 ∩ b9 == Box(point(1, 0), point(1, 1)) @test intersection(b1, b10) |> type == Touching - @test b1 ∩ b10 == Box(P2(0, 0), P2(0, 1)) + @test b1 ∩ b10 == Box(point(0, 0), point(0, 1)) # more touching examples - b1 = Box(P2(0, 0), P2(1, 1)) - b2 = Box(P2(1.0, 0.5), P2(2, 1)) - b3 = Box(P2(-1, 0), P2(0.0, 0.5)) - b4 = Box(P2(0, 1), P2(0.5, 2.0)) - b5 = Box(P2(0.5, -1.0), P2(1, 0)) + b1 = Box(point(0, 0), point(1, 1)) + b2 = Box(point(1.0, 0.5), point(2, 1)) + b3 = Box(point(-1, 0), point(0.0, 0.5)) + b4 = Box(point(0, 1), point(0.5, 2.0)) + b5 = Box(point(0.5, -1.0), point(1, 0)) @test intersection(b1, b2) |> type == Touching - @test b1 ∩ b2 == Box(P2(1.0, 0.5), P2(1, 1)) + @test b1 ∩ b2 == Box(point(1.0, 0.5), point(1, 1)) @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(P2(0.0, 0.0), P2(0.0, 0.5)) + @test b1 ∩ b3 == Box(point(0.0, 0.0), point(0.0, 0.5)) @test intersection(b1, b4) |> type == Touching - @test b1 ∩ b4 == Box(P2(0.0, 1.0), P2(0.5, 1.0)) + @test b1 ∩ b4 == Box(point(0.0, 1.0), point(0.5, 1.0)) @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(P2(0.5, 0.0), P2(1.0, 0.0)) + @test b1 ∩ b5 == Box(point(0.5, 0.0), point(1.0, 0.0)) # tricky examples with degenerate boxes - b1 = Box(P3(0, 0, 0), P3(2, 2, 0)) - b2 = Box(P3(3, 0, 0), P3(5, 2, 0)) - b3 = Box(P3(1, 0, 0), P3(3, 2, 0)) + b1 = Box(point(0, 0, 0), point(2, 2, 0)) + b2 = Box(point(3, 0, 0), point(5, 2, 0)) + b3 = Box(point(1, 0, 0), point(3, 2, 0)) @test intersection(b1, b2) |> type == NotIntersecting @test isnothing(b1 ∩ b2) @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(P3(1, 0, 0), P3(2, 2, 0)) + @test b1 ∩ b3 == Box(point(1, 0, 0), point(2, 2, 0)) # type stability tests - b1 = Box(P2(0, 0), P2(1, 1)) - b2 = Box(P2(0.5, 0.5), P2(2, 2)) + b1 = Box(point(0, 0), point(1, 1)) + b2 = Box(point(0.5, 0.5), point(2, 2)) @inferred someornone(b1, b2) # Ray-Box intersection - b = Box(P3(0, 0, 0), P3(1, 1, 1)) + b = Box(point(0, 0, 0), point(1, 1, 1)) - r = Ray(P3(0, 0, 0), V3(1, 1, 1)) + r = Ray(point(0, 0, 0), vector(1, 1, 1)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(P3(0, 0, 0), P3(1, 1, 1)) + @test r ∩ b == Segment(point(0, 0, 0), point(1, 1, 1)) - r = Ray(P3(-0.5, 0, 0), V3(1.0, 1.0, 1.0)) + r = Ray(point(-0.5, 0, 0), vector(1.0, 1.0, 1.0)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(P3(0.0, 0.5, 0.5), P3(0.5, 1.0, 1.0)) + @test r ∩ b == Segment(point(0.0, 0.5, 0.5), point(0.5, 1.0, 1.0)) - r = Ray(P3(3.0, 0.0, 0.5), V3(-1.0, 1.0, 0.0)) + r = Ray(point(3.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == NotIntersecting - r = Ray(P3(2.0, 0.0, 0.5), V3(-1.0, 1.0, 0.0)) + r = Ray(point(2.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == Touching - @test r ∩ b == P3(1.0, 1.0, 0.5) + @test r ∩ b == point(1.0, 1.0, 0.5) # the ray on a face of the box, got NaN in calculation - r = Ray(P3(1.5, 0.0, 0.0), V3(-1.0, 1.0, 0.0)) + r = Ray(point(1.5, 0.0, 0.0), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(P3(1.0, 0.5, 0.0), P3(0.5, 1.0, 0.0)) + @test r ∩ b == Segment(point(1.0, 0.5, 0.0), point(0.5, 1.0, 0.0)) end @testset "Triangles" begin @@ -855,315 +855,325 @@ reverse_segment(s) = Segment(vertices(s)[2], vertices(s)[1]) # intersections with triangle lying in XY plane - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) # intersects through t - s = Segment(P3(0.2, 0.2, 1.0), P3(0.2, 0.2, -1.0)) + s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == P3(0.2, 0.2, 0.0) + @test s ∩ t == point(0.2, 0.2, 0.0) # intersects at a vertex of t - s = Segment(P3(0.0, 0.0, 1.0), P3(0.0, 0.0, -1.0)) + s = Segment(point(0.0, 0.0, 1.0), point(0.0, 0.0, -1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == P3(0.0, 0.0, 0.0) + @test s ∩ t == point(0.0, 0.0, 0.0) # normal to, doesn't intersect with t - s = Segment(P3(0.9, 0.9, 1.0), P3(0.9, 0.9, -1.0)) + s = Segment(point(0.9, 0.9, 1.0), point(0.9, 0.9, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # coplanar, doesn't intersect with t - s = Segment(P3(-0.2, -0.2, 0.0), P3(1.2, -0.2, 0.0)) + s = Segment(point(-0.2, -0.2, 0.0), point(1.2, -0.2, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, above, doesn't intersect with t - s = Segment(P3(-0.2, 0.2, 1.0), P3(1.2, 0.2, 1.0)) + s = Segment(point(-0.2, 0.2, 1.0), point(1.2, 0.2, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, below, doesn't intersect with t - s = Segment(P3(-0.2, 0.2, -1.0), P3(1.2, 0.2, -1.0)) + s = Segment(point(-0.2, 0.2, -1.0), point(1.2, 0.2, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # coplanar, within bounding box of t, no intersection - s = Segment(P3(0.7, 0.8, 0.0), P3(0.8, 0.7, 0.0)) + s = Segment(point(0.7, 0.8, 0.0), point(0.8, 0.7, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment above and to right of t, no intersection - s = Segment(P3(1.0, 1.0, 0.0), P3(1.0, 1.0, 1.0)) + s = Segment(point(1.0, 1.0, 0.0), point(1.0, 1.0, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment below t, no intersection - s = Segment(P3(0.5, -1.0, 0.0), P3(0.5, -1.0, 1.0)) + s = Segment(point(0.5, -1.0, 0.0), point(0.5, -1.0, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment left of t, no intersection - s = Segment(P3(-1.0, 0.5, 0.0), P3(-1.0, 0.5, 1.0)) + s = Segment(point(-1.0, 0.5, 0.0), point(-1.0, 0.5, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment above and to right of t, no intersection - s = Segment(P3(1.0, 1.0, 0.0), P3(1.0, 1.0, -1.0)) + s = Segment(point(1.0, 1.0, 0.0), point(1.0, 1.0, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment below t, no intersection - s = Segment(P3(0.5, -1.0, 0.0), P3(0.5, -1.0, -1.0)) + s = Segment(point(0.5, -1.0, 0.0), point(0.5, -1.0, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment left of t, no intersection - s = Segment(P3(-1.0, 0.5, 0.0), P3(-1.0, 0.5, -1.0)) + s = Segment(point(-1.0, 0.5, 0.0), point(-1.0, 0.5, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment above and to right of t, no intersection - s = Segment(P3(1.0, 1.0, 1.0), P3(1.0, 1.0, 0.0)) + s = Segment(point(1.0, 1.0, 1.0), point(1.0, 1.0, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment below t, no intersection - s = Segment(P3(0.5, -1.0, 1.0), P3(0.5, -1.0, 0.0)) + s = Segment(point(0.5, -1.0, 1.0), point(0.5, -1.0, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment left of t, no intersection - s = Segment(P3(-1.0, 0.5, 1.0), P3(-1.0, 0.5, 0.0)) + s = Segment(point(-1.0, 0.5, 1.0), point(-1.0, 0.5, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # intersections with an inclined inclined triangle t - t = Triangle(P3(0, 0, 0), P3(2, 0, 0), P3(0, 2, 2)) + t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) # doesn't reach t, no intersection - s = Segment(P3(0.5, 0.5, 1.9), P3(0.5, 0.5, 1.8)) + s = Segment(point(0.5, 0.5, 1.9), point(0.5, 0.5, 1.8)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, offset from t, no intersection - s = Segment(P3(0.0, 0.5, 1.0), P3(1.0, 0.5, 1.0)) + s = Segment(point(0.0, 0.5, 1.0), point(1.0, 0.5, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # triangle as first argument - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) - s = Segment(P3(0.2, 0.2, 1.0), P3(0.2, 0.2, -1.0)) + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) @test intersection(t, s) |> type == Intersecting - @test s ∩ t == t ∩ s == P3(0.2, 0.2, 0.0) + @test s ∩ t == t ∩ s == point(0.2, 0.2, 0.0) # type stability tests - s = Segment(P3(0.2, 0.2, 1.0), P3(0.2, 0.2, -1.0)) - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) @inferred someornone(s, t) # https://github.com/JuliaGeometry/Meshes.jl/issues/728 - s = Segment(P3(0.5, 0.5, 0.0), P3(0.5, 0.5, 2.0)) - t = Triangle(P3(1.0, 0.0, 0.0), P3(0.0, 1.0, 0.0), P3(0.0, 0.0, 1.0)) + s = Segment(point(0.5, 0.5, 0.0), point(0.5, 0.5, 2.0)) + t = Triangle(point(1.0, 0.0, 0.0), point(0.0, 1.0, 0.0), point(0.0, 0.0, 1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == P3(0.5, 0.5, 0.0) - s = Segment(P3(0.5, 0.5, 2.0), P3(0.5, 0.5, 0.0)) + @test s ∩ t == t ∩ s == point(0.5, 0.5, 0.0) + s = Segment(point(0.5, 0.5, 2.0), point(0.5, 0.5, 0.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == P3(0.5, 0.5, 0.0) + @test s ∩ t == t ∩ s == point(0.5, 0.5, 0.0) # Intersection for a triangle and a ray - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) # intersects through t - r = Ray(P3(0.2, 0.2, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Crossing - @test r ∩ t == P3(0.2, 0.2, 0.0) + @test r ∩ t == point(0.2, 0.2, 0.0) # origin of ray intersects with middle of triangle - r = Ray(P3(0.2, 0.2, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.2, 0.2, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Touching - @test r ∩ t == P3(0.2, 0.2, 0.0) + @test r ∩ t == point(0.2, 0.2, 0.0) # Special case: the direction vector is not length enough to cross triangle - r = Ray(P3(0.2, 0.2, 1.0), V3(0.0, 0.0, -0.00001)) + r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, -0.00001)) @test intersection(r, t) |> type == Crossing if T == Float64 - @test r ∩ t ≈ P3(0.2, 0.2, 0.0) + @test r ∩ t ≈ point(0.2, 0.2, 0.0) end # Special case: reverse direction vector should not hit the triangle - r = Ray(P3(0.2, 0.2, 1.0), V3(0.0, 0.0, 1.0)) + r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # intersects at a vertex of t - r = Ray(P3(0.0, 0.0, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.0, 0.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == CornerCrossing - @test r ∩ t ≈ P3(0.0, 0.0, 0.0) + @test r ∩ t ≈ point(0.0, 0.0, 0.0) # normal to, doesn't intersect with t - r = Ray(P3(0.9, 0.9, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.9, 0.9, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # coplanar, doesn't intersect with t - r = Ray(P3(-0.2, -0.2, 0.0), V3(1.0, 0.0, 0.0)) + r = Ray(point(-0.2, -0.2, 0.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # parallel, above, doesn't intersect with t - r = Ray(P3(-0.2, 0.2, 1.0), V3(1.0, 0.0, 0.0)) + r = Ray(point(-0.2, 0.2, 1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # parallel, below, doesn't intersect with t - r = Ray(P3(-0.2, 0.2, -1.0), V3(1.0, 0.0, 0.0)) + r = Ray(point(-0.2, 0.2, -1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # coplanar, within bounding box of t, no intersection - r = Ray(P3(0.7, 0.8, 0.0), V3(1.0, -1.0, 0.0)) + r = Ray(point(0.7, 0.8, 0.0), vector(1.0, -1.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(P3(1.0, 1.0, 0.0), V3(0.0, 0.0, 1.0)) + r = Ray(point(1.0, 1.0, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(P3(0.5, -1.0, 0.0), V3(0.0, 0.0, 1.0)) + r = Ray(point(0.5, -1.0, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(P3(-1.0, 0.5, 0.0), V3(0.0, 0.0, 1.0)) + r = Ray(point(-1.0, 0.5, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(P3(1.0, 1.0, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(1.0, 1.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(P3(0.5, -1.0, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.5, -1.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(P3(-1.0, 0.5, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(-1.0, 0.5, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(P3(1.0, 1.0, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(1.0, 1.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(P3(0.5, -1.0, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.5, -1.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(P3(-1.0, 0.5, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(-1.0, 0.5, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # intersections with an inclined inclined triangle t - t = Triangle(P3(0, 0, 0), P3(2, 0, 0), P3(0, 2, 2)) + t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) # doesn't reach t, but a ray can hit the triangle - r = Ray(P3(0.5, 0.5, 1.9), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.5, 0.5, 1.9), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Crossing - @test r ∩ t ≈ P3(0.5, 0.5, 0.5) + @test r ∩ t ≈ point(0.5, 0.5, 0.5) # parallel, offset from t, no intersection - r = Ray(P3(0.0, 0.5, 1.0), V3(1.0, 0.0, 0.0)) + r = Ray(point(0.0, 0.5, 1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # origin of ray intersects with vertex of triangle - r = Ray(P3(0.0, 0.0, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.0, 0.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == CornerTouching - @test r ∩ t ≈ P3(0.0, 0.0, 0.0) + @test r ∩ t ≈ point(0.0, 0.0, 0.0) # origin of ray intersects with edge of triangle - r = Ray(P3(0.5, 0.0, 0.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.5, 0.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == EdgeTouching - @test r ∩ t ≈ P3(0.5, 0.0, 0.0) + @test r ∩ t ≈ point(0.5, 0.0, 0.0) # ray intersects with edge of triangle - r = Ray(P3(0.5, 0.0, 1.0), V3(0.0, 0.0, -1.0)) + r = Ray(point(0.5, 0.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == EdgeCrossing - @test r ∩ t ≈ P3(0.5, 0.0, 0.0) + @test r ∩ t ≈ point(0.5, 0.0, 0.0) end @testset "Ngons" begin o = Octagon( - P3(0.0, 0.0, 1.0), - P3(0.5, -0.5, 0.0), - P3(1.0, 0.0, 0.0), - P3(1.5, 0.5, -0.5), - P3(1.0, 1.0, 0.0), - P3(0.5, 1.5, 0.0), - P3(0.0, 1.0, 0.0), - P3(-0.5, 0.5, 0.0) + point(0.0, 0.0, 1.0), + point(0.5, -0.5, 0.0), + point(1.0, 0.0, 0.0), + point(1.5, 0.5, -0.5), + point(1.0, 1.0, 0.0), + point(0.5, 1.5, 0.0), + point(0.0, 1.0, 0.0), + point(-0.5, 0.5, 0.0) ) - r = Ray(P3(-1.0, -1.0, -1.0), V3(1.0, 1.0, 1.0)) + r = Ray(point(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) @test intersection(r, o) |> type == Intersecting - @test r ∩ o == PointSet([P3(0.0, 0.0, 0.0)]) + @test r ∩ o == PointSet([point(0.0, 0.0, 0.0)]) - r = Ray(P3(-1.0, -1.0, -1.0), V3(-1.0, -1.0, -1.0)) + r = Ray(point(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) @test intersection(r, o) |> type == NotIntersecting @test isnothing(r ∩ o) end @testset "Polygons" begin # triangle - poly = Triangle(P2(6, 2), P2(3, 5), P2(0, 2)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Triangle(point(6, 2), point(3, 5), point(0, 2)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) @test intersection(poly, other) |> type == Intersecting - @test all(vertices(poly ∩ other) .≈ [P2(5, 3), P2(4, 4), P2(2, 4), P2(0, 2), P2(5, 2)]) + @test all(vertices(poly ∩ other) .≈ [point(5, 3), point(4, 4), point(2, 4), point(0, 2), point(5, 2)]) # octagon - poly = Octagon(P2(8, -2), P2(8, 5), P2(2, 5), P2(4, 3), P2(6, 3), P2(4, 1), P2(2, 1), P2(2, -2)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = + Octagon(point(8, -2), point(8, 5), point(2, 5), point(4, 3), point(6, 3), point(4, 1), point(2, 1), point(2, -2)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) @test intersection(poly, other) |> type == Intersecting @test all( - vertices(poly ∩ other) .≈ - [P2(3, 4), P2(4, 3), P2(5, 3), P2(5, 2), P2(4, 1), P2(2, 1), P2(2, 0), P2(5, 0), P2(5, 4)] + vertices(poly ∩ other) .≈ [ + point(3, 4), + point(4, 3), + point(5, 3), + point(5, 2), + point(4, 1), + point(2, 1), + point(2, 0), + point(5, 0), + point(5, 4) + ] ) # inside - poly = Quadrangle(P2(1, 0), P2(1, 1), P2(0, 1), P2(0, 0)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Quadrangle(point(1, 0), point(1, 1), point(0, 1), point(0, 0)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) @test intersection(poly, other) |> type == Intersecting @test all(vertices(poly ∩ other) .≈ vertices(poly)) # outside - poly = Quadrangle(P2(7, 6), P2(7, 7), P2(6, 7), P2(6, 6)) - other = Quadrangle(P2(5, 0), P2(5, 4), P2(0, 4), P2(0, 0)) + poly = Quadrangle(point(7, 6), point(7, 7), point(6, 7), point(6, 6)) + other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) @test intersection(poly, other) |> type == NotIntersecting @test isnothing(poly ∩ other) # convex and non-convex polygons - quad = Quadrangle(P2(0, 0), P2(0.1, 0.0), P2(0.1, 0.1), P2(0.0, 0.1)) - poly = PolyArea(P2(0, 0), P2(2, 0), P2(1, 1), P2(1, 0.5)) + quad = Quadrangle(point(0, 0), point(0.1, 0.0), point(0.1, 0.1), point(0.0, 0.1)) + poly = PolyArea(point(0, 0), point(2, 0), point(1, 1), point(1, 0.5)) @test intersection(quad, poly) |> type == Intersecting - @test all(vertices(quad ∩ poly) .≈ [P2(0, 0), P2(0.1, 0), P2(0.1, 0.05)]) + @test all(vertices(quad ∩ poly) .≈ [point(0, 0), point(0.1, 0), point(0.1, 0.05)]) end @testset "Domains" begin - grid = CartesianGrid{T}(4, 4) + grid = cartgrid(4, 4) pset = PointSet(centroid.(grid)) - ball = Ball(P2(0, 0), T(1)) + ball = Ball(point(0, 0), T(1)) @test pset ∩ pset == pset @test pset ∩ grid == grid ∩ pset == pset - @test pset ∩ ball == ball ∩ pset == PointSet(P2(0.5, 0.5)) + @test pset ∩ ball == ball ∩ pset == PointSet(point(0.5, 0.5)) end end diff --git a/test/matrices.jl b/test/matrices.jl index 1096dd368..8a581a207 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -1,6 +1,6 @@ @testset "Matrices" begin # uniform weights for simple mesh - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) L = laplacematrix(mesh, weights=:uniform) @@ -13,13 +13,13 @@ ] # cotangent weights only defined for triangle meshes - points = P2[(0, 0), (1, 0), (1, 1), (0, 1)] + points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) connec = connect.([(1, 2, 3, 4)], Quadrangle) mesh = SimpleMesh(points, connec) @test_throws AssertionError laplacematrix(mesh, weights=:cotangent) # full Laplace-Beltrami operator - sphere = Sphere(P3(0, 0, 0), T(1)) + sphere = Sphere(point(0, 0, 0), T(1)) mesh = simplexify(sphere) L = laplacematrix(mesh) M = measurematrix(mesh) @@ -28,7 +28,7 @@ @test isdiag(M) # adjacency of CartesianGrid - grid = CartesianGrid{T}(100, 100) + grid = cartgrid(100, 100) A = adjacencymatrix(grid) d = sum(A, dims=2) @test size(A) == (10000, 10000) @@ -39,7 +39,7 @@ @test length(findall(==(2), d)) == 4 # adjacency of SimpleMesh - points = P2[(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)] + points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) mesh = SimpleMesh(points, connec, relations=true) A = adjacencymatrix(mesh) diff --git a/test/merging.jl b/test/merging.jl index da108d4d6..92642692b 100644 --- a/test/merging.jl +++ b/test/merging.jl @@ -1,12 +1,12 @@ @testset "Merging" begin - s = Sphere(P3(0, 0, 0), T(1)) + s = Sphere(point(0, 0, 0), T(1)) c = CylinderSurface(T(1)) m = merge(s, c) @test m isa Multi @test eltype(parent(m)) <: Primitive - s = Sphere(P3(0, 0, 0), T(1)) - b = Box(P3(0, 0, 0), P3(1, 1, 1)) + s = Sphere(point(0, 0, 0), T(1)) + b = Box(point(0, 0, 0), point(1, 1, 1)) ms = Multi([s]) mb = Multi([b]) @test merge(ms, b) == merge(ms, mb) == merge(s, mb) @@ -14,8 +14,8 @@ @test m isa Multi @test eltype(parent(m)) <: Primitive - m1 = SimpleMesh(rand(P3, 3), [connect((1, 2, 3))]) - m2 = SimpleMesh(rand(P3, 4), [connect((1, 2, 3, 4))]) + m1 = SimpleMesh(randpoint3(3), [connect((1, 2, 3))]) + m2 = SimpleMesh(randpoint3(4), [connect((1, 2, 3, 4))]) m = merge(m1, m2) @test m isa Mesh @test eltype(m) <: Ngon diff --git a/test/mesh.jl b/test/mesh.jl index db02a8c49..2d3981151 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -1,32 +1,32 @@ @testset "Meshes" begin @testset "CartesianGrid" begin - grid = CartesianGrid{T}(100) + grid = cartgrid(100) @test embeddim(grid) == 1 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (100,) - @test minimum(grid) == P1(0) - @test maximum(grid) == P1(100) - @test extrema(grid) == (P1(0), P1(100)) - @test spacing(grid) == T.((1,)) + @test minimum(grid) == point(0) + @test maximum(grid) == point(100) + @test extrema(grid) == (point(0), point(100)) + @test spacing(grid) == (T(1) * u"m",) @test nelements(grid) == 100 - @test eltype(grid) <: Segment{1,T} - @test measure(grid) ≈ T(100) + @test eltype(grid) <: Segment{1} + @test measure(grid) ≈ T(100) * u"m" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1] == Segment(P1(0), P1(1)) - @test grid[100] == Segment(P1(99), P1(100)) + @test grid[1] == Segment(point(0), point(1)) + @test grid[100] == Segment(point(99), point(100)) - grid = CartesianGrid{T}(200, 100) + grid = cartgrid(200, 100) @test embeddim(grid) == 2 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) - @test minimum(grid) == P2(0, 0) - @test maximum(grid) == P2(200, 100) - @test extrema(grid) == (P2(0, 0), P2(200, 100)) - @test spacing(grid) == T.((1, 1)) + @test minimum(grid) == point(0, 0) + @test maximum(grid) == point(200, 100) + @test extrema(grid) == (point(0, 0), point(200, 100)) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2,T} - @test measure(grid) ≈ T(200 * 100) + @test eltype(grid) <: Quadrangle{2} + @test measure(grid) ≈ T(200 * 100) * u"m^2" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1] == grid[1] @@ -34,15 +34,15 @@ grid = CartesianGrid((200, 100, 50), T.((0, 0, 0)), T.((1, 1, 1))) @test embeddim(grid) == 3 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100, 50) - @test minimum(grid) == P3(0, 0, 0) - @test maximum(grid) == P3(200, 100, 50) - @test extrema(grid) == (P3(0, 0, 0), P3(200, 100, 50)) - @test spacing(grid) == T.((1, 1, 1)) + @test minimum(grid) == point(0, 0, 0) + @test maximum(grid) == point(200, 100, 50) + @test extrema(grid) == (point(0, 0, 0), point(200, 100, 50)) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 * 50 - @test eltype(grid) <: Hexahedron{3,T} - @test measure(grid) ≈ T(200 * 100 * 50) + @test eltype(grid) <: Hexahedron{3} + @test measure(grid) ≈ T(200 * 100 * 50) * u"m^3" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1, 1] == grid[1] @@ -50,106 +50,114 @@ grid = CartesianGrid(T.((0, 0, 0)), T.((1, 1, 1)), T.((0.1, 0.1, 0.1))) @test embeddim(grid) == 3 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10, 10) - @test minimum(grid) == P3(0, 0, 0) - @test maximum(grid) == P3(1, 1, 1) - @test spacing(grid) == T.((0.1, 0.1, 0.1)) + @test minimum(grid) == point(0, 0, 0) + @test maximum(grid) == point(1, 1, 1) + @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m", T(0.1) * u"m") grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) @test embeddim(grid) == 2 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) - @test minimum(grid) == P2(-1.0, -1.0) - @test maximum(grid) == P2(1.0, 1.0) - @test spacing(grid) == T.((2 / 200, 2 / 100)) + @test minimum(grid) == point(-1.0, -1.0) + @test maximum(grid) == point(1.0, 1.0) + @test spacing(grid) == (T(2 / 200) * u"m", T(2 / 100) * u"m") @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2,T} + @test eltype(grid) <: Quadrangle{2} grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) @test embeddim(grid) == 3 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (20, 10, 5) - @test minimum(grid) == P3(0, 0, 0) - @test maximum(grid) == P3(100, 50, 25) - @test extrema(grid) == (P3(0, 0, 0), P3(100, 50, 25)) - @test spacing(grid) == T.((5, 5, 5)) + @test minimum(grid) == point(0, 0, 0) + @test maximum(grid) == point(100, 50, 25) + @test extrema(grid) == (point(0, 0, 0), point(100, 50, 25)) + @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") @test nelements(grid) == 20 * 10 * 5 - @test eltype(grid) <: Hexahedron{3,T} - @test vertices(grid[1]) == - (P3(0, 0, 0), P3(5, 0, 0), P3(5, 5, 0), P3(0, 5, 0), P3(0, 0, 5), P3(5, 0, 5), P3(5, 5, 5), P3(0, 5, 5)) + @test eltype(grid) <: Hexahedron{3} + @test vertices(grid[1]) == ( + point(0, 0, 0), + point(5, 0, 0), + point(5, 5, 0), + point(0, 5, 0), + point(0, 0, 5), + point(5, 0, 5), + point(5, 5, 5), + point(0, 5, 5) + ) @test all(centroid(grid, i) == centroid(grid[i]) for i in 1:nelements(grid)) # constructor with offset grid = CartesianGrid((10, 10), T.((1.0, 1.0)), T.((1.0, 1.0)), (2, 2)) @test embeddim(grid) == 2 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10) - @test minimum(grid) == P2(0.0, 0.0) - @test maximum(grid) == P2(10.0, 10.0) - @test spacing(grid) == T.((1, 1)) + @test minimum(grid) == point(0.0, 0.0) + @test maximum(grid) == point(10.0, 10.0) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 10 * 10 - @test eltype(grid) <: Quadrangle{2,T} + @test eltype(grid) <: Quadrangle{2} # indexing into a subgrid - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) sub = grid[1:2, 1:2] @test size(sub) == (2, 2) @test spacing(sub) == spacing(grid) @test minimum(sub) == minimum(grid) - @test maximum(sub) == P2(2, 2) + @test maximum(sub) == point(2, 2) sub = grid[1:1, 2:3] @test size(sub) == (1, 2) @test spacing(sub) == spacing(grid) - @test minimum(sub) == P2(0, 1) - @test maximum(sub) == P2(1, 3) + @test minimum(sub) == point(0, 1) + @test maximum(sub) == point(1, 3) sub = grid[2:4, 3:7] @test size(sub) == (3, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == P2(1, 2) - @test maximum(sub) == P2(4, 7) - grid = CartesianGrid(P2(1, 1), P2(11, 11), dims=(10, 10)) + @test minimum(sub) == point(1, 2) + @test maximum(sub) == point(4, 7) + grid = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) sub = grid[2:4, 3:7] @test size(sub) == (3, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == P2(2, 3) - @test maximum(sub) == P2(5, 8) + @test minimum(sub) == point(2, 3) + @test maximum(sub) == point(5, 8) sub = grid[2, 3:7] @test size(sub) == (1, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == P2(2, 3) - @test maximum(sub) == P2(3, 8) + @test minimum(sub) == point(2, 3) + @test maximum(sub) == point(3, 8) sub = grid[:, 3:7] @test size(sub) == (10, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == P2(1, 3) - @test maximum(sub) == P2(11, 8) + @test minimum(sub) == point(1, 3) + @test maximum(sub) == point(11, 8) @test_throws BoundsError grid[3:11, :] # subgrid with comparable vertices of grid - grid = CartesianGrid((10, 10), P2(0.0, 0.0), T.((1.2, 1.2))) + grid = CartesianGrid((10, 10), point(0.0, 0.0), T.((1.2, 1.2))) sub = grid[2:4, 5:7] - @test sub == CartesianGrid((3, 3), P2(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) + @test sub == CartesianGrid((3, 3), point(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) ind = reshape(reshape(1:121, 11, 11)[2:5, 5:8], :) @test vertices(grid)[ind] == vertices(sub) # subgrid from Cartesian ranges - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) sub1 = grid[1:2, 4:6] sub2 = grid[CartesianIndex(1, 4):CartesianIndex(2, 6)] @test sub1 == sub2 - grid = CartesianGrid{T}(200, 100) - @test centroid(grid, 1) == P2(0.5, 0.5) - @test centroid(grid, 2) == P2(1.5, 0.5) - @test centroid(grid, 200 * 100) == P2(199.5, 99.5) + grid = cartgrid(200, 100) + @test centroid(grid, 1) == point(0.5, 0.5) + @test centroid(grid, 2) == point(1.5, 0.5) + @test centroid(grid, 200 * 100) == point(199.5, 99.5) @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2,T} - @test grid[1] == Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - @test grid[2] == Quadrangle(P2(1, 0), P2(2, 0), P2(2, 1), P2(1, 1)) + @test eltype(grid) <: Quadrangle{2} + @test grid[1] == Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test grid[2] == Quadrangle(point(1, 0), point(2, 0), point(2, 1), point(1, 1)) # expand CartesianGrid with comparable vertices - grid = CartesianGrid((10, 10), P2(0.0, 0.0), T.((1.0, 1.0))) + grid = CartesianGrid((10, 10), point(0.0, 0.0), T.((1.0, 1.0))) left, right = (1, 1), (1, 1) newdim = size(grid) .+ left .+ right newoffset = offset(grid) .+ left @@ -157,7 +165,7 @@ @test issubset(vertices(grid), vertices(grid2)) # GridTopology from CartesianGrid - grid = CartesianGrid{T}(5, 5) + grid = cartgrid(5, 5) topo = topology(grid) vs = vertices(grid) for i in 1:nelements(grid) @@ -166,7 +174,7 @@ end # convert topology - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) mesh = topoconvert(HalfEdgeTopology, grid) @test mesh isa SimpleMesh @test nvertices(mesh) == 121 @@ -174,52 +182,51 @@ @test eltype(mesh) <: Quadrangle # single vertex access - grid = CartesianGrid{T}(10, 10) - @test vertex(grid, 1) == P2(0, 0) - @test vertex(grid, 121) == P2(10, 10) + grid = cartgrid(10, 10) + @test vertex(grid, 1) == point(0, 0) + @test vertex(grid, 121) == point(10, 10) # xyz - g1D = CartesianGrid{T}(10) - g2D = CartesianGrid{T}(10, 10) - g3D = CartesianGrid{T}(10, 10, 10) - @test Meshes.xyz(g1D) == (T.(0:10),) - @test Meshes.xyz(g2D) == (T.(0:10), T.(0:10)) - @test Meshes.xyz(g3D) == (T.(0:10), T.(0:10), T.(0:10)) + g1D = cartgrid(10) + g2D = cartgrid(10, 10) + g3D = cartgrid(10, 10, 10) + @test Meshes.xyz(g1D) == (T.(0:10) * u"m",) + @test Meshes.xyz(g2D) == (T.(0:10) * u"m", T.(0:10) * u"m") + @test Meshes.xyz(g3D) == (T.(0:10) * u"m", T.(0:10) * u"m", T.(0:10) * u"m") # XYZ - g1D = CartesianGrid{T}(10) - g2D = CartesianGrid{T}(10, 10) - g3D = CartesianGrid{T}(10, 10, 10) - x = T.(0:10) - y = T.(0:10)' - z = reshape(T.(0:10), 1, 1, 11) + g1D = cartgrid(10) + g2D = cartgrid(10, 10) + g3D = cartgrid(10, 10, 10) + x = T.(0:10) * u"m" + y = T.(0:10)' * u"m" + z = reshape(T.(0:10), 1, 1, 11) * u"m" @test Meshes.XYZ(g1D) == (x,) @test Meshes.XYZ(g2D) == (repeat(x, 1, 11), repeat(y, 11, 1)) @test Meshes.XYZ(g3D) == (repeat(x, 1, 11, 11), repeat(y, 11, 1, 11), repeat(z, 11, 11, 1)) # units - Q = typeof(zero(T) * u"m") - grid = CartesianGrid{Q}(10, 10) + grid = CartesianGrid((10, 10), point(0, 0), (T(1) * u"m", T(1) * u"m")) o = minimum(grid) s = spacing(grid) - @test unit(coordtype(o)) == u"m" - @test Unitful.numtype(coordtype(o)) === T + @test unit(Meshes.lentype(o)) == u"m" + @test Unitful.numtype(Meshes.lentype(o)) === T @test unit(eltype(s)) == u"m" @test Unitful.numtype(eltype(s)) === T - grid = CartesianGrid{T}(200, 100) + grid = cartgrid(200, 100) if T == Float32 @test sprint(show, MIME"text/plain"(), grid) == """ - 200×100 CartesianGrid{2,Float32} - minimum: Point(0.0f0, 0.0f0) - maximum: Point(200.0f0, 100.0f0) - spacing: (1.0f0, 1.0f0)""" + 200×100 CartesianGrid + ├─ minimum: Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ maximum: Point(x: 200.0f0 m, y: 100.0f0 m) + └─ spacing: (1.0f0 m, 1.0f0 m)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), grid) == """ - 200×100 CartesianGrid{2,Float64} - minimum: Point(0.0, 0.0) - maximum: Point(200.0, 100.0) - spacing: (1.0, 1.0)""" + 200×100 CartesianGrid + ├─ minimum: Point(x: 0.0 m, y: 0.0 m) + ├─ maximum: Point(x: 200.0 m, y: 100.0 m) + └─ spacing: (1.0 m, 1.0 m)""" end end @@ -228,45 +235,45 @@ y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) @test embeddim(grid) == 2 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) - @test minimum(grid) == P2(0, 0) - @test maximum(grid) == P2(1, 1) - @test extrema(grid) == (P2(0, 0), P2(1, 1)) + @test minimum(grid) == point(0, 0) + @test maximum(grid) == point(1, 1) + @test extrema(grid) == (point(0, 0), point(1, 1)) @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle{2,T} - @test measure(grid) ≈ T(1) - @test centroid(grid, 1) ≈ P2(0.1, 0.05) - @test centroid(grid[1]) ≈ P2(0.1, 0.05) - @test centroid(grid, 2) ≈ P2(0.3, 0.05) - @test centroid(grid[2]) ≈ P2(0.3, 0.05) + @test eltype(grid) <: Quadrangle{2} + @test measure(grid) ≈ T(1) * u"m^2" + @test centroid(grid, 1) ≈ point(0.1, 0.05) + @test centroid(grid[1]) ≈ point(0.1, 0.05) + @test centroid(grid, 2) ≈ point(0.3, 0.05) + @test centroid(grid[2]) ≈ point(0.3, 0.05) @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1] == grid[1] @test grid[5, 5] == grid[25] sub = grid[2:4, 3:5] @test size(sub) == (3, 3) - @test minimum(sub) == P2(0.2, 0.3) - @test maximum(sub) == P2(0.8, 1.0) + @test minimum(sub) == point(0.2, 0.3) + @test maximum(sub) == point(0.8, 1.0) sub = grid[2, 3:5] @test size(sub) == (1, 3) - @test minimum(sub) == P2(0.2, 0.3) - @test maximum(sub) == P2(0.4, 1.0) + @test minimum(sub) == point(0.2, 0.3) + @test maximum(sub) == point(0.4, 1.0) sub = grid[:, 3:5] @test size(sub) == (5, 3) - @test minimum(sub) == P2(0.0, 0.3) - @test maximum(sub) == P2(1.0, 1.0) + @test minimum(sub) == point(0.0, 0.3) + @test maximum(sub) == point(1.0, 1.0) @test_throws BoundsError grid[2:6, :] - @test Meshes.xyz(grid) == (x, y) - @test Meshes.XYZ(grid) == (repeat(x, 1, 6), repeat(y', 6, 1)) + @test Meshes.xyz(grid) == (x * u"m", y * u"m") + @test Meshes.XYZ(grid) == (repeat(x, 1, 6) * u"m", repeat(y', 6, 1) * u"m") # single vertex access grid = RectilinearGrid(T.(0:10), T.(0:10)) - @test vertex(grid, 1) == P2(0, 0) - @test vertex(grid, 121) == P2(10, 10) + @test vertex(grid, 1) == point(0, 0) + @test vertex(grid, 121) == point(10, 10) # conversion - cg = CartesianGrid{T}(10, 10) + cg = cartgrid(10, 10) rg = convert(RectilinearGrid, cg) @test size(rg) == size(cg) @test nvertices(rg) == nvertices(cg) @@ -274,7 +281,7 @@ @test topology(rg) == topology(cg) @test vertices(rg) == vertices(cg) - cg = CartesianGrid{T}(10, 20, 30) + cg = cartgrid(10, 20, 30) rg = convert(RectilinearGrid, cg) @test size(rg) == size(cg) @test nvertices(rg) == nvertices(cg) @@ -285,22 +292,22 @@ x = range(zero(T), stop=one(T), length=6) y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) - @test sprint(show, grid) == "5×5 RectilinearGrid{2,$T}" + @test sprint(show, grid) == "5×5 RectilinearGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 RectilinearGrid{2,Float32} + 5×5 RectilinearGrid 36 vertices - ├─ Point(0.0f0, 0.0f0) - ├─ Point(0.2f0, 0.0f0) - ├─ Point(0.4f0, 0.0f0) - ├─ Point(0.6f0, 0.0f0) - ├─ Point(0.8f0, 0.0f0) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.2f0 m, y: 0.0f0 m) + ├─ Point(x: 0.4f0 m, y: 0.0f0 m) + ├─ Point(x: 0.6f0 m, y: 0.0f0 m) + ├─ Point(x: 0.8f0 m, y: 0.0f0 m) ⋮ - ├─ Point(0.2f0, 1.0f0) - ├─ Point(0.4f0, 1.0f0) - ├─ Point(0.6f0, 1.0f0) - ├─ Point(0.8f0, 1.0f0) - └─ Point(1.0f0, 1.0f0) + ├─ Point(x: 0.2f0 m, y: 1.0f0 m) + ├─ Point(x: 0.4f0 m, y: 1.0f0 m) + ├─ Point(x: 0.6f0 m, y: 1.0f0 m) + ├─ Point(x: 0.8f0 m, y: 1.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m) 25 elements ├─ Quadrangle(1, 2, 8, 7) ├─ Quadrangle(2, 3, 9, 8) @@ -315,19 +322,19 @@ └─ Quadrangle(29, 30, 36, 35)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 RectilinearGrid{2,Float64} + 5×5 RectilinearGrid 36 vertices - ├─ Point(0.0, 0.0) - ├─ Point(0.2, 0.0) - ├─ Point(0.4, 0.0) - ├─ Point(0.6, 0.0) - ├─ Point(0.8, 0.0) + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 0.2 m, y: 0.0 m) + ├─ Point(x: 0.4 m, y: 0.0 m) + ├─ Point(x: 0.6 m, y: 0.0 m) + ├─ Point(x: 0.8 m, y: 0.0 m) ⋮ - ├─ Point(0.2, 1.0) - ├─ Point(0.4, 1.0) - ├─ Point(0.6, 1.0) - ├─ Point(0.8, 1.0) - └─ Point(1.0, 1.0) + ├─ Point(x: 0.2 m, y: 1.0 m) + ├─ Point(x: 0.4 m, y: 1.0 m) + ├─ Point(x: 0.6 m, y: 1.0 m) + ├─ Point(x: 0.8 m, y: 1.0 m) + └─ Point(x: 1.0 m, y: 1.0 m) 25 elements ├─ Quadrangle(1, 2, 8, 7) ├─ Quadrangle(2, 3, 9, 8) @@ -348,39 +355,39 @@ Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) grid = StructuredGrid(X, Y) @test embeddim(grid) == 2 - @test coordtype(grid) == T + @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) - @test minimum(grid) == P2(0, 0) - @test maximum(grid) == P2(1, 1) - @test extrema(grid) == (P2(0, 0), P2(1, 1)) + @test minimum(grid) == point(0, 0) + @test maximum(grid) == point(1, 1) + @test extrema(grid) == (point(0, 0), point(1, 1)) @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle{2,T} - @test measure(grid) ≈ T(1) - @test centroid(grid, 1) ≈ P2(0.1, 0.05) - @test centroid(grid[1]) ≈ P2(0.1, 0.05) - @test centroid(grid, 2) ≈ P2(0.3, 0.05) - @test centroid(grid[2]) ≈ P2(0.3, 0.05) + @test eltype(grid) <: Quadrangle{2} + @test measure(grid) ≈ T(1) * u"m^2" + @test centroid(grid, 1) ≈ point(0.1, 0.05) + @test centroid(grid[1]) ≈ point(0.1, 0.05) + @test centroid(grid, 2) ≈ point(0.3, 0.05) + @test centroid(grid[2]) ≈ point(0.3, 0.05) @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1] == grid[1] @test grid[5, 5] == grid[25] sub = grid[2:4, 3:5] @test size(sub) == (3, 3) - @test minimum(sub) == P2(0.2, 0.3) - @test maximum(sub) == P2(0.8, 1.0) + @test minimum(sub) == point(0.2, 0.3) + @test maximum(sub) == point(0.8, 1.0) sub = grid[2, 3:5] @test size(sub) == (1, 3) - @test minimum(sub) == P2(0.2, 0.3) - @test maximum(sub) == P2(0.4, 1.0) + @test minimum(sub) == point(0.2, 0.3) + @test maximum(sub) == point(0.4, 1.0) sub = grid[:, 3:5] @test size(sub) == (5, 3) - @test minimum(sub) == P2(0.0, 0.3) - @test maximum(sub) == P2(1.0, 1.0) + @test minimum(sub) == point(0.0, 0.3) + @test maximum(sub) == point(1.0, 1.0) @test_throws BoundsError grid[2:6, :] - @test Meshes.XYZ(grid) == (X, Y) + @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") # conversion - cg = CartesianGrid{T}(10, 10) + cg = cartgrid(10, 10) sg = convert(StructuredGrid, cg) @test size(sg) == size(cg) @test nvertices(sg) == nvertices(cg) @@ -388,7 +395,7 @@ @test topology(sg) == topology(cg) @test vertices(sg) == vertices(cg) - cg = CartesianGrid{T}(10, 20, 30) + cg = cartgrid(10, 20, 30) sg = convert(StructuredGrid, cg) @test size(sg) == size(cg) @test nvertices(sg) == nvertices(cg) @@ -415,22 +422,22 @@ X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) grid = StructuredGrid(X, Y) - @test sprint(show, grid) == "5×5 StructuredGrid{2,$T}" + @test sprint(show, grid) == "5×5 StructuredGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 StructuredGrid{2,Float32} + 5×5 StructuredGrid 36 vertices - ├─ Point(0.0f0, 0.0f0) - ├─ Point(0.2f0, 0.0f0) - ├─ Point(0.4f0, 0.0f0) - ├─ Point(0.6f0, 0.0f0) - ├─ Point(0.8f0, 0.0f0) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.2f0 m, y: 0.0f0 m) + ├─ Point(x: 0.4f0 m, y: 0.0f0 m) + ├─ Point(x: 0.6f0 m, y: 0.0f0 m) + ├─ Point(x: 0.8f0 m, y: 0.0f0 m) ⋮ - ├─ Point(0.2f0, 1.0f0) - ├─ Point(0.4f0, 1.0f0) - ├─ Point(0.6f0, 1.0f0) - ├─ Point(0.8f0, 1.0f0) - └─ Point(1.0f0, 1.0f0) + ├─ Point(x: 0.2f0 m, y: 1.0f0 m) + ├─ Point(x: 0.4f0 m, y: 1.0f0 m) + ├─ Point(x: 0.6f0 m, y: 1.0f0 m) + ├─ Point(x: 0.8f0 m, y: 1.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m) 25 elements ├─ Quadrangle(1, 2, 8, 7) ├─ Quadrangle(2, 3, 9, 8) @@ -445,19 +452,19 @@ └─ Quadrangle(29, 30, 36, 35)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 StructuredGrid{2,Float64} + 5×5 StructuredGrid 36 vertices - ├─ Point(0.0, 0.0) - ├─ Point(0.2, 0.0) - ├─ Point(0.4, 0.0) - ├─ Point(0.6, 0.0) - ├─ Point(0.8, 0.0) + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 0.2 m, y: 0.0 m) + ├─ Point(x: 0.4 m, y: 0.0 m) + ├─ Point(x: 0.6 m, y: 0.0 m) + ├─ Point(x: 0.8 m, y: 0.0 m) ⋮ - ├─ Point(0.2, 1.0) - ├─ Point(0.4, 1.0) - ├─ Point(0.6, 1.0) - ├─ Point(0.8, 1.0) - └─ Point(1.0, 1.0) + ├─ Point(x: 0.2 m, y: 1.0 m) + ├─ Point(x: 0.4 m, y: 1.0 m) + ├─ Point(x: 0.6 m, y: 1.0 m) + ├─ Point(x: 0.8 m, y: 1.0 m) + └─ Point(x: 1.0 m, y: 1.0 m) 25 elements ├─ Quadrangle(1, 2, 8, 7) ├─ Quadrangle(2, 3, 9, 8) @@ -474,15 +481,15 @@ end @testset "SimpleMesh" begin - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) triangles = Triangle.([ - (P2(0.0, 0.0), P2(1.0, 0.0), P2(0.5, 0.5)), - (P2(1.0, 0.0), P2(1.0, 1.0), P2(0.5, 0.5)), - (P2(1.0, 1.0), P2(0.0, 1.0), P2(0.5, 0.5)), - (P2(0.0, 1.0), P2(0.0, 0.0), P2(0.5, 0.5)) + (point(0.0, 0.0), point(1.0, 0.0), point(0.5, 0.5)), + (point(1.0, 0.0), point(1.0, 1.0), point(0.5, 0.5)), + (point(1.0, 1.0), point(0.0, 1.0), point(0.5, 0.5)), + (point(0.0, 1.0), point(0.0, 0.0), point(0.5, 0.5)) ]) @test vertices(mesh) == points @test collect(faces(mesh, 2)) == triangles @@ -491,49 +498,49 @@ for i in 1:length(triangles) @test mesh[i] == triangles[i] end - @test eltype(mesh) <: Triangle{2,T} - @test measure(mesh) ≈ T(1) - @test area(mesh) ≈ T(1) - @test extrema(mesh) == (P2(0, 0), P2(1, 1)) + @test eltype(mesh) <: Triangle{2} + @test measure(mesh) ≈ T(1) * u"m^2" + @test area(mesh) ≈ T(1) * u"m^2" + @test extrema(mesh) == (point(0, 0), point(1, 1)) # test constructors coords = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(coords, SimpleTopology(connec)) - @test eltype(mesh) <: Triangle{2,T} + @test eltype(mesh) <: Triangle{2} @test topology(mesh) isa SimpleTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 mesh = SimpleMesh(coords, connec) - @test eltype(mesh) <: Triangle{2,T} + @test eltype(mesh) <: Triangle{2} @test topology(mesh) isa SimpleTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 mesh = SimpleMesh(coords, connec, relations=true) - @test eltype(mesh) <: Triangle{2,T} + @test eltype(mesh) <: Triangle{2} @test topology(mesh) isa HalfEdgeTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) Δs = connect.([(3, 1, 5), (4, 6, 2)], Triangle) □s = connect.([(1, 2, 6, 5), (5, 6, 4, 3)], Quadrangle) mesh = SimpleMesh(points, [Δs; □s]) elms = [ - Triangle(P2(0.0, 1.0), P2(0.0, 0.0), P2(0.25, 0.5)), - Triangle(P2(1.0, 1.0), P2(0.75, 0.5), P2(1.0, 0.0)), - Quadrangle(P2(0.0, 0.0), P2(1.0, 0.0), P2(0.75, 0.5), P2(0.25, 0.5)), - Quadrangle(P2(0.25, 0.5), P2(0.75, 0.5), P2(1.0, 1.0), P2(0.0, 1.0)) + Triangle(point(0.0, 1.0), point(0.0, 0.0), point(0.25, 0.5)), + Triangle(point(1.0, 1.0), point(0.75, 0.5), point(1.0, 0.0)), + Quadrangle(point(0.0, 0.0), point(1.0, 0.0), point(0.75, 0.5), point(0.25, 0.5)), + Quadrangle(point(0.25, 0.5), point(0.75, 0.5), point(1.0, 1.0), point(0.0, 1.0)) ] @test collect(elements(mesh)) == elms @test nelements(mesh) == 4 for i in 1:length(elms) @test mesh[i] == elms[i] end - @test eltype(mesh) <: Polygon{2,T} + @test eltype(mesh) <: Polygon{2} # test for https://github.com/JuliaGeometry/Meshes.jl/issues/177 - points = P3[(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)] + points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) mesh = SimpleMesh(points, connec) topo = topology(mesh) @@ -541,13 +548,13 @@ @test collect(faces(topo, 3)) == [connect((1, 2, 3, 4), Tetrahedron)] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/187 - points = P3[(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)] + points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) mesh = SimpleMesh(points[4:-1:1], connec) meshvp = SimpleMesh(view(points, 4:-1:1), connec) @test mesh == meshvp - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) bytes = @allocated faces(mesh, 2) @@ -556,30 +563,30 @@ bytes = @allocated collect(cells) @test bytes < 800 - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) - @test centroid(mesh, 1) == centroid(Triangle(P2(0, 0), P2(1, 0), P2(0.5, 0.5))) - @test centroid(mesh, 2) == centroid(Triangle(P2(1, 0), P2(1, 1), P2(0.5, 0.5))) - @test centroid(mesh, 3) == centroid(Triangle(P2(1, 1), P2(0, 1), P2(0.5, 0.5))) - @test centroid(mesh, 4) == centroid(Triangle(P2(0, 1), P2(0, 0), P2(0.5, 0.5))) + @test centroid(mesh, 1) == centroid(Triangle(point(0, 0), point(1, 0), point(0.5, 0.5))) + @test centroid(mesh, 2) == centroid(Triangle(point(1, 0), point(1, 1), point(0.5, 0.5))) + @test centroid(mesh, 3) == centroid(Triangle(point(1, 1), point(0, 1), point(0.5, 0.5))) + @test centroid(mesh, 4) == centroid(Triangle(point(0, 1), point(0, 0), point(0.5, 0.5))) # merge operation with 2D geometries - mesh₁ = SimpleMesh(P2[(0, 0), (1, 0), (0, 1)], connect.([(1, 2, 3)])) - mesh₂ = SimpleMesh(P2[(1, 0), (1, 1), (0, 1)], connect.([(1, 2, 3)])) + mesh₁ = SimpleMesh(point.([(0, 0), (1, 0), (0, 1)]), connect.([(1, 2, 3)])) + mesh₂ = SimpleMesh(point.([(1, 0), (1, 1), (0, 1)]), connect.([(1, 2, 3)])) mesh = merge(mesh₁, mesh₂) @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] @test collect(elements(topology(mesh))) == connect.([(1, 2, 3), (4, 5, 6)]) # merge operation with 3D geometries - mesh₁ = SimpleMesh(P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)], connect.([(1, 2, 3, 4)], Tetrahedron)) - mesh₂ = SimpleMesh(P3[(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)], connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh₁ = SimpleMesh(point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh₂ = SimpleMesh(point.([(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) mesh = merge(mesh₁, mesh₂) @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] @test collect(elements(topology(mesh))) == connect.([(1, 2, 3, 4), (5, 6, 7, 8)], Tetrahedron) # convert any mesh to SimpleMesh - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) mesh = convert(SimpleMesh, grid) @test mesh isa SimpleMesh @test topology(mesh) == GridTopology(10, 10) @@ -588,35 +595,35 @@ @test eltype(mesh) <: Quadrangle # grid interface @test size(mesh) == (10, 10) - @test minimum(mesh) == P2(0, 0) - @test maximum(mesh) == P2(10, 10) - @test extrema(mesh) == (P2(0, 0), P2(10, 10)) + @test minimum(mesh) == point(0, 0) + @test maximum(mesh) == point(10, 10) + @test extrema(mesh) == (point(0, 0), point(10, 10)) @test vertex(mesh, 1) == vertex(mesh, ntuple(i -> 1, embeddim(mesh))) @test vertex(mesh, nvertices(mesh)) == vertex(mesh, size(mesh) .+ 1) @test mesh[1, 1] == mesh[1] @test mesh[10, 10] == mesh[100] sub = mesh[2:4, 3:7] @test size(sub) == (3, 5) - @test minimum(sub) == P2(1, 2) - @test maximum(sub) == P2(4, 7) + @test minimum(sub) == point(1, 2) + @test maximum(sub) == point(4, 7) sub = mesh[2, 3:7] @test size(sub) == (1, 5) - @test minimum(sub) == P2(1, 2) - @test maximum(sub) == P2(2, 7) + @test minimum(sub) == point(1, 2) + @test maximum(sub) == point(2, 7) sub = mesh[:, 3:7] @test size(sub) == (10, 5) - @test minimum(sub) == P2(0, 2) - @test maximum(sub) == P2(10, 7) + @test minimum(sub) == point(0, 2) + @test maximum(sub) == point(10, 7) @test_throws BoundsError grid[3:11, :] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 - points = rand(P2, 5) + points = randpoint2(5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test nvertices(mesh) == length(vertices(mesh)) == 5 # single vertex access - points = rand(P2, 5) + points = randpoint2(5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test vertex(mesh, 1) == points[1] @@ -625,18 +632,19 @@ @test vertex(mesh, 4) == points[4] @test vertex(mesh, 5) == points[5] - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) + @test sprint(show, mesh) == "4 SimpleMesh" if T == Float32 @test sprint(show, MIME"text/plain"(), mesh) == """ - 4 SimpleMesh{2,Float32} + 4 SimpleMesh 5 vertices - ├─ Point(0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0) - ├─ Point(0.0f0, 1.0f0) - ├─ Point(1.0f0, 1.0f0) - └─ Point(0.5f0, 0.5f0) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + └─ Point(x: 0.5f0 m, y: 0.5f0 m) 4 elements ├─ Triangle(1, 2, 5) ├─ Triangle(2, 4, 5) @@ -644,13 +652,13 @@ └─ Triangle(3, 1, 5)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), mesh) == """ - 4 SimpleMesh{2,Float64} + 4 SimpleMesh 5 vertices - ├─ Point(0.0, 0.0) - ├─ Point(1.0, 0.0) - ├─ Point(0.0, 1.0) - ├─ Point(1.0, 1.0) - └─ Point(0.5, 0.5) + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m) + └─ Point(x: 0.5 m, y: 0.5 m) 4 elements ├─ Triangle(1, 2, 5) ├─ Triangle(2, 4, 5) @@ -660,7 +668,7 @@ end @testset "TransformedMesh" begin - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) rgrid = convert(RectilinearGrid, grid) sgrid = convert(StructuredGrid, grid) mesh = convert(SimpleMesh, grid) @@ -685,41 +693,41 @@ tgrid = TransformedMesh(grid, trans) @test tgrid isa TransformedGrid @test size(tgrid) == (10, 10) - @test minimum(tgrid) == P2(0, 0) - @test maximum(tgrid) == P2(10, 10) - @test extrema(tgrid) == (P2(0, 0), P2(10, 10)) + @test minimum(tgrid) == point(0, 0) + @test maximum(tgrid) == point(10, 10) + @test extrema(tgrid) == (point(0, 0), point(10, 10)) @test vertex(tgrid, 1) == vertex(tgrid, ntuple(i -> 1, embeddim(tgrid))) @test vertex(tgrid, nvertices(tgrid)) == vertex(tgrid, size(tgrid) .+ 1) @test tgrid[1, 1] == tgrid[1] @test tgrid[10, 10] == tgrid[100] sub = tgrid[2:4, 3:7] @test size(sub) == (3, 5) - @test minimum(sub) == P2(1, 2) - @test maximum(sub) == P2(4, 7) + @test minimum(sub) == point(1, 2) + @test maximum(sub) == point(4, 7) sub = tgrid[2, 3:7] @test size(sub) == (1, 5) - @test minimum(sub) == P2(1, 2) - @test maximum(sub) == P2(2, 7) + @test minimum(sub) == point(1, 2) + @test maximum(sub) == point(2, 7) sub = tgrid[:, 3:7] @test size(sub) == (10, 5) - @test minimum(sub) == P2(0, 2) - @test maximum(sub) == P2(10, 7) - @test sprint(show, tgrid) == "10×10 TransformedGrid{2,$T}" + @test minimum(sub) == point(0, 2) + @test maximum(sub) == point(10, 7) + @test sprint(show, tgrid) == "10×10 TransformedGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), tgrid) == """ - 10×10 TransformedGrid{2,Float32} + 10×10 TransformedGrid 121 vertices - ├─ Point(0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0) - ├─ Point(2.0f0, 0.0f0) - ├─ Point(3.0f0, 0.0f0) - ├─ Point(4.0f0, 0.0f0) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 2.0f0 m, y: 0.0f0 m) + ├─ Point(x: 3.0f0 m, y: 0.0f0 m) + ├─ Point(x: 4.0f0 m, y: 0.0f0 m) ⋮ - ├─ Point(6.0f0, 10.0f0) - ├─ Point(7.0f0, 10.0f0) - ├─ Point(8.0f0, 10.0f0) - ├─ Point(9.0f0, 10.0f0) - └─ Point(10.0f0, 10.0f0) + ├─ Point(x: 6.0f0 m, y: 10.0f0 m) + ├─ Point(x: 7.0f0 m, y: 10.0f0 m) + ├─ Point(x: 8.0f0 m, y: 10.0f0 m) + ├─ Point(x: 9.0f0 m, y: 10.0f0 m) + └─ Point(x: 10.0f0 m, y: 10.0f0 m) 100 elements ├─ Quadrangle(1, 2, 13, 12) ├─ Quadrangle(2, 3, 14, 13) @@ -734,19 +742,19 @@ └─ Quadrangle(109, 110, 121, 120)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), tgrid) == """ - 10×10 TransformedGrid{2,Float64} + 10×10 TransformedGrid 121 vertices - ├─ Point(0.0, 0.0) - ├─ Point(1.0, 0.0) - ├─ Point(2.0, 0.0) - ├─ Point(3.0, 0.0) - ├─ Point(4.0, 0.0) + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 2.0 m, y: 0.0 m) + ├─ Point(x: 3.0 m, y: 0.0 m) + ├─ Point(x: 4.0 m, y: 0.0 m) ⋮ - ├─ Point(6.0, 10.0) - ├─ Point(7.0, 10.0) - ├─ Point(8.0, 10.0) - ├─ Point(9.0, 10.0) - └─ Point(10.0, 10.0) + ├─ Point(x: 6.0 m, y: 10.0 m) + ├─ Point(x: 7.0 m, y: 10.0 m) + ├─ Point(x: 8.0 m, y: 10.0 m) + ├─ Point(x: 9.0 m, y: 10.0 m) + └─ Point(x: 10.0 m, y: 10.0 m) 100 elements ├─ Quadrangle(1, 2, 13, 12) ├─ Quadrangle(2, 3, 14, 13) diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 9cdee8518..44ca25f58 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -1,7 +1,7 @@ @testset "Multi" begin - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) multi = Multi([poly, poly]) @test multi == multi @@ -13,46 +13,46 @@ @test boundary(multi) == merge(boundary(poly), boundary(poly)) @test rings(multi) == [rings(poly); rings(poly)] - poly1 = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - poly2 = PolyArea(P2[(1, 1), (2, 1), (2, 2), (1, 2)]) + poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) @test vertices(multi) == [vertices(poly1); vertices(poly2)] @test nvertices(multi) == nvertices(poly1) + nvertices(poly2) @test area(multi) == area(poly1) + area(poly2) @test perimeter(multi) == perimeter(poly1) + perimeter(poly2) - @test centroid(multi) == P2(1, 1) - @test P2(0.5, 0.5) ∈ multi - @test P2(1.5, 1.5) ∈ multi - @test P2(1.5, 0.5) ∉ multi - @test P2(0.5, 1.5) ∉ multi + @test centroid(multi) == point(1, 1) + @test point(0.5, 0.5) ∈ multi + @test point(1.5, 1.5) ∈ multi + @test point(1.5, 0.5) ∉ multi + @test point(0.5, 1.5) ∉ multi @test sprint(show, multi) == "Multi(2×PolyArea)" @test sprint(show, MIME"text/plain"(), multi) == """ - MultiPolyArea{2,$T} - ├─ PolyArea((0.0, 0.0), ..., (0.0, 1.0)) - └─ PolyArea((1.0, 1.0), ..., (1.0, 2.0))""" + MultiPolyArea + ├─ PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) + └─ PolyArea((x: 1.0 m, y: 1.0 m), ..., (x: 1.0 m, y: 2.0 m))""" - box1 = Box(P2(0, 0), P2(1, 1)) - box2 = Box(P2(1, 1), P2(2, 2)) + box1 = Box(point(0, 0), point(1, 1)) + box2 = Box(point(1, 1), point(2, 2)) mbox = Multi([box1, box2]) mchn = boundary(mbox) noth = boundary(mchn) @test mchn isa Multi @test isnothing(noth) - @test length(mchn) == T(8) + @test length(mchn) == T(8) * u"m" @test sprint(show, mbox) == "Multi(2×Box)" @test sprint(show, MIME"text/plain"(), mbox) == """ - MultiBox{2,$T} - ├─ Box(min: (0.0, 0.0), max: (1.0, 1.0)) - └─ Box(min: (1.0, 1.0), max: (2.0, 2.0))""" + MultiBox + ├─ Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)) + └─ Box(min: (x: 1.0 m, y: 1.0 m), max: (x: 2.0 m, y: 2.0 m))""" # constructor with iterator - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) multi = Multi(grid) @test parent(multi) == collect(grid) # boundary of multi-3D-geometry - box1 = Box(P3(0, 0, 0), P3(1, 1, 1)) - box2 = Box(P3(1, 1, 1), P3(2, 2, 2)) + box1 = Box(point(0, 0, 0), point(1, 1, 1)) + box2 = Box(point(1, 1, 1), point(2, 2, 2)) mbox = Multi([box1, box2]) mesh = boundary(mbox) @test mesh isa Mesh @@ -60,24 +60,24 @@ @test nelements(mesh) == 12 # unique vertices - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - quad = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) multi = Multi([poly, quad]) @test unique(multi) == multi @test sprint(show, multi) == "Multi(1×PolyArea, 1×Quadrangle)" @test sprint(show, MIME"text/plain"(), multi) == """ - MultiPolygon{2,$T} - ├─ PolyArea((0.0, 0.0), ..., (0.0, 1.0)) - └─ Quadrangle((0.0, 0.0), ..., (0.0, 1.0))""" + MultiPolygon + ├─ PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) + └─ Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))""" # type aliases - point = P2(0, 0) - segm = Segment(P2(0, 0), P2(1, 1)) - rope = Rope(P2[(0, 0), (1, 0), (1, 1)]) - ring = Ring(P2[(0, 0), (1, 0), (1, 1)]) - tri = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - @test Multi([point, point]) isa MultiPoint + p = point(0, 0) + segm = Segment(point(0, 0), point(1, 1)) + rope = Rope(point.([(0, 0), (1, 0), (1, 1)])) + ring = Ring(point.([(0, 0), (1, 0), (1, 1)])) + tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test Multi([p, p]) isa MultiPoint @test Multi([segm, segm]) isa MultiSegment @test Multi([rope, rope]) isa MultiRope @test Multi([ring, ring]) isa MultiRing diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index fb493f036..c01a8fc11 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -3,62 +3,62 @@ 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = BallSearch(𝒟, MetricBall(T(1))) - n = search(P2(0, 0), s) + n = search(point(0, 0), s) @test Set(n) == Set([1, 2, 11]) - n = search(P2(9, 0), s) + n = search(point(9, 0), s) @test Set(n) == Set([9, 10, 20]) - n = search(P2(0, 9), s) + n = search(point(0, 9), s) @test Set(n) == Set([91, 81, 92]) - n = search(P2(9, 9), s) + n = search(point(9, 9), s) @test Set(n) == Set([100, 99, 90]) s = BallSearch(𝒟, MetricBall(T(√2 + eps(T)))) - n = search(P2(0, 0), s) + n = search(point(0, 0), s) @test Set(n) == Set([1, 2, 11, 12]) - n = search(P2(9, 0), s) + n = search(point(9, 0), s) @test Set(n) == Set([9, 10, 19, 20]) - n = search(P2(0, 9), s) + n = search(point(0, 9), s) @test Set(n) == Set([81, 82, 91, 92]) - n = search(P2(9, 9), s) + n = search(point(9, 9), s) @test Set(n) == Set([89, 90, 99, 100]) # non MinkowskiMetric example 𝒟 = CartesianGrid((360, 180), T.((0.0, -90.0)), T.((1.0, 1.0))) s = BallSearch(𝒟, MetricBall(T(150), Haversine(T(6371)))) - n = search(P2(0, 0), s) + n = search(point(0, 0), s) @test Set(n) == Set([32041, 32400, 32401, 32760]) # construct from vector of geometries - s = BallSearch(rand(P2, 100), MetricBall(T(1))) + s = BallSearch(randpoint2(100), MetricBall(T(1))) @test s isa BallSearch end @testset "KNearestSearch" begin 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = KNearestSearch(𝒟, 3) - n = search(P2(0, 0), s) + n = search(point(0, 0), s) @test Set(n) == Set([1, 2, 11]) - n = search(P2(9, 0), s) + n = search(point(9, 0), s) @test Set(n) == Set([9, 10, 20]) - n = search(P2(0, 9), s) + n = search(point(0, 9), s) @test Set(n) == Set([91, 81, 92]) - n = search(P2(9, 9), s) + n = search(point(9, 9), s) @test Set(n) == Set([100, 99, 90]) - n, d = searchdists(P2(9, 9), s) + n, d = searchdists(point(9, 9), s) @test Set(n) == Set([100, 99, 90]) @test length(d) == 3 n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, P2(9, 9), s) + nn = search!(n, point(9, 9), s) @test nn == 3 @test Set(n[1:nn]) == Set([100, 99, 90]) n = Vector{Int}(undef, maxneighbors(s)) - d = Vector{T}(undef, maxneighbors(s)) - nn = searchdists!(n, d, P2(9, 9), s) + d = Vector{ℳ}(undef, maxneighbors(s)) + nn = searchdists!(n, d, point(9, 9), s) @test nn == 3 @test Set(n[1:nn]) == Set([100, 99, 90]) # construct from vector of geometries - s = KNearestSearch(rand(P2, 100), 3) + s = KNearestSearch(randpoint2(100), 3) @test s isa KNearestSearch end @@ -66,48 +66,48 @@ 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = KBallSearch(𝒟, 10, MetricBall(T(100))) - n = search(P2(5, 5), s) + n = search(point(5, 5), s) @test length(n) == 10 s = KBallSearch(𝒟, 10, MetricBall(T.((100, 100)))) - n = search(P2(5, 5), s) + n = search(point(5, 5), s) @test length(n) == 10 s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n = search(P2(5, 5), s) + n = search(point(5, 5), s) @test length(n) == 5 @test n[1] == 56 s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n, d = searchdists(P2(5, 5), s) + n, d = searchdists(point(5, 5), s) @test length(n) == 5 @test length(d) == 5 s = KBallSearch(𝒟, 10, MetricBall(T(1))) n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, P2(5, 5), s) + nn = search!(n, point(5, 5), s) @test nn == 5 s = KBallSearch(𝒟, 10, MetricBall(T(1))) n = Vector{Int}(undef, maxneighbors(s)) - d = Vector{T}(undef, maxneighbors(s)) - nn = searchdists!(n, d, P2(5, 5), s) + d = Vector{ℳ}(undef, maxneighbors(s)) + nn = searchdists!(n, d, point(5, 5), s) @test nn == 5 mask = trues(nelements(𝒟)) mask[56] = false - n = search(P2(5, 5), s, mask=mask) + n = search(point(5, 5), s, mask=mask) @test length(n) == 4 - n = search(P2(-0.2, -0.2), s) + n = search(point(-0.2, -0.2), s) @test length(n) == 1 - n = search(P2(-10, -10), s) + n = search(point(-10, -10), s) @test length(n) == 0 - n, d = searchdists(P2(5, 5), s, mask=mask) + n, d = searchdists(point(5, 5), s, mask=mask) @test length(n) == 4 @test length(d) == 4 # construct from vector of geometries - s = KBallSearch(rand(P2, 100), 10, MetricBall(T(1))) + s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) @test s isa KBallSearch end end diff --git a/test/orientation.jl b/test/orientation.jl index 5fa63451b..84e6f0a74 100644 --- a/test/orientation.jl +++ b/test/orientation.jl @@ -1,12 +1,12 @@ @testset "orientation" begin # test orientation - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test orientation(t) == CCW - t = Triangle(P2(0, 0), P2(0, 1), P2(1, 0)) + t = Triangle(point(0, 0), point(0, 1), point(1, 0)) @test orientation(t) == CW # orientation of 3D rings in X-Y plane - r1 = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - r2 = Ring(P3[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]) + r1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + r2 = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test orientation(r1) == orientation(r2) end diff --git a/test/partitioning.jl b/test/partitioning.jl index f7841a6da..4ed11aabf 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -1,27 +1,27 @@ @testset "Partitioning" begin setify(lists) = Set(Set.(lists)) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p = partition(g, UniformPartition(100)) @test parent(p) == g @test length(p) == 100 @testset "UniformPartition" begin rng = StableRNG(123) - g = CartesianGrid{T}(3, 3) + g = cartgrid(3, 3) p = partition(rng, g, UniformPartition(3, false)) @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) rng = StableRNG(123) p = partition(rng, g, UniformPartition(3)) @test setify(indices(p)) == setify([[5, 4, 2], [6, 7, 8], [9, 3, 1]]) - g = CartesianGrid{T}(2, 3) + g = cartgrid(2, 3) p = partition(g, UniformPartition(3, false)) @test setify(indices(p)) == setify([[1, 2], [3, 4], [5, 6]]) # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, UniformPartition(3)) rng = StableRNG(123) p2 = partition(rng, g, UniformPartition(3)) @@ -29,7 +29,7 @@ end @testset "DirectionPartition" begin - g = CartesianGrid{T}(3, 3) + g = cartgrid(3, 3) # basic checks on small grids p = partition(g, DirectionPartition(T.((1, 0)))) @@ -54,7 +54,7 @@ # partition of arbitrarily large grid always # returns the "lines" and "columns" for n in [10, 100, 200] - g = CartesianGrid{T}(n, n) + g = cartgrid(n, n) p = partition(g, DirectionPartition(T.((1, 0)))) @test setify(indices(p)) == setify([collect(((i - 1) * n + 1):(i * n)) for i in 1:n]) @@ -69,7 +69,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, DirectionPartition(T.((1, 0)))) rng = StableRNG(123) p2 = partition(rng, g, DirectionPartition(T.((1, 0)))) @@ -77,7 +77,7 @@ end @testset "FractionPartition" begin - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p = partition(g, FractionPartition(T(0.5))) @test nelements(p[1]) == nelements(p[2]) == 50 @@ -93,7 +93,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, FractionPartition(T(0.5))) rng = StableRNG(123) p2 = partition(rng, g, FractionPartition(T(0.5))) @@ -101,7 +101,7 @@ end @testset "BlockPartition" begin - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p = partition(g, BlockPartition(T(5), T(5))) @test length(p) == 4 @@ -111,7 +111,7 @@ @test length(p) == 12 @test Set(nelements.(p)) == Set([5, 10]) - g = CartesianGrid{T}(50, 50, 50) + g = cartgrid(50, 50, 50) p = partition(g, BlockPartition(T(1.0), T(1.0), T(1.0), neighbors=false)) @test length(p) == 125000 @@ -127,7 +127,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, BlockPartition(T(5), T(2))) rng = StableRNG(123) p2 = partition(rng, g, BlockPartition(T(5), T(2))) @@ -169,7 +169,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) rng = StableRNG(123) p2 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) @@ -202,7 +202,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) rng = StableRNG(123) p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) @@ -231,7 +231,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, BallPartition(T(2))) rng = StableRNG(123) p2 = partition(rng, g, BallPartition(T(2))) @@ -249,7 +249,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, PlanePartition(T.((1, 0)))) rng = StableRNG(123) p2 = partition(rng, g, PlanePartition(T.((1, 0)))) @@ -267,7 +267,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, partitioner) rng = StableRNG(123) p2 = partition(rng, g, partitioner) @@ -278,14 +278,14 @@ g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) # check if there are 100 partitions, each one having only 1 point - sp = SpatialPredicatePartition((x, y) -> norm(x - y) < T(1)) + sp = SpatialPredicatePartition((x, y) -> norm(x - y) < T(1) * u"m") s = indices(partition(g, sp)) @test length(s) == 100 nelms = [nelements(d) for d in partition(g, sp)] @test all(nelms .== 1) # defining a predicate to check if points x and y belong to the square [0.,5.]x[0.,5.] - pred(x, y) = all(T[0, 0] .<= x .<= T[5, 5]) && all(T[0, 0] .<= y .<= T[5, 5]) + pred(x, y) = all(T[0, 0] * u"m" .<= x .<= T[5, 5] * u"m") && all(T[0, 0] * u"m" .<= y .<= T[5, 5] * u"m") sp = SpatialPredicatePartition(pred) p = partition(g, sp) s = indices(p) @@ -301,7 +301,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, sp) rng = StableRNG(123) p2 = partition(rng, g, sp) @@ -329,7 +329,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, bmn) rng = StableRNG(123) p2 = partition(rng, g, bmn) @@ -349,7 +349,7 @@ # reproducible results with rng rng = StableRNG(123) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p1 = partition(rng, g, bmn) rng = StableRNG(123) p2 = partition(rng, g, bmn) diff --git a/test/pointification.jl b/test/pointification.jl index 533b7fc28..6406da54e 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -1,50 +1,50 @@ @testset "Pointification" begin - point = P2(0, 0) - @test pointify(point) == [P2(0, 0)] + p = point(0, 0) + @test pointify(p) == [point(0, 0)] - sphere = Sphere(P2(0, 0), T(1)) + sphere = Sphere(point(0, 0), T(1)) points = pointify(sphere) @test all(∈(sphere), points) - ball = Ball(P2(0, 0), T(1)) + ball = Ball(point(0, 0), T(1)) points = pointify(ball) @test all(∈(boundary(ball)), points) - verts = [P2(0, 0), P2(1, 1)] + verts = [point(0, 0), point(1, 1)] segment = Segment(verts...) points = pointify(segment) @test points == verts - verts = [P2(0, 0), P2(1, 0), P2(1, 1)] + verts = [point(0, 0), point(1, 0), point(1, 1)] triangle = Triangle(verts...) points = pointify(triangle) @test points == verts - verts = [P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)] + verts = [point(0, 0), point(1, 0), point(1, 1), point(0, 1)] quadrangle = Quadrangle(verts...) points = pointify(quadrangle) @test points == verts - tri = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - quad = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) + quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) multi = Multi([tri, quad]) points = pointify(multi) @test points == [pointify(tri); pointify(quad)] - tri = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - quad = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) + quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) gset = GeometrySet([tri, quad]) points = pointify(gset) @test points == [pointify(tri); pointify(quad)] - pts = rand(P2, 100) + pts = randpoint2(100) pset = PointSet(pts) @test pointify(pset) == pts - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) @test pointify(grid) == vertices(grid) - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) mesh = convert(SimpleMesh, grid) points = pointify(mesh) @test points == vertices(mesh) diff --git a/test/polytopes.jl b/test/polytopes.jl index 31ea94c6a..ce8b1a1c5 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -3,37 +3,37 @@ @test paramdim(Segment) == 1 @test nvertices(Segment) == 2 - s = Segment(P1(1.0), P1(2.0)) - @test vertex(s, 1) == P1(1.0) - @test vertex(s, 2) == P1(2.0) - @test all(P1(x) ∈ s for x in 1:0.01:2) - @test all(P1(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) + s = Segment(point(1.0), point(2.0)) + @test vertex(s, 1) == point(1.0) + @test vertex(s, 2) == point(2.0) + @test all(point(x) ∈ s for x in 1:0.01:2) + @test all(point(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) @test s ≈ s - @test !(s ≈ Segment(P1(2.0), P1(1.0))) - @test !(s ≈ Segment(P1(-1.0), P1(2.0))) - - s = Segment(P2(0, 0), P2(1, 1)) - @test minimum(s) == P2(0, 0) - @test maximum(s) == P2(1, 1) - @test extrema(s) == (P2(0, 0), P2(1, 1)) - @test isapprox(length(s), sqrt(T(2))) - @test s(T(0)) == P2(0, 0) - @test s(T(1)) == P2(1, 1) - @test all(P2(x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [P2(-0.1, -0.1), P2(1.1, 1.1), P2(0.5, 0.49), P2(1, 2)]) + @test !(s ≈ Segment(point(2.0), point(1.0))) + @test !(s ≈ Segment(point(-1.0), point(2.0))) + + s = Segment(point(0, 0), point(1, 1)) + @test minimum(s) == point(0, 0) + @test maximum(s) == point(1, 1) + @test extrema(s) == (point(0, 0), point(1, 1)) + @test isapprox(length(s), sqrt(T(2)) * u"m") + @test s(T(0)) == point(0, 0) + @test s(T(1)) == point(1, 1) + @test all(point(x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [point(-0.1, -0.1), point(1.1, 1.1), point(0.5, 0.49), point(1, 2)]) @test_throws DomainError(T(1.2), "s(t) is not defined for t outside [0, 1].") s(T(1.2)) @test_throws DomainError(T(-0.5), "s(t) is not defined for t outside [0, 1].") s(T(-0.5)) @test s ≈ s - @test !(s ≈ Segment(P2(1, 1), P2(0, 0))) - @test !(s ≈ Segment(P2(1, 2), P2(0, 0))) + @test !(s ≈ Segment(point(1, 1), point(0, 0))) + @test !(s ≈ Segment(point(1, 2), point(0, 0))) - s = Segment(P3(0, 0, 0), P3(1, 1, 1)) - @test all(P3(x, x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [P3(-0.1, -0.1, -0.1), P3(1.1, 1.1, 1.1)]) - @test all(p ∉ s for p in [P3(0.5, 0.5, 0.49), P3(1, 1, 2)]) + s = Segment(point(0, 0, 0), point(1, 1, 1)) + @test all(point(x, x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [point(-0.1, -0.1, -0.1), point(1.1, 1.1, 1.1)]) + @test all(p ∉ s for p in [point(0.5, 0.5, 0.49), point(1, 1, 2)]) @test s ≈ s - @test !(s ≈ Segment(P3(1, 1, 1), P3(0, 0, 0))) - @test !(s ≈ Segment(P3(1, 1, 1), P3(0, 1, 0))) + @test !(s ≈ Segment(point(1, 1, 1), point(0, 0, 0))) + @test !(s ≈ Segment(point(1, 1, 1), point(0, 1, 0))) s = Segment(Point(1.0, 1.0, 1.0, 1.0), Point(2.0, 2.0, 2.0, 2.0)) @test all(Point(x, x, x, x) ∈ s for x in 1:0.01:2) @@ -43,11 +43,11 @@ @test !(s ≈ Segment(Point(2, 2, 2, 2), Point(1, 1, 1, 1))) @test !(s ≈ Segment(Point(1, 1, 2, 1), Point(0, 0, 0, 0))) - s = Segment(P3(0, 0, 0), P3(1, 1, 1)) - @test boundary(s) == Multi([P3(0, 0, 0), P3(1, 1, 1)]) - @test perimeter(s) == zero(T) - @test center(s) == P3(0.5, 0.5, 0.5) - @test coordtype(center(s)) == T + s = Segment(point(0, 0, 0), point(1, 1, 1)) + @test boundary(s) == Multi([point(0, 0, 0), point(1, 1, 1)]) + @test perimeter(s) == zero(T) * u"m" + @test center(s) == point(0.5, 0.5, 0.5) + @test Meshes.lentype(center(s)) == ℳ # unitful coordinates x1 = T(0)u"m" @@ -57,114 +57,114 @@ @test perimeter(s) == 0u"m" xm = T(0.5)u"m" @test center(s) == Point(xm, xm, xm) - @test coordtype(center(s)) == typeof(xm) + @test Meshes.lentype(center(s)) == typeof(xm) - s = rand(Segment{2,T}) + s = rand(Segment{2}) @test s isa Segment @test embeddim(s) == 2 - @test coordtype(s) === T - s = rand(Segment{3,T}) + @test Meshes.lentype(s) === Meshes.Met{Float64} + s = rand(Segment{3}) @test s isa Segment @test embeddim(s) == 3 - @test coordtype(s) === T + @test Meshes.lentype(s) === Meshes.Met{Float64} - s = Segment(P2(0, 0), P2(1, 1)) - @test sprint(show, s) == "Segment((0.0, 0.0), (1.0, 1.0))" + s = Segment(point(0, 0), point(1, 1)) + @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), s) == """ - Segment{2,Float32} - ├─ Point(0.0f0, 0.0f0) - └─ Point(1.0f0, 1.0f0)""" + Segment + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), s) == """ - Segment{2,Float64} - ├─ Point(0.0, 0.0) - └─ Point(1.0, 1.0)""" + Segment + ├─ Point(x: 0.0 m, y: 0.0 m) + └─ Point(x: 1.0 m, y: 1.0 m)""" end end @testset "Ropes/Rings" begin - c1 = Rope(P2[(1, 1), (2, 2)]) - c2 = Rope(P2(1, 1), P2(2, 2)) + c1 = Rope(point.([(1, 1), (2, 2)])) + c2 = Rope(point(1, 1), point(2, 2)) c3 = Rope(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 - c1 = Ring(P2[(1, 1), (2, 2)]) - c2 = Ring(P2(1, 1), P2(2, 2)) + c1 = Ring(point.([(1, 1), (2, 2)])) + c2 = Ring(point(1, 1), point(2, 2)) c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 - c = Rope(P2[(1, 1), (2, 2)]) - @test vertex(c, 1) == P2(1, 1) - @test vertex(c, 2) == P2(2, 2) - c = Ring(P2[(1, 1), (2, 2)]) - @test vertex(c, 0) == P2(2, 2) - @test vertex(c, 1) == P2(1, 1) - @test vertex(c, 2) == P2(2, 2) - @test vertex(c, 3) == P2(1, 1) - @test vertex(c, 4) == P2(2, 2) - - c = Rope(P2[(1, 1), (2, 2), (3, 3)]) - @test collect(segments(c)) == [Segment(P2(1, 1), P2(2, 2)), Segment(P2(2, 2), P2(3, 3))] - c = Ring(P2[(1, 1), (2, 2), (3, 3)]) + c = Rope(point.([(1, 1), (2, 2)])) + @test vertex(c, 1) == point(1, 1) + @test vertex(c, 2) == point(2, 2) + c = Ring(point.([(1, 1), (2, 2)])) + @test vertex(c, 0) == point(2, 2) + @test vertex(c, 1) == point(1, 1) + @test vertex(c, 2) == point(2, 2) + @test vertex(c, 3) == point(1, 1) + @test vertex(c, 4) == point(2, 2) + + c = Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test collect(segments(c)) == [Segment(point(1, 1), point(2, 2)), Segment(point(2, 2), point(3, 3))] + c = Ring(point.([(1, 1), (2, 2), (3, 3)])) @test collect(segments(c)) == - [Segment(P2(1, 1), P2(2, 2)), Segment(P2(2, 2), P2(3, 3)), Segment(P2(3, 3), P2(1, 1))] + [Segment(point(1, 1), point(2, 2)), Segment(point(2, 2), point(3, 3)), Segment(point(3, 3), point(1, 1))] - c = Rope(P2[(1, 1), (2, 2), (2, 2), (3, 3)]) - @test unique(c) == Rope(P2[(1, 1), (2, 2), (3, 3)]) - @test c == Rope(P2[(1, 1), (2, 2), (2, 2), (3, 3)]) + c = Rope(point.([(1, 1), (2, 2), (2, 2), (3, 3)])) + @test unique(c) == Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test c == Rope(point.([(1, 1), (2, 2), (2, 2), (3, 3)])) unique!(c) - @test c == Rope(P2[(1, 1), (2, 2), (3, 3)]) + @test c == Rope(point.([(1, 1), (2, 2), (3, 3)])) - c = Rope(P2[(1, 1), (2, 2), (3, 3)]) - @test close(c) == Ring(P2[(1, 1), (2, 2), (3, 3)]) - c = Ring(P2[(1, 1), (2, 2), (3, 3)]) - @test open(c) == Rope(P2[(1, 1), (2, 2), (3, 3)]) + c = Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test close(c) == Ring(point.([(1, 1), (2, 2), (3, 3)])) + c = Ring(point.([(1, 1), (2, 2), (3, 3)])) + @test open(c) == Rope(point.([(1, 1), (2, 2), (3, 3)])) - c = Rope(P2[(1, 1), (2, 2), (3, 3)]) + c = Rope(point.([(1, 1), (2, 2), (3, 3)])) reverse!(c) - @test c == Rope(P2[(3, 3), (2, 2), (1, 1)]) - c = Rope(P2[(1, 1), (2, 2), (3, 3)]) - @test reverse(c) == Rope(P2[(3, 3), (2, 2), (1, 1)]) + @test c == Rope(point.([(3, 3), (2, 2), (1, 1)])) + c = Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test reverse(c) == Rope(point.([(3, 3), (2, 2), (1, 1)])) - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test angles(c) ≈ [-π / 2, -π / 2, -π / 2, -π / 2] - c = Rope(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + c = Rope(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test angles(c) ≈ [-π / 2, -π / 2] - c = Ring(P2[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) @test angles(c) ≈ [-atan(2), -π / 2, +π / 2, -π / 2, -π / 2, -(π - atan(2))] @test innerangles(c) ≈ [atan(2), π / 2, 3π / 2, π / 2, π / 2, π - atan(2)] - c1 = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + c1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) c2 = Ring(vertices(c1)) @test c1 == c2 - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - @test centroid(c) == P2(0.5, 0.5) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(c) == point(0.5, 0.5) - c = Rope(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - @test boundary(c) == Multi(P2[(0, 0), (0, 1)]) - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + c = Rope(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test boundary(c) == Multi(point.([(0, 0), (0, 1)])) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test isnothing(boundary(c)) # should not repeat the first vertex manually - @test_throws ArgumentError Ring(P2[(0, 0), (0, 0)]) - @test_throws ArgumentError Ring(P2[(0, 0), (1, 0), (1, 1), (0, 0)]) + @test_throws ArgumentError Ring(point.([(0, 0), (0, 0)])) + @test_throws ArgumentError Ring(point.([(0, 0), (1, 0), (1, 1), (0, 0)])) # degenerate rings with 1 or 2 vertices are allowed - r = Ring(P2[(0, 0)]) + r = Ring(point.([(0, 0)])) @test isclosed(r) @test nvertices(r) == 1 - @test collect(segments(r)) == [Segment(P2(0, 0), P2(0, 0))] - r = Ring(P2[(0, 0), (1, 1)]) + @test collect(segments(r)) == [Segment(point(0, 0), point(0, 0))] + r = Ring(point.([(0, 0), (1, 1)])) @test isclosed(r) @test nvertices(r) == 2 - @test collect(segments(r)) == [Segment(P2(0, 0), P2(1, 1)), Segment(P2(1, 1), P2(0, 0))] + @test collect(segments(r)) == [Segment(point(0, 0), point(1, 1)), Segment(point(1, 1), point(0, 0))] - p1 = P2(1, 1) - p2 = P2(3, 1) - p3 = P2(1, 0) - p4 = P2(3, 0) - pts = P2[(0, 0), (2, 2), (4, 0)] + p1 = point(1, 1) + p2 = point(3, 1) + p3 = point(1, 0) + p4 = point(3, 0) + pts = point.([(0, 0), (2, 2), (4, 0)]) r = Ring(pts) @test p1 ∈ r @test p2 ∈ r @@ -177,27 +177,30 @@ @test p4 ∉ r # approximately equal vertices - pts = P2[ - (-48.04448403189499, -18.326530800015174) - (-48.044478457836675, -18.326503670869467) - (-48.04447845783733, -18.326503670869915) - (-48.04447835073269, -18.326503149587666) - (-48.044468448930644, -18.326490894176693) - (-48.04447208741723, -18.326486301018672) - (-48.044459173572015, -18.32646700775326) - (-48.04445616736389, -18.326461847186216) - (-48.044459897846174, -18.326466190774774) - (-48.044462696066695, -18.32646303439271) - (-48.044473299571635, -18.326478565399572) - (-48.044473299571635, -18.326478565399565) - (-48.044484052460334, -18.326494315209573) - (-48.04449288424675, -18.326504598503668) - (-48.044492356262886, -18.32650647783081) - (-48.0444943180541, -18.326509351276243) - (-48.044492458690776, -18.32651322842786) - (-48.04450917793127, -18.326524641668517) - (-48.044501408820125, -18.326551273900744) - ] + pts = + point.( + [ + (-48.04448403189499, -18.326530800015174) + (-48.044478457836675, -18.326503670869467) + (-48.04447845783733, -18.326503670869915) + (-48.04447835073269, -18.326503149587666) + (-48.044468448930644, -18.326490894176693) + (-48.04447208741723, -18.326486301018672) + (-48.044459173572015, -18.32646700775326) + (-48.04445616736389, -18.326461847186216) + (-48.044459897846174, -18.326466190774774) + (-48.044462696066695, -18.32646303439271) + (-48.044473299571635, -18.326478565399572) + (-48.044473299571635, -18.326478565399565) + (-48.044484052460334, -18.326494315209573) + (-48.04449288424675, -18.326504598503668) + (-48.044492356262886, -18.32650647783081) + (-48.0444943180541, -18.326509351276243) + (-48.044492458690776, -18.32651322842786) + (-48.04450917793127, -18.326524641668517) + (-48.044501408820125, -18.326551273900744) + ] + ) r1 = Rope(pts) r2 = Ring(pts) ur1 = unique(r1) @@ -212,66 +215,66 @@ @test nvertices(ur2) == 17 end - r = rand(Rope{2,T}) + r = rand(Rope{2}) @test r isa Rope @test embeddim(r) == 2 - @test coordtype(r) === T - r = rand(Rope{3,T}) + @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Rope{3}) @test r isa Rope @test embeddim(r) == 3 - @test coordtype(r) === T + @test Meshes.lentype(r) === Meshes.Met{Float64} - r = rand(Ring{2,T}) + r = rand(Ring{2}) @test r isa Ring @test embeddim(r) == 2 - @test coordtype(r) === T - r = rand(Ring{3,T}) + @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Ring{3}) @test r isa Ring @test embeddim(r) == 3 - @test coordtype(r) === T + @test Meshes.lentype(r) === Meshes.Met{Float64} # issimple benchmark - r = Sphere(P2(0, 0), T(1)) |> pointify |> Ring + r = Sphere(point(0, 0), T(1)) |> pointify |> Ring @test issimple(r) @test @elapsed(issimple(r)) < 0.02 @test @allocated(issimple(r)) < 950000 # innerangles in 3D is obtained via projection - r1 = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - r2 = Ring(P3[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]) + r1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + r2 = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test innerangles(r1) ≈ innerangles(r2) - ri = Ring(P2[(1, 1), (2, 2), (3, 3)]) - ro = Rope(P2[(1, 1), (2, 2), (3, 3)]) - @test sprint(show, ri) == "Ring((1.0, 1.0), (2.0, 2.0), (3.0, 3.0))" - @test sprint(show, ro) == "Rope((1.0, 1.0), (2.0, 2.0), (3.0, 3.0))" + ri = Ring(point.([(1, 1), (2, 2), (3, 3)])) + ro = Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test sprint(show, ri) == "Ring((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" + @test sprint(show, ro) == "Rope((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), ri) == """ - Ring{2,Float32} - ├─ Point(1.0f0, 1.0f0) - ├─ Point(2.0f0, 2.0f0) - └─ Point(3.0f0, 3.0f0)""" + Ring + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" @test sprint(show, MIME("text/plain"), ro) == """ - Rope{2,Float32} - ├─ Point(1.0f0, 1.0f0) - ├─ Point(2.0f0, 2.0f0) - └─ Point(3.0f0, 3.0f0)""" + Rope + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" else @test sprint(show, MIME("text/plain"), ri) == """ - Ring{2,Float64} - ├─ Point(1.0, 1.0) - ├─ Point(2.0, 2.0) - └─ Point(3.0, 3.0)""" + Ring + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + └─ Point(x: 3.0 m, y: 3.0 m)""" @test sprint(show, MIME("text/plain"), ro) == """ - Rope{2,Float64} - ├─ Point(1.0, 1.0) - ├─ Point(2.0, 2.0) - └─ Point(3.0, 3.0)""" + Rope + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + └─ Point(x: 3.0 m, y: 3.0 m)""" end end @testset "Ngons" begin - pts = (P2(0, 0), P2(1, 0), P2(0, 1)) + pts = (point(0, 0), point(1, 0), point(0, 1)) tups = (T.((0, 0)), T.((1, 0)), T.((0, 1))) @test paramdim(Ngon) == 2 @test vertices(Ngon(pts)) == pts @@ -287,58 +290,58 @@ @test paramdim(NGON) == 2 @test nvertices(NGON) == NVERT[i] - n = rand(NGON{2,T}) + n = rand(NGON{2}) @test n isa NGON @test embeddim(n) == 2 - @test coordtype(n) === T - n = rand(NGON{3,T}) + @test Meshes.lentype(n) === Meshes.Met{Float64} + n = rand(NGON{3}) @test n isa NGON @test embeddim(n) == 3 - @test coordtype(n) === T + @test Meshes.lentype(n) === Meshes.Met{Float64} end # error: the number of vertices must be greater than or equal to 3 - @test_throws ArgumentError Ngon(P2(0, 0), P2(1, 1)) - @test_throws ArgumentError Ngon{2}(P2(0, 0), P2(1, 1)) + @test_throws ArgumentError Ngon(point(0, 0), point(1, 1)) + @test_throws ArgumentError Ngon{2}(point(0, 0), point(1, 1)) # --------- # TRIANGLE # --------- # Triangle in 2D space - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - @test vertex(t, 1) == P2(0, 0) - @test vertex(t, 2) == P2(1, 0) - @test vertex(t, 3) == P2(0, 1) - @test signarea(t) == T(0.5) - @test area(t) == T(0.5) - t = Triangle(P2(0, 0), P2(0, 1), P2(1, 0)) - @test signarea(t) == T(-0.5) - @test area(t) == T(0.5) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - for p in P2[(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)] + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test vertex(t, 1) == point(0, 0) + @test vertex(t, 2) == point(1, 0) + @test vertex(t, 3) == point(0, 1) + @test signarea(t) == T(0.5) * u"m^2" + @test area(t) == T(0.5) * u"m^2" + t = Triangle(point(0, 0), point(0, 1), point(1, 0)) + @test signarea(t) == T(-0.5) * u"m^2" + @test area(t) == T(0.5) * u"m^2" + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + for p in point.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) @test p ∈ t end - for p in P2[(-1, 0), (0, -1), (0.5, 1.0)] + for p in point.([(-1, 0), (0, -1), (0.5, 1.0)]) @test p ∉ t end - t = Triangle(P2(0.4, 0.4), P2(0.6, 0.4), P2(0.8, 0.4)) - @test P2(0.2, 0.4) ∉ t - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - @test t(T(0.0), T(0.0)) == P2(0, 0) - @test t(T(1.0), T(0.0)) == P2(1, 0) - @test t(T(0.0), T(1.0)) == P2(0, 1) - @test t(T(0.5), T(0.5)) == P2(0.5, 0.5) + t = Triangle(point(0.4, 0.4), point(0.6, 0.4), point(0.8, 0.4)) + @test point(0.2, 0.4) ∉ t + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test t(T(0.0), T(0.0)) == point(0, 0) + @test t(T(1.0), T(0.0)) == point(1, 0) + @test t(T(0.0), T(1.0)) == point(0, 1) + @test t(T(0.5), T(0.5)) == point(0.5, 0.5) @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) @test !hasholes(t) @test unique(t) == t @test boundary(t) == first(rings(t)) - @test rings(t) == [Ring(P2(0, 0), P2(1, 0), P2(0, 1))] + @test rings(t) == [Ring(point(0, 0), point(1, 0), point(0, 1))] @test convexhull(t) == t - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - @test perimeter(t) ≈ T(1 + 1 + √2) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" # https://github.com/JuliaGeometry/Meshes.jl/issues/333 t = Triangle((0.0f0, 0.0f0), (1.0f0, 0.0f0), (0.5f0, 1.0f0)) @@ -346,52 +349,52 @@ @test Point(0.5e0, 0.5e0) ∈ t # point at edge of triangle - @test P2(3, 1) ∈ Triangle(P2(1, 1), P2(5, 1), P2(3, 3)) + @test point(3, 1) ∈ Triangle(point(1, 1), point(5, 1), point(3, 3)) # test angles - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45], atol=8 * eps(T))) @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45], atol=8 * eps(T))) # Triangle in 3D space - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) - @test area(t) == T(0.5) - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 1)) - @test area(t) > T(0.7) - for p in P3[(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)] + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + @test area(t) == T(0.5) * u"m^2" + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + @test area(t) > T(0.7) * u"m^2" + for p in point.([(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)]) @test p ∈ t end - for p in P3[(-1, 0, 0), (1, 2, 0), (0, 1, 2)] + for p in point.([(-1, 0, 0), (1, 2, 0), (0, 1, 2)]) @test p ∉ t end - t = Triangle(P3(0, 0, 0), P3(0, 1, 0), P3(0, 0, 1)) - @test t(T(0.0), T(0.0)) == P3(0, 0, 0) - @test t(T(1.0), T(0.0)) == P3(0, 1, 0) - @test t(T(0.0), T(1.0)) == P3(0, 0, 1) - @test t(T(0.5), T(0.5)) == P3(0, 0.5, 0.5) + t = Triangle(point(0, 0, 0), point(0, 1, 0), point(0, 0, 1)) + @test t(T(0.0), T(0.0)) == point(0, 0, 0) + @test t(T(1.0), T(0.0)) == point(0, 1, 0) + @test t(T(0.0), T(1.0)) == point(0, 0, 1) + @test t(T(0.5), T(0.5)) == point(0, 0.5, 0.5) @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) - @test isapprox(normal(t), V3(0.5, 0, 0)) - t = Triangle(P3(0, 0, 0), P3(2, 0, 0), P3(0, 2, 2)) - @test isapprox(normal(t), V3(0, -2, 2)) + @test isapprox(normal(t), vector(0.5, 0, 0)) + t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) + @test isapprox(normal(t), vector(0, -2, 2)) - t = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - @test sprint(show, t) == "Triangle((0.0, 0.0), (1.0, 0.0), (0.0, 1.0))" + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ - Triangle{2,Float32} - ├─ Point(0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0) - └─ Point(0.0f0, 1.0f0)""" + Triangle + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), t) == """ - Triangle{2,Float64} - ├─ Point(0.0, 0.0) - ├─ Point(1.0, 0.0) - └─ Point(0.0, 1.0)""" + Triangle + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" end # ----------- @@ -399,66 +402,66 @@ # ----------- # test periodicity of Quadrangle - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) # Quadrangle in 2D space - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - @test vertex(q, 1) == P2(0, 0) - @test vertex(q, 2) == P2(1, 0) - @test vertex(q, 3) == P2(1, 1) - @test vertex(q, 4) == P2(0, 1) - @test area(q) == T(1) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1.5, 1.0), P2(0.5, 1.0)) - @test area(q) == T(1) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1.5, 1.0), P2(0.5, 1.0)) - for p in P2[(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)] + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test vertex(q, 1) == point(0, 0) + @test vertex(q, 2) == point(1, 0) + @test vertex(q, 3) == point(1, 1) + @test vertex(q, 4) == point(0, 1) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(point(0, 0), point(1, 0), point(1.5, 1.0), point(0.5, 1.0)) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(point(0, 0), point(1, 0), point(1.5, 1.0), point(0.5, 1.0)) + for p in point.([(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)]) @test p ∈ q end - for p in P2[(0, 1), (1.5, 0.0)] + for p in point.([(0, 1), (1.5, 0.0)]) @test p ∉ q end - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test !hasholes(q) @test unique(q) == q @test boundary(q) == first(rings(q)) - @test rings(q) == [Ring(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1))] - @test q(T(0), T(0)) == P2(0, 0) - @test q(T(1), T(0)) == P2(1, 0) - @test q(T(1), T(1)) == P2(1, 1) - @test q(T(0), T(1)) == P2(0, 1) + @test rings(q) == [Ring(point(0, 0), point(1, 0), point(1, 1), point(0, 1))] + @test q(T(0), T(0)) == point(0, 0) + @test q(T(1), T(0)) == point(1, 0) + @test q(T(1), T(1)) == point(1, 1) + @test q(T(0), T(1)) == point(0, 1) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - @test perimeter(q) ≈ T(4) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test perimeter(q) ≈ T(4) * u"m" # Quadrangle in 3D space - q = Quadrangle(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0)) - @test area(q) == T(1) - q = Quadrangle(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 1)) - @test area(q) > T(1) - @test q(T(0), T(0)) == P3(0, 0, 0) - @test q(T(1), T(0)) == P3(1, 0, 0) - @test q(T(1), T(1)) == P3(1, 1, 0) - @test q(T(0), T(1)) == P3(0, 1, 1) - - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - @test sprint(show, q) == "Quadrangle((0.0, 0.0), ..., (0.0, 1.0))" + q = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 1)) + @test area(q) > T(1) * u"m^2" + @test q(T(0), T(0)) == point(0, 0, 0) + @test q(T(1), T(0)) == point(1, 0, 0) + @test q(T(1), T(1)) == point(1, 1, 0) + @test q(T(0), T(1)) == point(0, 1, 1) + + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), q) == """ - Quadrangle{2,Float32} - ├─ Point(0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0) - ├─ Point(1.0f0, 1.0f0) - └─ Point(0.0f0, 1.0f0)""" + Quadrangle + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), q) == """ - Quadrangle{2,Float64} - ├─ Point(0.0, 0.0) - ├─ Point(1.0, 0.0) - ├─ Point(1.0, 1.0) - └─ Point(0.0, 1.0)""" + Quadrangle + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" end end @@ -466,32 +469,32 @@ @test paramdim(PolyArea) == 2 # equality and approximate equality - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test poly == poly @test poly ≈ poly # outer chain with 2 vertices is fixed by default - poly = PolyArea(P2[(0, 0), (1, 0)]) - @test rings(poly) == [Ring(P2[(0, 0), (0.5, 0.0), (1, 0)])] + poly = PolyArea(point.([(0, 0), (1, 0)])) + @test rings(poly) == [Ring(point.([(0, 0), (0.5, 0.0), (1, 0)]))] # inner chain with 2 vertices is removed by default - poly = PolyArea([P2[(0, 0), (1, 0), (1, 1), (0, 1)], P2[(1, 2), (2, 3)]]) - @test rings(poly) == [Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)])] + poly = PolyArea([point.([(0, 0), (1, 0), (1, 1), (0, 1)]), point.([(1, 2), (2, 3)])]) + @test rings(poly) == [Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)]))] # orientation of chains is fixed by default - poly = PolyArea(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) - @test vertices(poly) == CircularVector(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - poly = PolyArea(P2[(0, 0), (0, 1), (1, 1), (1, 0)], fix=false) - @test vertices(poly) == CircularVector(P2[(0, 0), (0, 1), (1, 1), (1, 0)]) + poly = PolyArea(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test vertices(poly) == CircularVector(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly = PolyArea(point.([(0, 0), (0, 1), (1, 1), (1, 0)]), fix=false) + @test vertices(poly) == CircularVector(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) # test accessor methods - poly = PolyArea(P2[(1, 2), (2, 3)], fix=false) - @test vertices(poly) == CircularVector(P2[(1, 2), (2, 3)]) - poly = PolyArea([P2[(1, 2), (2, 3)], P2[(1.1, 2.54), (1.4, 1.5)]], fix=false) - @test vertices(poly) == CircularVector(P2[(1, 2), (2, 3), (1.1, 2.54), (1.4, 1.5)]) + poly = PolyArea(point.([(1, 2), (2, 3)]), fix=false) + @test vertices(poly) == CircularVector(point.([(1, 2), (2, 3)])) + poly = PolyArea([point.([(1, 2), (2, 3)]), point.([(1.1, 2.54), (1.4, 1.5)])], fix=false) + @test vertices(poly) == CircularVector(point.([(1, 2), (2, 3), (1.1, 2.54), (1.4, 1.5)])) # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 @@ -553,40 +556,42 @@ @test orientation(b, TriangleOrientation()) == CCW # winding orientation is only suitable # for simple polygonal chains - if issimple(b) - @test orientation(b, WindingOrientation()) == CCW - end + # if issimple(b) + # @test orientation(b, WindingOrientation()) == CCW + # end end # test uniqueness - points = P2[(1, 1), (2, 2), (2, 2), (3, 3)] + points = point.([(1, 1), (2, 2), (2, 2), (3, 3)]) poly = PolyArea(points) unique!(poly) - @test first(rings(poly)) == Ring(P2[(1, 1), (2, 2), (3, 3)]) + @test first(rings(poly)) == Ring(point.([(1, 1), (2, 2), (3, 3)])) # approximately equal vertices poly = PolyArea( - P2[ - (-48.04448403189499, -18.326530800015174) - (-48.044478457836675, -18.326503670869467) - (-48.04447845783733, -18.326503670869915) - (-48.04447835073269, -18.326503149587666) - (-48.044468448930644, -18.326490894176693) - (-48.04447208741723, -18.326486301018672) - (-48.044459173572015, -18.32646700775326) - (-48.04445616736389, -18.326461847186216) - (-48.044459897846174, -18.326466190774774) - (-48.044462696066695, -18.32646303439271) - (-48.044473299571635, -18.326478565399572) - (-48.044473299571635, -18.326478565399565) - (-48.044484052460334, -18.326494315209573) - (-48.04449288424675, -18.326504598503668) - (-48.044492356262886, -18.32650647783081) - (-48.0444943180541, -18.326509351276243) - (-48.044492458690776, -18.32651322842786) - (-48.04450917793127, -18.326524641668517) - (-48.044501408820125, -18.326551273900744) - ] + point.( + [ + (-48.04448403189499, -18.326530800015174) + (-48.044478457836675, -18.326503670869467) + (-48.04447845783733, -18.326503670869915) + (-48.04447835073269, -18.326503149587666) + (-48.044468448930644, -18.326490894176693) + (-48.04447208741723, -18.326486301018672) + (-48.044459173572015, -18.32646700775326) + (-48.04445616736389, -18.326461847186216) + (-48.044459897846174, -18.326466190774774) + (-48.044462696066695, -18.32646303439271) + (-48.044473299571635, -18.326478565399572) + (-48.044473299571635, -18.326478565399565) + (-48.044484052460334, -18.326494315209573) + (-48.04449288424675, -18.326504598503668) + (-48.044492356262886, -18.32650647783081) + (-48.0444943180541, -18.326509351276243) + (-48.044492458690776, -18.32651322842786) + (-48.04450917793127, -18.326524641668517) + (-48.044501408820125, -18.326551273900744) + ] + ) ) upoly = unique(poly) @test nvertices(upoly) < nvertices(poly) @@ -597,199 +602,247 @@ end # invalid inner - outer = rand(P2, 10) - v1, v2 = rand(V2, 2) - inner = [Point(v1), Point(v1), Point(v2)] + outer = Ring(randpoint2(10)) + p1, p2 = randpoint2(2) + inner = Ring(p1, p1, p2) poly = PolyArea([outer, inner]) upoly = unique(poly) @test hasholes(poly) @test !hasholes(upoly) # centroid - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - @test centroid(poly) == P2(0.5, 0.5) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(poly) == point(0.5, 0.5) # single vertex access - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - @test vertex(poly, 1) == P2(0, 0) - @test vertex(poly, 4) == P2(0, 1) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test vertex(poly, 1) == point(0, 0) + @test vertex(poly, 4) == point(0, 1) # point in polygonal area - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test all(p ∈ poly for p in outer) - @test P2(0.5, 0.5) ∈ poly - @test P2(0.2, 0.6) ∈ poly - @test P2(1.5, 0.5) ∉ poly - @test P2(-0.5, 0.5) ∉ poly - @test P2(0.25, 0.25) ∉ poly - @test P2(0.75, 0.25) ∉ poly - @test P2(0.75, 0.75) ∈ poly + @test point(0.5, 0.5) ∈ poly + @test point(0.2, 0.6) ∈ poly + @test point(1.5, 0.5) ∉ poly + @test point(-0.5, 0.5) ∉ poly + @test point(0.25, 0.25) ∉ poly + @test point(0.75, 0.25) ∉ poly + @test point(0.75, 0.75) ∈ poly # area - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) - @test area(poly) ≈ T(0.92) + @test area(poly) ≈ T(0.92) * u"m^2" - p = rand(PolyArea{2,T}) + p = rand(PolyArea{2}) @test p isa PolyArea @test embeddim(p) == 2 - @test coordtype(p) === T - p = rand(PolyArea{3,T}) + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(PolyArea{3}) @test p isa PolyArea @test embeddim(p) == 3 - @test coordtype(p) === T + @test Meshes.lentype(p) === Meshes.Met{Float64} - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) - @test sprint(show, poly1) == "PolyArea((0.0, 0.0), ..., (0.0, 1.0))" + @test sprint(show, poly1) == "PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" @test sprint(show, poly2) == "PolyArea(4-Ring, 4-Ring, 4-Ring)" @test sprint(show, MIME("text/plain"), poly1) == """ - PolyArea{2,$T} + PolyArea outer - └─ Ring((0.0, 0.0), ..., (0.0, 1.0))""" + └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))""" @test sprint(show, MIME("text/plain"), poly2) == """ - PolyArea{2,$T} + PolyArea outer - └─ Ring((0.0, 0.0), ..., (0.0, 1.0)) + └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) inner - ├─ Ring((0.2, 0.2), ..., (0.4, 0.2)) - └─ Ring((0.6, 0.2), ..., (0.8, 0.2))""" + ├─ Ring((x: 0.2 m, y: 0.2 m), ..., (x: 0.4 m, y: 0.2 m)) + └─ Ring((x: 0.6 m, y: 0.2 m), ..., (x: 0.8 m, y: 0.2 m))""" # should not repeat the first vertex manually - @test_throws ArgumentError PolyArea(P2[(0, 0), (0, 0)]) - @test_throws ArgumentError PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 0)]) + @test_throws ArgumentError PolyArea(point.([(0, 0), (0, 0)])) + @test_throws ArgumentError PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 0)])) end @testset "Polyhedra" begin @test paramdim(Tetrahedron) == 3 @test nvertices(Tetrahedron) == 4 - t = Tetrahedron(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0), P3(0, 0, 1)) - @test vertex(t, 1) == P3(0, 0, 0) - @test vertex(t, 2) == P3(1, 0, 0) - @test vertex(t, 3) == P3(0, 1, 0) - @test vertex(t, 4) == P3(0, 0, 1) - @test measure(t) == T(1 / 6) + t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + @test vertex(t, 1) == point(0, 0, 0) + @test vertex(t, 2) == point(1, 0, 0) + @test vertex(t, 3) == point(0, 1, 0) + @test vertex(t, 4) == point(0, 0, 1) + @test measure(t) == T(1 / 6) * u"m^3" m = boundary(t) n = normal.(m) @test m isa Mesh @test nvertices(m) == 4 @test nelements(m) == 4 - @test n[1] == T[0, 0, -0.5] - @test n[2] == T[0, -0.5, 0] - @test n[3] == T[-0.5, 0, 0] - @test all(>(0), n[4]) - @test t(T(0), T(0), T(0)) ≈ P3(0, 0, 0) - @test t(T(1), T(0), T(0)) ≈ P3(1, 0, 0) - @test t(T(0), T(1), T(0)) ≈ P3(0, 1, 0) - @test t(T(0), T(0), T(1)) ≈ P3(0, 0, 1) + @test n[1] == vector(0, 0, -0.5) + @test n[2] == vector(0, -0.5, 0) + @test n[3] == vector(-0.5, 0, 0) + @test all(>(T(0) * u"m"), n[4]) + @test t(T(0), T(0), T(0)) ≈ point(0, 0, 0) + @test t(T(1), T(0), T(0)) ≈ point(1, 0, 0) + @test t(T(0), T(1), T(0)) ≈ point(0, 1, 0) + @test t(T(0), T(0), T(1)) ≈ point(0, 0, 1) @test_throws DomainError((T(1), T(1), T(1)), "invalid barycentric coordinates for tetrahedron.") t(T(1), T(1), T(1)) - t = rand(Tetrahedron{3,T}) + t = rand(Tetrahedron{3}) @test t isa Tetrahedron @test embeddim(t) == 3 - @test coordtype(t) === T + @test Meshes.lentype(t) === Meshes.Met{Float64} - t = Tetrahedron(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0), P3(0, 0, 1)) - @test sprint(show, t) == "Tetrahedron((0.0, 0.0, 0.0), ..., (0.0, 0.0, 1.0))" + t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ - Tetrahedron{3,Float32} - ├─ Point(0.0f0, 0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0, 0.0f0) - ├─ Point(0.0f0, 1.0f0, 0.0f0) - └─ Point(0.0f0, 0.0f0, 1.0f0)""" + Tetrahedron + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), t) == """ - Tetrahedron{3,Float64} - ├─ Point(0.0, 0.0, 0.0) - ├─ Point(1.0, 0.0, 0.0) - ├─ Point(0.0, 1.0, 0.0) - └─ Point(0.0, 0.0, 1.0)""" + Tetrahedron + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end @test paramdim(Hexahedron) == 3 @test nvertices(Hexahedron) == 8 - h = - Hexahedron(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1), P3(1, 0, 1), P3(1, 1, 1), P3(0, 1, 1)) - @test vertex(h, 1) == P3(0, 0, 0) - @test vertex(h, 8) == P3(0, 1, 1) - @test h(T(0), T(0), T(0)) == P3(0, 0, 0) - @test h(T(0), T(0), T(1)) == P3(0, 0, 1) - @test h(T(0), T(1), T(0)) == P3(0, 1, 0) - @test h(T(0), T(1), T(1)) == P3(0, 1, 1) - @test h(T(1), T(0), T(0)) == P3(1, 0, 0) - @test h(T(1), T(0), T(1)) == P3(1, 0, 1) - @test h(T(1), T(1), T(0)) == P3(1, 1, 0) - @test h(T(1), T(1), T(1)) == P3(1, 1, 1) - - h = - Hexahedron(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1), P3(1, 0, 1), P3(1, 1, 1), P3(0, 1, 1)) - @test volume(h) ≈ T(1 * 1 * 1) - h = - Hexahedron(P3(0, 0, 0), P3(2, 0, 0), P3(2, 2, 0), P3(0, 2, 0), P3(0, 0, 2), P3(2, 0, 2), P3(2, 2, 2), P3(0, 2, 2)) - @test volume(h) ≈ T(2 * 2 * 2) + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) + @test vertex(h, 1) == point(0, 0, 0) + @test vertex(h, 8) == point(0, 1, 1) + @test h(T(0), T(0), T(0)) == point(0, 0, 0) + @test h(T(0), T(0), T(1)) == point(0, 0, 1) + @test h(T(0), T(1), T(0)) == point(0, 1, 0) + @test h(T(0), T(1), T(1)) == point(0, 1, 1) + @test h(T(1), T(0), T(0)) == point(1, 0, 0) + @test h(T(1), T(0), T(1)) == point(1, 0, 1) + @test h(T(1), T(1), T(0)) == point(1, 1, 0) + @test h(T(1), T(1), T(1)) == point(1, 1, 1) + + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) + @test volume(h) ≈ T(1 * 1 * 1) * u"m^3" + h = Hexahedron( + point(0, 0, 0), + point(2, 0, 0), + point(2, 2, 0), + point(0, 2, 0), + point(0, 0, 2), + point(2, 0, 2), + point(2, 2, 2), + point(0, 2, 2) + ) + @test volume(h) ≈ T(2 * 2 * 2) * u"m^3" # volume formula of a frustum of a prism is V = 1/3*H*(S₁+S₂+sqrt(S₁*S₂)) # here we build a hexahedron which is a frustum of a prism with # bottom area S₁= 4, top area S₂= 1, height H = 2 - h = - Hexahedron(P3(0, 0, 0), P3(2, 0, 0), P3(2, 2, 0), P3(0, 2, 0), P3(0, 0, 2), P3(1, 0, 2), P3(1, 1, 2), P3(0, 1, 2)) - @test volume(h) ≈ T(1 / 3 * 2 * (1 + 4 + sqrt(1 * 4))) - - h = - Hexahedron(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1), P3(1, 0, 1), P3(1, 1, 1), P3(0, 1, 1)) + h = Hexahedron( + point(0, 0, 0), + point(2, 0, 0), + point(2, 2, 0), + point(0, 2, 0), + point(0, 0, 2), + point(1, 0, 2), + point(1, 1, 2), + point(0, 1, 2) + ) + @test volume(h) ≈ T(1 / 3 * 2 * (1 + 4 + sqrt(1 * 4))) * u"m^3" + + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) m = boundary(h) @test m isa Mesh @test nvertices(m) == 8 @test nelements(m) == 6 - h = rand(Hexahedron{3,T}) + h = rand(Hexahedron{3}) @test h isa Hexahedron @test embeddim(h) == 3 - @test coordtype(h) === T - - h = - Hexahedron(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1), P3(1, 0, 1), P3(1, 1, 1), P3(0, 1, 1)) - @test sprint(show, h) == "Hexahedron((0.0, 0.0, 0.0), ..., (0.0, 1.0, 1.0))" + @test Meshes.lentype(h) === Meshes.Met{Float64} + + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) + @test sprint(show, h) == "Hexahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), h) == """ - Hexahedron{3,Float32} - ├─ Point(0.0f0, 0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0, 0.0f0) - ├─ Point(1.0f0, 1.0f0, 0.0f0) - ├─ Point(0.0f0, 1.0f0, 0.0f0) - ├─ Point(0.0f0, 0.0f0, 1.0f0) - ├─ Point(1.0f0, 0.0f0, 1.0f0) - ├─ Point(1.0f0, 1.0f0, 1.0f0) - └─ Point(0.0f0, 1.0f0, 1.0f0)""" + Hexahedron + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), h) == """ - Hexahedron{3,Float64} - ├─ Point(0.0, 0.0, 0.0) - ├─ Point(1.0, 0.0, 0.0) - ├─ Point(1.0, 1.0, 0.0) - ├─ Point(0.0, 1.0, 0.0) - ├─ Point(0.0, 0.0, 1.0) - ├─ Point(1.0, 0.0, 1.0) - ├─ Point(1.0, 1.0, 1.0) - └─ Point(0.0, 1.0, 1.0)""" + Hexahedron + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" end @test paramdim(Pyramid) == 3 @test nvertices(Pyramid) == 5 - p = Pyramid(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1)) - @test volume(p) ≈ T(1 / 3) + p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + @test volume(p) ≈ T(1 / 3) * u"m^3" m = boundary(p) @test m isa Mesh @test nelements(m) == 5 @@ -799,29 +852,29 @@ @test m[4] isa Triangle @test m[5] isa Triangle - p = rand(Pyramid{3,T}) + p = rand(Pyramid{3}) @test p isa Pyramid @test embeddim(p) == 3 - @test coordtype(p) === T + @test Meshes.lentype(p) === Meshes.Met{Float64} - p = Pyramid(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1)) - @test sprint(show, p) == "Pyramid((0.0, 0.0, 0.0), ..., (0.0, 0.0, 1.0))" + p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), p) == """ - Pyramid{3,Float32} - ├─ Point(0.0f0, 0.0f0, 0.0f0) - ├─ Point(1.0f0, 0.0f0, 0.0f0) - ├─ Point(1.0f0, 1.0f0, 0.0f0) - ├─ Point(0.0f0, 1.0f0, 0.0f0) - └─ Point(0.0f0, 0.0f0, 1.0f0)""" + Pyramid + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), p) == """ - Pyramid{3,Float64} - ├─ Point(0.0, 0.0, 0.0) - ├─ Point(1.0, 0.0, 0.0) - ├─ Point(1.0, 1.0, 0.0) - ├─ Point(0.0, 1.0, 0.0) - └─ Point(0.0, 0.0, 1.0)""" + Pyramid + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end end end diff --git a/test/predicates.jl b/test/predicates.jl index f60dc0f0b..250d7f383 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -1,97 +1,98 @@ @testset "Predicates" begin @testset "issimplex" begin @test issimplex(Segment) - @test issimplex(Segment(P2(0, 0), P2(1, 0))) + @test issimplex(Segment(point(0, 0), point(1, 0))) @test issimplex(Triangle) - @test issimplex(Triangle(P2(0, 0), P2(1, 0), P2(0, 1))) + @test issimplex(Triangle(point(0, 0), point(1, 0), point(0, 1))) @test issimplex(Tetrahedron) - @test issimplex(Tetrahedron(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0), P3(0, 0, 1))) + @test issimplex(Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1))) end @testset "isconvex" begin # primitives - r = Ray(P2(0, 0), V2(1, 1)) + r = Ray(point(0, 0), vector(1, 1)) @test isconvex(r) - l = Line(P2(0, 0), P2(1, 1)) + l = Line(point(0, 0), point(1, 1)) @test isconvex(l) - p = Plane(P3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0)) + p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) @test isconvex(p) - b = Box(P1(0), P1(1)) + b = Box(point(0), point(1)) @test isconvex(b) - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) @test isconvex(b) - b = Box(P3(0, 0, 0), P3(1, 1, 1)) - b = Ball(P3(1, 2, 3), T(5)) + b = Box(point(0, 0, 0), point(1, 1, 1)) + b = Ball(point(1, 2, 3), T(5)) @test isconvex(b) @test isconvex(b) - s = Sphere(P2(0, 0), T(1)) + s = Sphere(point(0, 0), T(1)) @test !isconvex(s) - s = Sphere(P3(0, 0, 0), T(1)) + s = Sphere(point(0, 0, 0), T(1)) @test !isconvex(s) - d = Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(2)) + d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) @test isconvex(d) - c = Circle(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(2)) + c = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) @test !isconvex(c) - b = BezierCurve(P2[(0, 0), (1, 0), (2, 0)]) + b = BezierCurve(point.([(0, 0), (1, 0), (2, 0)])) @test isconvex(b) - b = BezierCurve(P2[(0, 0), (1, 1), (2, 2)]) + b = BezierCurve(point.([(0, 0), (1, 1), (2, 2)])) @test isconvex(b) - b = BezierCurve(P2[(0, 0)]) + b = BezierCurve(point.([(0, 0)])) @test isconvex(b) - b = BezierCurve(P2[(0, 0), (1, 0)]) + b = BezierCurve(point.([(0, 0), (1, 0)])) @test isconvex(b) - b = BezierCurve(P2[(0, 0), (5, 3), (-10, 3), (17, 20)]) + b = BezierCurve(point.([(0, 0), (5, 3), (-10, 3), (17, 20)])) @test !isconvex(b) - b = BezierCurve(P2[(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)]) + b = BezierCurve(point.([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)])) @test isconvex(b) - b = BezierCurve(P2[]) + P = typeof(point(0, 0)) + b = BezierCurve(P[]) @test isconvex(b) - c = Cylinder(Plane(P3(1, 2, 3), V3(0, 0, 1)), Plane(P3(4, 5, 6), V3(0, 0, 1)), T(5)) + c = Cylinder(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) @test isconvex(c) c = CylinderSurface(T(2)) @test !isconvex(c) - d = Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(2)) - a = P3(0, 0, 1) + d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + a = point(0, 0, 1) c = Cone(d, a) @test isconvex(c) - d = Disk(Plane(P3(0, 0, 0), V3(0, 0, 1)), T(2)) - a = P3(0, 0, 1) + d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + a = point(0, 0, 1) c = ConeSurface(d, a) @test !isconvex(c) t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) @test !isconvex(t) # polytopes - s = Segment(P2(0, 0), P2(1, 1)) + s = Segment(point(0, 0), point(1, 1)) @test isconvex(s) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test isconvex(t) - q1 = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - q2 = Quadrangle(P2(0.8, 0.8), P2(1, 0), P2(1, 1), P2(0, 1)) - q3 = Quadrangle(P2(0, 0), P2(0.2, 0.8), P2(1, 1), P2(0, 1)) - q4 = Quadrangle(P2(0, 0), P2(1, 0), P2(0.2, 0.2), P2(0, 1)) - q5 = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0.8, 0.2)) + q1 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q2 = Quadrangle(point(0.8, 0.8), point(1, 0), point(1, 1), point(0, 1)) + q3 = Quadrangle(point(0, 0), point(0.2, 0.8), point(1, 1), point(0, 1)) + q4 = Quadrangle(point(0, 0), point(1, 0), point(0.2, 0.2), point(0, 1)) + q5 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0.8, 0.2)) @test isconvex(q1) @test !isconvex(q2) @test !isconvex(q3) @test !isconvex(q4) @test !isconvex(q5) - q1 = Quadrangle(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0)) - q2 = Quadrangle(P3(0.8, 0.8, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0)) - q3 = Quadrangle(P3(0, 0, 0), P3(0.2, 0.8, 0), P3(1, 1, 0), P3(0, 1, 0)) - q4 = Quadrangle(P3(0, 0, 0), P3(1, 0, 0), P3(0.2, 0.2, 0), P3(0, 1, 0)) - q5 = Quadrangle(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0.8, 0.2, 0)) + q1 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) + q2 = Quadrangle(point(0.8, 0.8, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) + q3 = Quadrangle(point(0, 0, 0), point(0.2, 0.8, 0), point(1, 1, 0), point(0, 1, 0)) + q4 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(0.2, 0.2, 0), point(0, 1, 0)) + q5 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0.8, 0.2, 0)) @test isconvex(q1) @test !isconvex(q2) @test !isconvex(q3) @test !isconvex(q4) @test !isconvex(q5) - t = Tetrahedron(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0), P3(0, 0, 1)) + t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) @test isconvex(t) - outer = P2[(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)] - inner = P2[(5, 7), (10, 12), (15, 7)] + outer = point.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) + inner = point.([(5, 7), (10, 12), (15, 7)]) pent = Pentagon(outer...) tri = Triangle(inner...) poly = PolyArea([outer, inner]) @@ -100,14 +101,14 @@ @test isconvex(tri) @test !isconvex(poly) @test isconvex(multi) - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) @test isconvex(poly1) @test !isconvex(poly2) - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)]) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) @test !isconvex(poly) end @@ -154,56 +155,56 @@ @test isperiodic(Quadrangle) == (false, false) @test isperiodic(Hexahedron) == (false, false, false) - @test isperiodic(CartesianGrid{T}(10, 10)) == (false, false) - @test isperiodic(CartesianGrid{T}(10, 10, 10)) == (false, false, false) + @test isperiodic(cartgrid(10, 10)) == (false, false) + @test isperiodic(cartgrid(10, 10, 10)) == (false, false, false) end @testset "in" begin - h = first(CartesianGrid{T}(10, 10, 10)) - @test P3(0, 0, 0) ∈ h - @test P3(0.5, 0.5, 0.5) ∈ h - @test P3(-1, 0, 0) ∉ h - @test P3(0, 2, 0) ∉ h + h = first(cartgrid(10, 10, 10)) + @test point(0, 0, 0) ∈ h + @test point(0.5, 0.5, 0.5) ∈ h + @test point(-1, 0, 0) ∉ h + @test point(0, 2, 0) ∉ h end @testset "issubset" begin - point = P2(0.5, 0.5) - box = Box(P2(0, 0), P2(1, 1)) - ball = Ball(P2(0, 0)) - tri = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - quad = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - @test point ⊆ box - @test point ⊆ ball - @test point ⊆ tri - @test point ⊆ quad - @test point ⊆ point + p = point(0.5, 0.5) + box = Box(point(0, 0), point(1, 1)) + ball = Ball(point(0, 0)) + tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) + quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test p ⊆ box + @test p ⊆ ball + @test p ⊆ tri + @test p ⊆ quad + @test p ⊆ p @test quad ⊆ quad - s1 = Segment(P2(0, 0), P2(1, 1)) - s2 = Segment(P2(0.5, 0.5), P2(1, 1)) - s3 = Segment(P2(0, 0), P2(0.5, 0.5)) + s1 = Segment(point(0, 0), point(1, 1)) + s2 = Segment(point(0.5, 0.5), point(1, 1)) + s3 = Segment(point(0, 0), point(0.5, 0.5)) @test s2 ⊆ s1 @test s3 ⊆ s1 @test s1 ⊆ s1 - seg = Segment(P2(0, 0), P2(1, 1)) - box = Box(P2(0, 0), P2(1, 1)) - ball = Ball(P2(0, 0)) + seg = Segment(point(0, 0), point(1, 1)) + box = Box(point(0, 0), point(1, 1)) + ball = Ball(point(0, 0)) @test seg ⊆ box @test !(seg ⊆ ball) - t1 = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - t2 = Triangle(P2(0, 0), P2(1, 0), P2(0.8, 0.8)) - t3 = Triangle(P2(0, 0), P2(1, 0), P2(1.1, 1.1)) + t1 = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t2 = Triangle(point(0, 0), point(1, 0), point(0.8, 0.8)) + t3 = Triangle(point(0, 0), point(1, 0), point(1.1, 1.1)) @test t2 ⊆ t1 @test !(t3 ⊆ t1) - tri = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) - box = Box(P2(0, 0), P2(1, 1)) - ball = Ball(P2(0, 0)) - quad = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - pent = Pentagon(P2(0, 0), P2(1, 0), P2(1, 1), P2(0.5, 1.5), P2(0, 1)) - poly = PolyArea(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) + box = Box(point(0, 0), point(1, 1)) + ball = Ball(point(0, 0)) + quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + pent = Pentagon(point(0, 0), point(1, 0), point(1, 1), point(0.5, 1.5), point(0, 1)) + poly = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test tri ⊆ quad @test !(quad ⊆ tri) @test tri ⊆ box @@ -219,18 +220,18 @@ @test quad ⊆ poly @test poly ⊆ quad - quad1 = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) - quad2 = Quadrangle(P2(0, 0), P2(1.1, 0), P2(1, 1), P2(0, 1)) - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + quad1 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + quad2 = Quadrangle(point(0, 0), point(1.1, 0), point(1, 1), point(0, 1)) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) multi = Multi([poly]) @test quad1 ⊆ poly @test !(quad2 ⊆ poly) @test quad1 ⊆ multi @test !(quad2 ⊆ multi) - p1 = P2(-1.0, 0.0) - p2 = P2(0.0, 0.0) - p3 = P2(1.0, 0.0) + p1 = point(-1.0, 0.0) + p2 = point(0.0, 0.0) + p3 = point(1.0, 0.0) l1 = Line(p1, p3) l2 = Line(p2, p3) @test l1 ⊆ l2 @@ -238,8 +239,8 @@ @test l1 ⊆ l1 @test l2 ⊆ l2 - pts1 = P2[(5, 7), (10, 12), (15, 7)] - pts2 = P2[(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)] + pts1 = point.([(5, 7), (10, 12), (15, 7)]) + pts2 = point.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) pent = Pentagon(pts2...) tri = Triangle(pts1...) poly1 = PolyArea(pts2) @@ -253,11 +254,11 @@ @test pent ⊈ poly2 @test pent ⊆ multi - poly1 = PolyArea(P2[(4, 12), (11, 11), (16, 8), (16, 1), (13, -2), (2, -2), (-3, 4), (-2, 8)]) - poly2 = PolyArea(P2[(3, 0), (1, 2), (3, 4), (1, 6), (4, 7), (10, 7), (11, 4), (9, 0)]) - poly3 = PolyArea(P2[(3, 2), (4, 4), (3, 8), (12, 8), (14, 4), (12, 1)]) - poly4 = PolyArea(P2[(8, 2), (5, 4), (5, 6), (9, 6), (10, 4)]) - poly5 = PolyArea(P2[(3, 9), (6, 11), (10, 10), (10, 9)]) + poly1 = PolyArea(point.([(4, 12), (11, 11), (16, 8), (16, 1), (13, -2), (2, -2), (-3, 4), (-2, 8)])) + poly2 = PolyArea(point.([(3, 0), (1, 2), (3, 4), (1, 6), (4, 7), (10, 7), (11, 4), (9, 0)])) + poly3 = PolyArea(point.([(3, 2), (4, 4), (3, 8), (12, 8), (14, 4), (12, 1)])) + poly4 = PolyArea(point.([(8, 2), (5, 4), (5, 6), (9, 6), (10, 4)])) + poly5 = PolyArea(point.([(3, 9), (6, 11), (10, 10), (10, 9)])) @test poly2 ⊆ poly1 @test poly3 ⊆ poly1 @test poly4 ⊆ poly1 @@ -269,22 +270,22 @@ end @testset "intersects" begin - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - q = Quadrangle(P2(1, 1), P2(2, 1), P2(2, 2), P2(1, 2)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + q = Quadrangle(point(1, 1), point(2, 1), point(2, 2), point(1, 2)) @test intersects(t, t) @test intersects(q, q) @test !intersects(t, q) @test !intersects(q, t) - t = Triangle(P2(1, 0), P2(2, 0), P2(1, 1)) - q = Quadrangle(P2(1.3, 0.5), P2(2.3, 0.5), P2(2.3, 1.5), P2(1.3, 1.5)) + t = Triangle(point(1, 0), point(2, 0), point(1, 1)) + q = Quadrangle(point(1.3, 0.5), point(2.3, 0.5), point(2.3, 1.5), point(1.3, 1.5)) @test intersects(t, t) @test intersects(q, q) @test intersects(t, q) @test intersects(q, t) - t = Triangle(P2(1, 0), P2(2, 0), P2(1, 1)) - q = Quadrangle(P2(1.3, 0.5), P2(2.3, 0.5), P2(2.3, 1.5), P2(1.3, 1.5)) + t = Triangle(point(1, 0), point(2, 0), point(1, 1)) + q = Quadrangle(point(1.3, 0.5), point(2.3, 0.5), point(2.3, 1.5), point(1.3, 1.5)) m = Multi([t, q]) @test intersects(m, t) @test intersects(t, m) @@ -292,52 +293,52 @@ @test intersects(q, m) @test intersects(m, m) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - b = Ball(P2(0, 0), T(1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + b = Ball(point(0, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test intersects(t, b) @test intersects(b, t) - t = Triangle(P2(1, 0), P2(2, 0), P2(1, 1)) - b = Ball(P2(0, 0), T(1)) + t = Triangle(point(1, 0), point(2, 0), point(1, 1)) + b = Ball(point(0, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test intersects(t, b) @test intersects(b, t) - t = Triangle(P2(1, 0), P2(2, 0), P2(1, 1)) - b = Ball(P2(-0.01, 0), T(1)) + t = Triangle(point(1, 0), point(2, 0), point(1, 1)) + b = Ball(point(-0.01, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test !intersects(t, b) @test !intersects(b, t) # https://github.com/JuliaGeometry/Meshes.jl/issues/250 - t1 = Triangle(P3(0, 0, 0), P3(2, 0, 0), P3(1, 2, 0)) - t2 = Triangle(P3(1, 0, 0), P3(3, 0, 0), P3(2, 2, 0)) - t3 = Triangle(P3(3, 0, 0), P3(5, 0, 0), P3(4, 2, 0)) + t1 = Triangle(point(0, 0, 0), point(2, 0, 0), point(1, 2, 0)) + t2 = Triangle(point(1, 0, 0), point(3, 0, 0), point(2, 2, 0)) + t3 = Triangle(point(3, 0, 0), point(5, 0, 0), point(4, 2, 0)) @test intersects(t1, t2) @test intersects(t2, t3) @test !intersects(t1, t3) # https://github.com/JuliaGeometry/Meshes.jl/issues/639 - r = Ray(P2(0.41169768366272996, 0.8990554132423699), V2(0.47249211625247445, 0.2523149692768657)) - b = Box(P2(1.0, 1.0), P2(5.0, 2.0)) + r = Ray(point(0.41169768366272996, 0.8990554132423699), vector(0.47249211625247445, 0.2523149692768657)) + b = Box(point(1.0, 1.0), point(5.0, 2.0)) @test intersects(r, b) @test intersects(b, r) - t = Triangle(P3(0, 0, 0), P3(2, 0, 0), P3(1, 2, 0)) - r1 = Ray(P3(1, 1, 1), V3(0, 0, -1)) - r2 = Ray(P3(1, 1, 1), V3(0, 0, 1)) + t = Triangle(point(0, 0, 0), point(2, 0, 0), point(1, 2, 0)) + r1 = Ray(point(1, 1, 1), vector(0, 0, -1)) + r2 = Ray(point(1, 1, 1), vector(0, 0, 1)) @test intersects(r1, t) @test intersects(t, r1) @test !intersects(r2, t) @test !intersects(t, r2) - r = Ray(P2(0, 0), V2(1, 0)) - s1 = Sphere(P2(3, 0), T(1)) - s2 = Sphere(P2(0, 3), T(1)) + r = Ray(point(0, 0), vector(1, 0)) + s1 = Sphere(point(3, 0), T(1)) + s2 = Sphere(point(0, 3), T(1)) @test intersects(r, s1) @test !intersects(r, s2) @@ -361,41 +362,41 @@ @test !intersects(t(r), t(s2)) end - r = Ray(P2(0, 0), V2(1, 0)) - s = Sphere(P2(floatmax(Float32) / 2, 0), 1) + r = Ray(point(0, 0), vector(1, 0)) + s = Sphere(point(floatmax(Float32) / 2, 0), 1) @test intersects(r, s) - r = Ray(P3(0, 0, 0), V3(1, 0, 0)) - s1 = Sphere(P3(5, 0, 1 - eps(T(1))), T(1)) - s2 = Sphere(P3(5, 0, 1 + eps(T(1))), T(1)) + r = Ray(point(0, 0, 0), vector(1, 0, 0)) + s1 = Sphere(point(5, 0, 1 - eps(T(1))), T(1)) + s2 = Sphere(point(5, 0, 1 + eps(T(1))), T(1)) @test intersects(r, s1) @test !intersects(r, s2) # https://github.com/JuliaGeometry/Meshes.jl/issues/635 - q1 = Quadrangle(P3(4.0, 4.0, 0.0), P3(3.0, 3.0, 2.0), P3(3.0, 1.0, 2.0), P3(4.0, 0.0, 0.0)) - q2 = Quadrangle(P3(3.6, 3.0, 1.0), P3(5.6, 3.0, 1.0), P3(5.6, 1.0, 1.0), P3(3.6, 1.0, 1.0)) - q3 = Quadrangle(P3(3.6, 1.0, 1.0), P3(5.6, 1.0, 1.0), P3(5.6, -1.0, 1.0), P3(3.6, -1.0, 1.0)) - q4 = Quadrangle(P3(2.1, 1.0, 1.0), P3(4.1, 1.0, 1.0), P3(4.1, -1.0, 1.0), P3(2.1, -1.0, 1.0)) + q1 = Quadrangle(point(4.0, 4.0, 0.0), point(3.0, 3.0, 2.0), point(3.0, 1.0, 2.0), point(4.0, 0.0, 0.0)) + q2 = Quadrangle(point(3.6, 3.0, 1.0), point(5.6, 3.0, 1.0), point(5.6, 1.0, 1.0), point(3.6, 1.0, 1.0)) + q3 = Quadrangle(point(3.6, 1.0, 1.0), point(5.6, 1.0, 1.0), point(5.6, -1.0, 1.0), point(3.6, -1.0, 1.0)) + q4 = Quadrangle(point(2.1, 1.0, 1.0), point(4.1, 1.0, 1.0), point(4.1, -1.0, 1.0), point(2.1, -1.0, 1.0)) @test !intersects(q1, q2) @test !intersects(q1, q3) @test intersects(q1, q1) @test intersects(q1, q4) - h1 = Tetrahedron(P3(1, 1, 0), P3(4, 4, 0), P3(2.5, 2.5, 1.5), P3(1, 3, 2)) - h2 = Tetrahedron(P3(-1.0, 2.0, 1.0), P3(2.0, 1.0, 1.0), P3(-1.0, 4.0, 0.0), P3(0.5, 2.5, 1.5)) - h3 = Tetrahedron(P3(-1.3, 2.0, 1.0), P3(1.7, 1.0, 1.0), P3(-1.3, 4.0, 0.0), P3(0.2, 2.5, 1.5)) + h1 = Tetrahedron(point(1, 1, 0), point(4, 4, 0), point(2.5, 2.5, 1.5), point(1, 3, 2)) + h2 = Tetrahedron(point(-1.0, 2.0, 1.0), point(2.0, 1.0, 1.0), point(-1.0, 4.0, 0.0), point(0.5, 2.5, 1.5)) + h3 = Tetrahedron(point(-1.3, 2.0, 1.0), point(1.7, 1.0, 1.0), point(-1.3, 4.0, 0.0), point(0.2, 2.5, 1.5)) @test intersects(h1, h2) @test !intersects(h1, h3) - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) - ball1 = Ball(P2(0.5, 0.5), T(0.05)) - ball2 = Ball(P2(0.3, 0.3), T(0.05)) - ball3 = Ball(P2(0.7, 0.3), T(0.05)) - ball4 = Ball(P2(0.3, 0.3), T(0.15)) + ball1 = Ball(point(0.5, 0.5), T(0.05)) + ball2 = Ball(point(0.3, 0.3), T(0.05)) + ball3 = Ball(point(0.7, 0.3), T(0.05)) + ball4 = Ball(point(0.3, 0.3), T(0.15)) @test intersects(poly1, poly1) @test intersects(poly2, poly2) @test intersects(poly1, poly2) @@ -415,21 +416,21 @@ @test intersects(mesh1, mesh2) @test intersects(mesh2, mesh1) - point = P2(0.5, 0.5) - ball = Ball(P2(0, 0), T(1)) - @test intersects(point, ball) - @test intersects(ball, point) - @test intersects(point, point) - @test !intersects(point, point + V2(1, 1)) + p = point(0.5, 0.5) + ball = Ball(point(0, 0), T(1)) + @test intersects(p, ball) + @test intersects(ball, p) + @test intersects(p, p) + @test !intersects(p, p + vector(1, 1)) - poly = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - box = Box(P2(0, 0), P2(2, 2)) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + box = Box(point(0, 0), point(2, 2)) @test intersects(poly, box) - b1 = Box(P2(0, 0), P2(2, 2)) - b2 = Box(P2(2, 0), P2(4, 2)) - p1 = P2(1, 1) - p2 = P2(3, 1) + b1 = Box(point(0, 0), point(2, 2)) + b2 = Box(point(2, 0), point(4, 2)) + p1 = point(1, 1) + p2 = point(3, 1) m = Multi([b1, b2]) @test intersects(p1, b1) @test !intersects(p2, b1) @@ -440,18 +441,18 @@ @test intersects(m, p2) @test intersects(p2, m) - s1 = Segment(P2(0, 0), P2(4, 4)) - s2 = Segment(P2(4, 0), P2(0, 4)) - s3 = Segment(P2(2, 0), P2(4, 2)) + s1 = Segment(point(0, 0), point(4, 4)) + s2 = Segment(point(4, 0), point(0, 4)) + s3 = Segment(point(2, 0), point(4, 2)) @test intersects(s1, s2) @test intersects(s2, s3) @test !intersects(s1, s3) - s1 = Segment(P2(4, 0), P2(0, 4)) - s2 = Segment(P2(4, 0), P2(8, 4)) - s3 = Segment(P2(0, 8), P2(8, 8)) - r1 = Rope(P2[(0, 0), (4, 4), (8, 0)]) - r2 = Ring(P2[(0, 2), (4, 6), (8, 2)]) + s1 = Segment(point(4, 0), point(0, 4)) + s2 = Segment(point(4, 0), point(8, 4)) + s3 = Segment(point(0, 8), point(8, 8)) + r1 = Rope(point.([(0, 0), (4, 4), (8, 0)])) + r2 = Ring(point.([(0, 2), (4, 6), (8, 2)])) @test intersects(s1, r1) @test intersects(s2, r1) @test !intersects(s3, r1) @@ -460,30 +461,30 @@ @test !intersects(s3, r2) @test intersects(r1, r2) - r1 = Rope(P2[(0, 0), (2, 2), (4, 0)]) - r2 = Rope(P2[(3, 0), (5, 2), (7, 0)]) - r3 = Rope(P2[(6, 0), (8, 2), (10, 0)]) + r1 = Rope(point.([(0, 0), (2, 2), (4, 0)])) + r2 = Rope(point.([(3, 0), (5, 2), (7, 0)])) + r3 = Rope(point.([(6, 0), (8, 2), (10, 0)])) @test intersects(r1, r2) @test intersects(r2, r3) @test !intersects(r1, r3) - r1 = Ring(P2[(0, 0), (2, 2), (4, 0)]) - r2 = Ring(P2[(3, 0), (5, 2), (7, 0)]) - r3 = Ring(P2[(6, 0), (8, 2), (10, 0)]) + r1 = Ring(point.([(0, 0), (2, 2), (4, 0)])) + r2 = Ring(point.([(3, 0), (5, 2), (7, 0)])) + r3 = Ring(point.([(6, 0), (8, 2), (10, 0)])) @test intersects(r1, r2) @test intersects(r2, r3) @test !intersects(r1, r3) - t = Triangle(P2(3, 1), P2(7, 5), P2(11, 1)) - q = Quadrangle(P2(2, 0), P2(2, 7), P2(12, 7), P2(12, 0)) - b = Box(P2(2, 0), P2(12, 7)) - s1 = Segment(P2(5, 2), P2(9, 2)) - s2 = Segment(P2(0, 3), P2(5, 3)) - s3 = Segment(P2(4, 4), P2(10, 4)) - s4 = Segment(P2(1, 6), P2(13, 6)) - s5 = Segment(P2(0, 9), P2(14, 9)) - r1 = Ring(P2[(1, 2), (7, 8), (13, 2)]) - r2 = Rope(P2[(1, 2), (7, 8), (13, 2)]) + t = Triangle(point(3, 1), point(7, 5), point(11, 1)) + q = Quadrangle(point(2, 0), point(2, 7), point(12, 7), point(12, 0)) + b = Box(point(2, 0), point(12, 7)) + s1 = Segment(point(5, 2), point(9, 2)) + s2 = Segment(point(0, 3), point(5, 3)) + s3 = Segment(point(4, 4), point(10, 4)) + s4 = Segment(point(1, 6), point(13, 6)) + s5 = Segment(point(0, 9), point(14, 9)) + r1 = Ring(point.([(1, 2), (7, 8), (13, 2)])) + r2 = Rope(point.([(1, 2), (7, 8), (13, 2)])) @test intersects(s1, t) @test intersects(s2, t) @test intersects(s3, t) @@ -507,40 +508,40 @@ @test intersects(r2, b) # performance test - b1 = Box(P2(0, 0), P2(3, 3)) - b2 = Box(P2(2, 2), P2(5, 5)) + b1 = Box(point(0, 0), point(3, 3)) + b2 = Box(point(2, 2), point(5, 5)) @test intersects(b1, b2) @test intersects(b2, b1) @test @elapsed(intersects(b1, b2)) < 5e-5 @test @allocated(intersects(b1, b2)) < 100 # partial application - points = P2[(0, 0), (1, 0), (1, 1), (0, 1)] + points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) poly = PolyArea(points) - box = Box(P2(0, 0), P2(2, 2)) + box = Box(point(0, 0), point(2, 2)) @test intersects(box)(poly) @test all(intersects(box), points) # method ambiguities - point = P2(3, 1) - ring = Ring(P2[(0, 0), (2, 2), (4, 0)]) - rope = Rope(P2[(2, 0), (4, 2), (6, 0)]) - seg = Segment(P2(0, 1), P2(6, 1)) + p = point(3, 1) + ring = Ring(point.([(0, 0), (2, 2), (4, 0)])) + rope = Rope(point.([(2, 0), (4, 2), (6, 0)])) + seg = Segment(point(0, 1), point(6, 1)) multi = Multi([ring]) - @test intersects(point, ring) - @test intersects(point, rope) - @test intersects(point, seg) - @test intersects(point, multi) + @test intersects(p, ring) + @test intersects(p, rope) + @test intersects(p, seg) + @test intersects(p, multi) @test intersects(ring, multi) @test intersects(rope, multi) @test intersects(seg, multi) end @testset "iscollinear" begin - @test iscollinear(P2(0, 0), P2(1, 1), P2(2, 2)) + @test iscollinear(point(0, 0), point(1, 1), point(2, 2)) end @testset "iscoplanar" begin - @test iscoplanar(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0)) + @test iscoplanar(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) end end diff --git a/test/primitives.jl b/test/primitives.jl index f0bf8d477..a300904fd 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -3,466 +3,455 @@ @test embeddim(Point(1)) == 1 @test embeddim(Point(1, 2)) == 2 @test embeddim(Point(1, 2, 3)) == 3 - @test coordtype(Point(1, 1)) == Float64 - @test coordtype(Point(1.0, 1.0)) == Float64 - @test coordtype(Point(1.0f0, 1.0f0)) == Float32 - @test coordtype(Point1(1)) == Float64 - @test coordtype(Point2(1, 1)) == Float64 - @test coordtype(Point3(1, 1, 1)) == Float64 - @test coordtype(Point1f(1)) == Float32 - @test coordtype(Point2f(1, 1)) == Float32 - @test coordtype(Point3f(1, 1, 1)) == Float32 - - @test coordtype(Point{2,T}((1, 1))) == T - @test coordtype(Point{2,T}(1, 1)) == T - - @test coordinates(P1(1)) == T[1] - @test coordinates(P2(1, 2)) == T[1, 2] - @test coordinates(P3(1, 2, 3)) == T[1, 2, 3] - - @test P1(1) - P1(1) == T[0] - @test P2(1, 2) - P2(1, 1) == T[0, 1] - @test P3(1, 2, 3) - P3(1, 1, 1) == T[0, 1, 2] - @test_throws DimensionMismatch P2(1, 2) - P3(1, 2, 3) - - @test P1(1) + V1(0) == P1(1) - @test P1(2) + V1(2) == P1(4) - @test P2(1, 2) + V2(0, 0) == P2(1, 2) - @test P2(2, 3) + V2(2, 1) == P2(4, 4) - @test P3(1, 2, 3) + V3(0, 0, 0) == P3(1, 2, 3) - @test P3(2, 3, 4) + V3(2, 1, 0) == P3(4, 4, 4) - @test_throws DimensionMismatch P2(1, 2) + V3(1, 2, 3) - - @test P1(1) - V1(0) == P1(1) - @test P1(2) - V1(2) == P1(0) - @test P2(1, 2) - V2(0, 0) == P2(1, 2) - @test P2(2, 3) - V2(2, 1) == P2(0, 2) - @test P3(1, 2, 3) - V3(0, 0, 0) == P3(1, 2, 3) - @test P3(2, 3, 4) - V3(2, 1, 0) == P3(0, 2, 4) - - @test embeddim(rand(P1)) == 1 - @test embeddim(rand(P2)) == 2 - @test embeddim(rand(P3)) == 3 - @test coordtype(rand(P1)) == T - @test coordtype(rand(P2)) == T - @test coordtype(rand(P3)) == T - - @test eltype(rand(P1, 3)) == P1 - @test eltype(rand(P2, 3)) == P2 - @test eltype(rand(P3, 3)) == P3 - - @test P1(1) ≈ P1(1 + eps(T)) - @test P2(1, 2) ≈ P2(1 + eps(T), T(2)) - @test P3(1, 2, 3) ≈ P3(1 + eps(T), T(2), T(3)) + @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1.0, 1.0)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1.0f0, 1.0f0)) == Meshes.Met{Float32} + + @test Meshes.lentype(Point((T(1), T(1)))) == ℳ + @test Meshes.lentype(Point(T(1), T(1))) == ℳ + + @test coordinates(point(1)) == vector(1) + @test coordinates(point(1, 2)) == vector(1, 2) + @test coordinates(point(1, 2, 3)) == vector(1, 2, 3) + + @test point(1) - point(1) == vector(0) + @test point(1, 2) - point(1, 1) == vector(0, 1) + @test point(1, 2, 3) - point(1, 1, 1) == vector(0, 1, 2) + @test_throws MethodError point(1, 2) - point(1, 2, 3) + + @test point(1) + vector(0) == point(1) + @test point(2) + vector(2) == point(4) + @test point(1, 2) + vector(0, 0) == point(1, 2) + @test point(2, 3) + vector(2, 1) == point(4, 4) + @test point(1, 2, 3) + vector(0, 0, 0) == point(1, 2, 3) + @test point(2, 3, 4) + vector(2, 1, 0) == point(4, 4, 4) + @test_throws MethodError point(1, 2) + vector(1, 2, 3) + + @test point(1) - vector(0) == point(1) + @test point(2) - vector(2) == point(0) + @test point(1, 2) - vector(0, 0) == point(1, 2) + @test point(2, 3) - vector(2, 1) == point(0, 2) + @test point(1, 2, 3) - vector(0, 0, 0) == point(1, 2, 3) + @test point(2, 3, 4) - vector(2, 1, 0) == point(0, 2, 4) + + @test embeddim(rand(Point{1})) == 1 + @test embeddim(rand(Point{2})) == 2 + @test embeddim(rand(Point{3})) == 3 + @test Meshes.lentype(rand(Point{1})) == Meshes.Met{Float64} + @test Meshes.lentype(rand(Point{2})) == Meshes.Met{Float64} + @test Meshes.lentype(rand(Point{3})) == Meshes.Met{Float64} + + @test point(1) ≈ point(1 + eps(T)) + @test point(1, 2) ≈ point(1 + eps(T), T(2)) + @test point(1, 2, 3) ≈ point(1 + eps(T), T(2), T(3)) @test embeddim(Point((1,))) == 1 - @test coordtype(Point((1,))) == Float64 - @test coordtype(Point((1.0,))) == Float64 + @test Meshes.lentype(Point((1,))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0,))) == Meshes.Met{Float64} @test embeddim(Point((1, 2))) == 2 - @test coordtype(Point((1, 2))) == Float64 - @test coordtype(Point((1.0, 2.0))) == Float64 + @test Meshes.lentype(Point((1, 2))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0, 2.0))) == Meshes.Met{Float64} @test embeddim(Point((1, 2, 3))) == 3 - @test coordtype(Point((1, 2, 3))) == Float64 - @test coordtype(Point((1.0, 2.0, 3.0))) == Float64 + @test Meshes.lentype(Point((1, 2, 3))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0, 2.0, 3.0))) == Meshes.Met{Float64} # check all 1D Point constructors, because those tend to make trouble @test Point(1) == Point((1,)) - @test Point{1,T}(-2) == Point{1,T}((-2,)) - @test Point{1,T}(0) == Point{1,T}((0,)) - - @test_throws DimensionMismatch Point{2,T}(1) - @test_throws DimensionMismatch Point{3,T}((2, 3)) - @test_throws DimensionMismatch Point{-3,T}((4, 5, 6)) - - # There are 2 cases that throw a MethodError instead of a DimensionMismatch: - # `Point{1,T}((2,3))` because it tries to take the tuple as a whole and convert to T and: - # `Point{1,T}(2,3)` which does about the same. - # I don't think this can reasonably be fixed here without hurting performance + @test Point(T(-2)) == Point((T(-2),)) + @test Point(T(0)) == Point((T(0),)) # check that input of mixed coordinate types is allowed and works as expected - @test Point(1, 0.2) == Point{2,Float64}(1.0, 0.2) - @test Point((3.0, 4)) == Point{2,Float64}(3.0, 4.0) - @test Point((5.0, 6.0, 7)) == Point{3,Float64}(5.0, 6.0, 7.0) - @test Point{2,T}(8, 9.0) == Point{2,T}((8.0, 9.0)) - @test Point{2,T}((-1.0, -2)) == Point{2,T}((-1, -2)) - @test Point{4,T}((0, -1.0, +2, -4.0)) == Point{4,T}((0.0f0, -1.0f0, +2.0f0, -4.0f0)) + @test Point(1, 0.2) == Point(1.0, 0.2) + @test Point((3.0, 4)) == Point(3.0, 4.0) + @test Point((5.0, 6.0, 7)) == Point(5.0, 6.0, 7.0) + @test Point(8, T(9.0)) == Point((T(8.0), T(9.0))) + @test Point((T(-1.0), -2)) == Point((T(-1.0), T(-2.0))) + @test Point((0, T(-1.0), +2, T(-4.0))) == Point((T(0.0), T(-1.0), T(+2.0), T(-4.0))) # Integer coordinates converted to Float64 - @test coordtype(Point(1)) == Float64 - @test coordtype(Point(1, 2)) == Float64 - @test coordtype(Point(1, 2, 3)) == Float64 + @test Meshes.lentype(Point(1)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1, 2)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1, 2, 3)) == Meshes.Met{Float64} # Unitful coordinates - point = Point(1u"m", 1u"m") - @test unit(coordtype(point)) == u"m" - @test Unitful.numtype(coordtype(point)) === Float64 - point = Point(1.0u"m", 1.0u"m") - @test unit(coordtype(point)) == u"m" - @test Unitful.numtype(coordtype(point)) === Float64 - point = Point(1.0f0u"m", 1.0f0u"m") - @test unit(coordtype(point)) == u"m" - @test Unitful.numtype(coordtype(point)) === Float32 + p = Point(1u"m", 1u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float64 + p = Point(1.0u"m", 1.0u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float64 + p = Point(1.0f0u"m", 1.0f0u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float32 # generalized inequality - @test P2(1, 1) ⪯ P2(1, 1) - @test !(P2(1, 1) ≺ P2(1, 1)) - @test P2(1, 2) ⪯ P2(3, 4) - @test P2(1, 2) ≺ P2(3, 4) - @test P2(1, 1) ⪰ P2(1, 1) - @test !(P2(1, 1) ≻ P2(1, 1)) - @test P2(3, 4) ⪰ P2(1, 2) - @test P2(3, 4) ≻ P2(1, 2) + @test point(1, 1) ⪯ point(1, 1) + @test !(point(1, 1) ≺ point(1, 1)) + @test point(1, 2) ⪯ point(3, 4) + @test point(1, 2) ≺ point(3, 4) + @test point(1, 1) ⪰ point(1, 1) + @test !(point(1, 1) ≻ point(1, 1)) + @test point(3, 4) ⪰ point(1, 2) + @test point(3, 4) ≻ point(1, 2) # center and centroid - @test Meshes.center(P2(1, 1)) == P2(1, 1) - @test centroid(P2(1, 1)) == P2(1, 1) + @test Meshes.center(point(1, 1)) == point(1, 1) + @test centroid(point(1, 1)) == point(1, 1) # measure of points is zero - @test measure(P2(1, 2)) == zero(T) - @test measure(P3(1, 2, 3)) == zero(T) + @test measure(point(1, 2)) == zero(ℳ) + @test measure(point(1, 2, 3)) == zero(ℳ) # boundary of points is nothing - @test isnothing(boundary(rand(P1))) - @test isnothing(boundary(rand(P2))) - @test isnothing(boundary(rand(P3))) + @test isnothing(boundary(rand(Point{1}))) + @test isnothing(boundary(rand(Point{2}))) + @test isnothing(boundary(rand(Point{3}))) # check broadcasting works as expected - @test P2(2, 2) .- [P2(2, 3), P2(3, 1)] == [[0.0, -1.0], [-1.0, 1.0]] - @test P3(2, 2, 2) .- [P3(2, 3, 1), P3(3, 1, 4)] == [[0.0, -1.0, 1.0], [-1.0, 1.0, -2.0]] + @test point(2, 2) .- [point(2, 3), point(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] + @test point(2, 2, 2) .- [point(2, 3, 1), point(3, 1, 4)] == [vector(0.0, -1.0, 1.0), vector(-1.0, 1.0, -2.0)] # angles between 2D points - @test ∠(P2(0, 1), P2(0, 0), P2(1, 0)) ≈ T(-π / 2) - @test ∠(P2(1, 0), P2(0, 0), P2(0, 1)) ≈ T(π / 2) - @test ∠(P2(-1, 0), P2(0, 0), P2(0, 1)) ≈ T(-π / 2) - @test ∠(P2(0, 1), P2(0, 0), P2(-1, 0)) ≈ T(π / 2) - @test ∠(P2(0, -1), P2(0, 0), P2(1, 0)) ≈ T(π / 2) - @test ∠(P2(1, 0), P2(0, 0), P2(0, -1)) ≈ T(-π / 2) - @test ∠(P2(0, -1), P2(0, 0), P2(-1, 0)) ≈ T(-π / 2) - @test ∠(P2(-1, 0), P2(0, 0), P2(0, -1)) ≈ T(π / 2) + @test ∠(point(0, 1), point(0, 0), point(1, 0)) ≈ T(-π / 2) + @test ∠(point(1, 0), point(0, 0), point(0, 1)) ≈ T(π / 2) + @test ∠(point(-1, 0), point(0, 0), point(0, 1)) ≈ T(-π / 2) + @test ∠(point(0, 1), point(0, 0), point(-1, 0)) ≈ T(π / 2) + @test ∠(point(0, -1), point(0, 0), point(1, 0)) ≈ T(π / 2) + @test ∠(point(1, 0), point(0, 0), point(0, -1)) ≈ T(-π / 2) + @test ∠(point(0, -1), point(0, 0), point(-1, 0)) ≈ T(-π / 2) + @test ∠(point(-1, 0), point(0, 0), point(0, -1)) ≈ T(π / 2) # angles between 3D points - @test ∠(P3(1, 0, 0), P3(0, 0, 0), P3(0, 1, 0)) ≈ T(π / 2) - @test ∠(P3(1, 0, 0), P3(0, 0, 0), P3(0, 0, 1)) ≈ T(π / 2) - @test ∠(P3(0, 1, 0), P3(0, 0, 0), P3(1, 0, 0)) ≈ T(π / 2) - @test ∠(P3(0, 1, 0), P3(0, 0, 0), P3(0, 0, 1)) ≈ T(π / 2) - @test ∠(P3(0, 0, 1), P3(0, 0, 0), P3(1, 0, 0)) ≈ T(π / 2) - @test ∠(P3(0, 0, 1), P3(0, 0, 0), P3(0, 1, 0)) ≈ T(π / 2) + @test ∠(point(1, 0, 0), point(0, 0, 0), point(0, 1, 0)) ≈ T(π / 2) + @test ∠(point(1, 0, 0), point(0, 0, 0), point(0, 0, 1)) ≈ T(π / 2) + @test ∠(point(0, 1, 0), point(0, 0, 0), point(1, 0, 0)) ≈ T(π / 2) + @test ∠(point(0, 1, 0), point(0, 0, 0), point(0, 0, 1)) ≈ T(π / 2) + @test ∠(point(0, 0, 1), point(0, 0, 0), point(1, 0, 0)) ≈ T(π / 2) + @test ∠(point(0, 0, 1), point(0, 0, 0), point(0, 1, 0)) ≈ T(π / 2) # a point pertains to itself - p = P2(0, 0) - q = P2(1, 1) + p = point(0, 0) + q = point(1, 1) @test p ∈ p @test q ∈ q @test p ∉ q @test q ∉ p - p = P3(0, 0, 0) - q = P3(1, 1, 1) + p = point(0, 0, 0) + q = point(1, 1, 1) @test p ∈ p @test q ∈ q @test p ∉ q @test q ∉ p - p = P2(0, 1) - @test sprint(show, p, context=:compact => true) == "(0.0, 1.0)" + p = point(0, 1) + @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" if T === Float32 - @test sprint(show, p) == "Point(0.0f0, 1.0f0)" + @test sprint(show, p) == "Point(x: 0.0f0 m, y: 1.0f0 m)" + @test sprint(show, MIME("text/plain"), p) == """ + Point with Cartesian{NoDatum} coordinates + ├─ x: 0.0f0 m + └─ y: 1.0f0 m""" else - @test sprint(show, p) == "Point(0.0, 1.0)" + @test sprint(show, p) == "Point(x: 0.0 m, y: 1.0 m)" + @test sprint(show, MIME("text/plain"), p) == """ + Point with Cartesian{NoDatum} coordinates + ├─ x: 0.0 m + └─ y: 1.0 m""" end end @testset "Ray" begin - r = Ray(P2(0, 0), V2(1, 1)) + r = Ray(point(0, 0), vector(1, 1)) @test paramdim(r) == 1 - @test measure(r) == T(Inf) - @test length(r) == T(Inf) - @test boundary(r) == P2(0, 0) - @test perimeter(r) == zero(T) - - r = Ray(P2(0, 0), V2(1, 1)) - @test r(T(0.0)) == P2(0, 0) - @test r(T(1.0)) == P2(1, 1) - @test r(T(Inf)) == P2(Inf, Inf) - @test r(T(1.0)) - r(T(0.0)) == V2(1, 1) + @test measure(r) == typemax(ℳ) + @test length(r) == typemax(ℳ) + @test boundary(r) == point(0, 0) + @test perimeter(r) == zero(ℳ) + + r = Ray(point(0, 0), vector(1, 1)) + @test r(T(0.0)) == point(0, 0) + @test r(T(1.0)) == point(1, 1) + @test r(T(Inf)) == point(Inf, Inf) + @test r(T(1.0)) - r(T(0.0)) == vector(1, 1) @test_throws DomainError(T(-1), "r(t) is not defined for t < 0.") r(T(-1)) - p₁ = P3(3, 3, 3) - p₂ = P3(-3, -3, -3) - p₃ = P3(1, 0, 0) - r = Ray(P3(0, 0, 0), V3(1, 1, 1)) + p₁ = point(3, 3, 3) + p₂ = point(-3, -3, -3) + p₃ = point(1, 0, 0) + r = Ray(point(0, 0, 0), vector(1, 1, 1)) @test p₁ ∈ r @test p₂ ∉ r @test p₃ ∉ r - r1 = Ray(P3(0, 0, 0), V3(1, 0, 0)) - r2 = Ray(P3(1, 1, 1), V3(1, 2, 1)) + r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(point(1, 1, 1), vector(1, 2, 1)) @test r1 != r2 - r1 = Ray(P3(0, 0, 0), V3(1, 0, 0)) - r2 = Ray(P3(1, 0, 0), V3(-1, 0, 0)) + r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(point(1, 0, 0), vector(-1, 0, 0)) @test r1 != r2 - r1 = Ray(P3(0, 0, 0), V3(1, 0, 0)) - r2 = Ray(P3(1, 0, 0), V3(1, 0, 0)) + r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(point(1, 0, 0), vector(1, 0, 0)) @test r1 != r2 - r1 = Ray(P3(0, 0, 0), V3(2, 0, 0)) - r2 = Ray(P3(0, 0, 0), V3(1, 0, 0)) + r1 = Ray(point(0, 0, 0), vector(2, 0, 0)) + r2 = Ray(point(0, 0, 0), vector(1, 0, 0)) @test r1 == r2 - r2 = rand(Ray{2,T}) - r3 = rand(Ray{3,T}) + r2 = rand(Ray{2}) + r3 = rand(Ray{3}) @test r2 isa Ray @test r3 isa Ray @test embeddim(r2) == 2 @test embeddim(r3) == 3 - r = Ray(P2(0, 0), V2(1, 1)) - @test sprint(show, r) == "Ray(p: (0.0, 0.0), v: (1.0, 1.0))" + r = Ray(point(0, 0), vector(1, 1)) + @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), r) == """ - Ray{2,Float32} - ├─ p: Point(0.0f0, 0.0f0) - └─ v: Vec(1.0f0, 1.0f0)""" + Ray + ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ v: Vec(1.0f0 m, 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), r) == """ - Ray{2,Float64} - ├─ p: Point(0.0, 0.0) - └─ v: Vec(1.0, 1.0)""" + Ray + ├─ p: Point(x: 0.0 m, y: 0.0 m) + └─ v: Vec(1.0 m, 1.0 m)""" end end @testset "Line" begin - l = Line(P2(0, 0), P2(1, 1)) + l = Line(point(0, 0), point(1, 1)) @test paramdim(l) == 1 - @test measure(l) == T(Inf) - @test length(l) == T(Inf) + @test measure(l) == typemax(ℳ) + @test length(l) == typemax(ℳ) @test isnothing(boundary(l)) - @test perimeter(l) == zero(T) + @test perimeter(l) == zero(ℳ) - l = Line(P2(0, 0), P2(1, 1)) - @test (l(0), l(1)) == (P2(0, 0), P2(1, 1)) + l = Line(point(0, 0), point(1, 1)) + @test (l(0), l(1)) == (point(0, 0), point(1, 1)) - l2 = rand(Line{2,T}) - l3 = rand(Line{3,T}) + l2 = rand(Line{2}) + l3 = rand(Line{3}) @test l2 isa Line @test l3 isa Line @test embeddim(l2) == 2 @test embeddim(l3) == 3 - l = Line(P2(0, 0), P2(1, 1)) - @test sprint(show, l) == "Line(a: (0.0, 0.0), b: (1.0, 1.0))" + l = Line(point(0, 0), point(1, 1)) + @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), l) == """ - Line{2,Float32} - ├─ a: Point(0.0f0, 0.0f0) - └─ b: Point(1.0f0, 1.0f0)""" + Line + ├─ a: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ b: Point(x: 1.0f0 m, y: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), l) == """ - Line{2,Float64} - ├─ a: Point(0.0, 0.0) - └─ b: Point(1.0, 1.0)""" + Line + ├─ a: Point(x: 0.0 m, y: 0.0 m) + └─ b: Point(x: 1.0 m, y: 1.0 m)""" end end @testset "Plane" begin - p = Plane(P3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0)) - @test p(T(1), T(0)) == P3(1, 0, 0) + p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test p(T(1), T(0)) == point(1, 0, 0) @test paramdim(p) == 2 @test embeddim(p) == 3 - @test measure(p) == T(Inf) - @test area(p) == T(Inf) - @test p(T(0), T(0)) == P3(0, 0, 0) + @test measure(p) == typemax(ℳ)^2 + @test area(p) == typemax(ℳ)^2 + @test p(T(0), T(0)) == point(0, 0, 0) @test normal(p) == Vec(0, 0, 1) @test isnothing(boundary(p)) - @test perimeter(p) == zero(T) + @test perimeter(p) == zero(ℳ) - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) - @test p(T(1), T(0)) == P3(1, 0, 0) - @test p(T(0), T(1)) == P3(0, 1, 0) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + @test p(T(1), T(0)) == point(1, 0, 0) + @test p(T(0), T(1)) == point(0, 1, 0) - p₁ = Plane(P3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0)) - p₂ = Plane(P3(0, 0, 0), V3(0, 1, 0), V3(1, 0, 0)) + p₁ = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p₂ = Plane(point(0, 0, 0), vector(0, 1, 0), vector(1, 0, 0)) @test p₁ ≈ p₂ - p₁ = Plane(P3(0, 0, 0), V3(1, 1, 0)) - p₂ = Plane(P3(0, 0, 0), -V3(1, 1, 0)) + p₁ = Plane(point(0, 0, 0), vector(1, 1, 0)) + p₂ = Plane(point(0, 0, 0), -vector(1, 1, 0)) @test p₁ ≈ p₂ # https://github.com/JuliaGeometry/Meshes.jl/issues/624 - p₁ = Plane(P3(0, 0, 0), V3(0, 0, 1)) - p₂ = Plane(P3(0, 0, 10), V3(0, 0, 1)) + p₁ = Plane(point(0, 0, 0), vector(0, 0, 1)) + p₂ = Plane(point(0, 0, 10), vector(0, 0, 1)) @test !(p₁ ≈ p₂) # normal to plane has norm one regardless of basis - p = Plane(P3(0, 0, 0), V3(2, 0, 0), V3(0, 3, 0)) + p = Plane(point(0, 0, 0), vector(2, 0, 0), vector(0, 3, 0)) n = normal(p) - @test isapprox(norm(n), T(1), atol=atol(T)) + @test isapprox(norm(n), oneunit(ℳ), atol=atol(ℳ)) # plane passing through three points - p₁ = P3(0, 0, 0) - p₂ = P3(1, 2, 3) - p₃ = P3(3, 2, 1) + p₁ = point(0, 0, 0) + p₂ = point(1, 2, 3) + p₃ = point(3, 2, 1) p = Plane(p₁, p₂, p₃) @test p₁ ∈ p @test p₂ ∈ p @test p₃ ∈ p - p = rand(Plane{T}) + p = rand(Plane) @test p isa Plane @test embeddim(p) == 3 - p = Plane(P3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0)) - @test sprint(show, p) == "Plane(p: (0.0, 0.0, 0.0), u: (1.0, 0.0, 0.0), v: (0.0, 1.0, 0.0))" + p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test sprint(show, p) == + "Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, 0.0 m, 0.0 m), v: (0.0 m, 1.0 m, 0.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), p) == """ - Plane{3,Float32} - ├─ p: Point(0.0f0, 0.0f0, 0.0f0) - ├─ u: Vec(1.0f0, 0.0f0, 0.0f0) - └─ v: Vec(0.0f0, 1.0f0, 0.0f0)""" + Plane + ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ u: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) + └─ v: Vec(0.0f0 m, 1.0f0 m, 0.0f0 m)""" else @test sprint(show, MIME("text/plain"), p) == """ - Plane{3,Float64} - ├─ p: Point(0.0, 0.0, 0.0) - ├─ u: Vec(1.0, 0.0, 0.0) - └─ v: Vec(0.0, 1.0, 0.0)""" + Plane + ├─ p: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ u: Vec(1.0 m, 0.0 m, 0.0 m) + └─ v: Vec(0.0 m, 1.0 m, 0.0 m)""" end end @testset "BezierCurve" begin - b = BezierCurve(P2(0, 0), P2(0.5, 1), P2(1, 0)) + b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) @test embeddim(b) == 2 @test paramdim(b) == 1 - b = BezierCurve(P2(0, 0), P2(0.5, 1), P2(1, 0)) + b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) for method in [DeCasteljau(), Horner()] - @test b(T(0), method) == P2(0, 0) - @test b(T(1), method) == P2(1, 0) - @test b(T(0.5), method) == P2(0.5, 0.5) - @test b(T(0.5), method) == P2(0.5, 0.5) + @test b(T(0), method) == point(0, 0) + @test b(T(1), method) == point(1, 0) + @test b(T(0.5), method) == point(0.5, 0.5) + @test b(T(0.5), method) == point(0.5, 0.5) @test_throws DomainError(T(-0.1), "b(t) is not defined for t outside [0, 1].") b(T(-0.1), method) @test_throws DomainError(T(1.2), "b(t) is not defined for t outside [0, 1].") b(T(1.2), method) end - @test boundary(b) == Multi([P2(0, 0), P2(1, 0)]) - b = BezierCurve(P2(0, 0), P2(1, 1)) - @test boundary(b) == Multi([P2(0, 0), P2(1, 1)]) - @test perimeter(b) == zero(T) + @test boundary(b) == Multi([point(0, 0), point(1, 0)]) + b = BezierCurve(point(0, 0), point(1, 1)) + @test boundary(b) == Multi([point(0, 0), point(1, 1)]) + @test perimeter(b) == zero(ℳ) - b = BezierCurve(P2.(randn(100), randn(100))) + b = BezierCurve(point.(randn(100), randn(100))) t1 = @timed b(T(0.2)) t2 = @timed b(T(0.2), Horner()) @test t1.time > t2.time @test t2.bytes < 100 - b2 = rand(BezierCurve{2,T}) - b3 = rand(BezierCurve{3,T}) + b2 = rand(BezierCurve{2}) + b3 = rand(BezierCurve{3}) @test b2 isa BezierCurve @test b3 isa BezierCurve @test embeddim(b2) == 2 @test embeddim(b3) == 3 - b = BezierCurve(P2(0, 0), P2(0.5, 1), P2(1, 0)) + b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) + @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" if T === Float32 - @test sprint(show, b) == "BezierCurve(controls: Point2f[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)])" @test sprint(show, MIME("text/plain"), b) == """ - BezierCurve{2,Float32} - └─ controls: Point2f[Point(0.0f0, 0.0f0), Point(0.5f0, 1.0f0), Point(1.0f0, 0.0f0)]""" + BezierCurve + └─ controls: [Point(x: 0.0f0 m, y: 0.0f0 m), Point(x: 0.5f0 m, y: 1.0f0 m), Point(x: 1.0f0 m, y: 0.0f0 m)]""" else - @test sprint(show, b) == "BezierCurve(controls: Point2[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)])" @test sprint(show, MIME("text/plain"), b) == """ - BezierCurve{2,Float64} - └─ controls: Point2[Point(0.0, 0.0), Point(0.5, 1.0), Point(1.0, 0.0)]""" + BezierCurve + └─ controls: [Point(x: 0.0 m, y: 0.0 m), Point(x: 0.5 m, y: 1.0 m), Point(x: 1.0 m, y: 0.0 m)]""" end end @testset "Box" begin - b = Box(P1(0), P1(1)) + b = Box(point(0), point(1)) @test embeddim(b) == 1 @test paramdim(b) == 1 - @test coordtype(b) == T - @test minimum(b) == P1(0) - @test maximum(b) == P1(1) - @test extrema(b) == (P1(0), P1(1)) + @test Meshes.lentype(b) == ℳ + @test minimum(b) == point(0) + @test maximum(b) == point(1) + @test extrema(b) == (point(0), point(1)) - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) @test embeddim(b) == 2 @test paramdim(b) == 2 - @test coordtype(b) == T - @test minimum(b) == P2(0, 0) - @test maximum(b) == P2(1, 1) - @test extrema(b) == (P2(0, 0), P2(1, 1)) + @test Meshes.lentype(b) == ℳ + @test minimum(b) == point(0, 0) + @test maximum(b) == point(1, 1) + @test extrema(b) == (point(0, 0), point(1, 1)) - b = Box(P3(0, 0, 0), P3(1, 1, 1)) + b = Box(point(0, 0, 0), point(1, 1, 1)) @test embeddim(b) == 3 @test paramdim(b) == 3 - @test coordtype(b) == T - @test minimum(b) == P3(0, 0, 0) - @test maximum(b) == P3(1, 1, 1) - @test extrema(b) == (P3(0, 0, 0), P3(1, 1, 1)) - - b = Box(P1(0), P1(1)) - @test boundary(b) == Multi([P1(0), P1(1)]) - @test measure(b) == T(1) - @test P1(0) ∈ b - @test P1(1) ∈ b - @test P1(0.5) ∈ b - @test P1(-0.5) ∉ b - @test P1(1.5) ∉ b - - b = Box(P2(0, 0), P2(1, 1)) - @test measure(b) == area(b) == T(1) - @test P2(1, 1) ∈ b - @test perimeter(b) ≈ T(4) - - b = Box(P2(1, 1), P2(2, 2)) - @test sides(b) == T.((1, 1)) - @test Meshes.center(b) == P2(1.5, 1.5) - @test diagonal(b) == √T(2) - - b = Box(P2(1, 2), P2(3, 4)) - v = P2[(1, 2), (3, 2), (3, 4), (1, 4)] + @test Meshes.lentype(b) == ℳ + @test minimum(b) == point(0, 0, 0) + @test maximum(b) == point(1, 1, 1) + @test extrema(b) == (point(0, 0, 0), point(1, 1, 1)) + + b = Box(point(0), point(1)) + @test boundary(b) == Multi([point(0), point(1)]) + @test measure(b) == T(1) * u"m" + @test point(0) ∈ b + @test point(1) ∈ b + @test point(0.5) ∈ b + @test point(-0.5) ∉ b + @test point(1.5) ∉ b + + b = Box(point(0, 0), point(1, 1)) + @test measure(b) == area(b) == T(1) * u"m^2" + @test point(1, 1) ∈ b + @test perimeter(b) ≈ T(4) * u"m" + + b = Box(point(1, 1), point(2, 2)) + @test sides(b) == (T(1) * u"m", T(1) * u"m") + @test Meshes.center(b) == point(1.5, 1.5) + @test diagonal(b) == √T(2) * u"m" + + b = Box(point(1, 2), point(3, 4)) + v = point.([(1, 2), (3, 2), (3, 4), (1, 4)]) @test boundary(b) == Ring(v) - b = Box(P3(1, 2, 3), P3(4, 5, 6)) - v = P3[(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)] + b = Box(point(1, 2, 3), point(4, 5, 6)) + v = point.([(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)]) c = connect.([(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)]) @test boundary(b) == SimpleMesh(v, c) - b = Box(P2(0, 0), P2(1, 1)) - @test boundary(b) == Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + b = Box(point(0, 0), point(1, 1)) + @test boundary(b) == Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - b = Box(P3(0, 0, 0), P3(1, 1, 1)) + b = Box(point(0, 0, 0), point(1, 1, 1)) m = boundary(b) @test m isa Mesh @test nvertices(m) == 8 @test nelements(m) == 6 # subsetting with boxes - b1 = Box(P2(0, 0), P2(0.5, 0.5)) - b2 = Box(P2(0.1, 0.1), P2(0.5, 0.5)) - b3 = Box(P2(0, 0), P2(1, 1)) + b1 = Box(point(0, 0), point(0.5, 0.5)) + b2 = Box(point(0.1, 0.1), point(0.5, 0.5)) + b3 = Box(point(0, 0), point(1, 1)) @test b1 ⊆ b3 @test b2 ⊆ b3 @test !(b1 ⊆ b2) @test !(b3 ⊆ b1) @test !(b3 ⊆ b1) - b = Box(P2(0, 0), P2(10, 20)) - @test b(T(0.0), T(0.0)) == P2(0, 0) - @test b(T(0.5), T(0.0)) == P2(5, 0) - @test b(T(1.0), T(0.0)) == P2(10, 0) - @test b(T(0.0), T(0.5)) == P2(0, 10) - @test b(T(0.0), T(1.0)) == P2(0, 20) + b = Box(point(0, 0), point(10, 20)) + @test b(T(0.0), T(0.0)) == point(0, 0) + @test b(T(0.5), T(0.0)) == point(5, 0) + @test b(T(1.0), T(0.0)) == point(10, 0) + @test b(T(0.0), T(0.5)) == point(0, 10) + @test b(T(0.0), T(1.0)) == point(0, 20) - b = Box(P3(0, 0, 0), P3(10, 20, 30)) - @test b(T(0.0), T(0.0), T(0.0)) == P3(0, 0, 0) - @test b(T(1.0), T(1.0), T(1.0)) == P3(10, 20, 30) + b = Box(point(0, 0, 0), point(10, 20, 30)) + @test b(T(0.0), T(0.0), T(0.0)) == point(0, 0, 0) + @test b(T(1.0), T(1.0), T(1.0)) == point(10, 20, 30) - b1 = rand(Box{1,T}) - b2 = rand(Box{2,T}) - b3 = rand(Box{3,T}) + b1 = rand(Box{1}) + b2 = rand(Box{2}) + b3 = rand(Box{3}) @test b1 isa Box @test b2 isa Box @test b3 isa Box @@ -470,101 +459,101 @@ @test embeddim(b2) == 2 @test embeddim(b3) == 3 - @test_throws AssertionError Box(P1(1), P1(0)) - @test_throws AssertionError Box(P2(1, 1), P2(0, 0)) - @test_throws AssertionError Box(P3(1, 1, 1), P3(0, 0, 0)) + @test_throws AssertionError Box(point(1), point(0)) + @test_throws AssertionError Box(point(1, 1), point(0, 0)) + @test_throws AssertionError Box(point(1, 1, 1), point(0, 0, 0)) - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) q = convert(Quadrangle, b) @test q isa Quadrangle - @test q == Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + @test q == Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - b = Box(P3(0, 0, 0), P3(1, 1, 1)) + b = Box(point(0, 0, 0), point(1, 1, 1)) h = convert(Hexahedron, b) @test h isa Hexahedron @test h == Hexahedron( - P3(0, 0, 0), - P3(1, 0, 0), - P3(1, 1, 0), - P3(0, 1, 0), - P3(0, 0, 1), - P3(1, 0, 1), - P3(1, 1, 1), - P3(0, 1, 1) + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) ) - b = Box(P2(0, 0), P2(1, 1)) - @test sprint(show, b) == "Box(min: (0.0, 0.0), max: (1.0, 1.0))" + b = Box(point(0, 0), point(1, 1)) + @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), b) == """ - Box{2,Float32} - ├─ min: Point(0.0f0, 0.0f0) - └─ max: Point(1.0f0, 1.0f0)""" + Box + ├─ min: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ max: Point(x: 1.0f0 m, y: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), b) == """ - Box{2,Float64} - ├─ min: Point(0.0, 0.0) - └─ max: Point(1.0, 1.0)""" + Box + ├─ min: Point(x: 0.0 m, y: 0.0 m) + └─ max: Point(x: 1.0 m, y: 1.0 m)""" end end @testset "Ball" begin - b = Ball(P3(1, 2, 3), T(5)) + b = Ball(point(1, 2, 3), T(5)) @test embeddim(b) == 3 @test paramdim(b) == 3 - @test coordtype(b) == T - @test Meshes.center(b) == P3(1, 2, 3) - @test radius(b) == T(5) + @test Meshes.lentype(b) == ℳ + @test Meshes.center(b) == point(1, 2, 3) + @test radius(b) == T(5) * u"m" - b = Ball(P3(1, 2, 3), 4) - @test coordtype(b) == T + b = Ball(point(1, 2, 3), 4) + @test Meshes.lentype(b) == ℳ - b1 = Ball(P2(0, 0), T(1)) - b2 = Ball(P2(0, 0)) + b1 = Ball(point(0, 0), T(1)) + b2 = Ball(point(0, 0)) b3 = Ball(T.((0, 0))) @test b1 == b2 == b3 - b = Ball(P2(0, 0), T(2)) - @test measure(b) ≈ T(π) * (T(2)^2) - b = Ball(P3(0, 0, 0), T(2)) - @test measure(b) ≈ T(4 / 3) * T(π) * (T(2)^3) + b = Ball(point(0, 0), T(2)) + @test measure(b) ≈ T(π) * (T(2)^2) * u"m^2" + b = Ball(point(0, 0, 0), T(2)) + @test measure(b) ≈ T(4 / 3) * T(π) * (T(2)^3) * u"m^3" @test_throws ArgumentError length(b) @test_throws ArgumentError area(b) - b = Ball(P2(0, 0), T(2)) - @test P2(1, 0) ∈ b - @test P2(0, 1) ∈ b - @test P2(2, 0) ∈ b - @test P2(0, 2) ∈ b - @test P2(3, 5) ∉ b - @test perimeter(b) ≈ T(4π) - - b = Ball(P3(0, 0, 0), T(2)) - @test P3(1, 0, 0) ∈ b - @test P3(0, 0, 1) ∈ b - @test P3(2, 0, 0) ∈ b - @test P3(0, 0, 2) ∈ b - @test P3(3, 5, 2) ∉ b - - b = Ball(P2(0, 0), T(2)) - @test b(T(0), T(0)) ≈ P2(0, 0) - @test b(T(1), T(0)) ≈ P2(2, 0) - - b = Ball(P2(7, 7), T(1.5)) + b = Ball(point(0, 0), T(2)) + @test point(1, 0) ∈ b + @test point(0, 1) ∈ b + @test point(2, 0) ∈ b + @test point(0, 2) ∈ b + @test point(3, 5) ∉ b + @test perimeter(b) ≈ T(4π) * u"m" + + b = Ball(point(0, 0, 0), T(2)) + @test point(1, 0, 0) ∈ b + @test point(0, 0, 1) ∈ b + @test point(2, 0, 0) ∈ b + @test point(0, 0, 2) ∈ b + @test point(3, 5, 2) ∉ b + + b = Ball(point(0, 0), T(2)) + @test b(T(0), T(0)) ≈ point(0, 0) + @test b(T(1), T(0)) ≈ point(2, 0) + + b = Ball(point(7, 7), T(1.5)) ps = b.(1, rand(T, 100)) all(∈(b), ps) - b = Ball(P3(0, 0, 0), T(2)) - @test b(T(0), T(0), T(0)) ≈ P3(0, 0, 0) - @test b(T(1), T(0), T(0)) ≈ P3(0, 0, 2) + b = Ball(point(0, 0, 0), T(2)) + @test b(T(0), T(0), T(0)) ≈ point(0, 0, 0) + @test b(T(1), T(0), T(0)) ≈ point(0, 0, 2) - b = Ball(P3(7, 7, 7), T(1.5)) + b = Ball(point(7, 7, 7), T(1.5)) ps = b.(1, rand(T, 100), rand(T, 100)) all(∈(b), ps) - b1 = rand(Ball{1,T}) - b2 = rand(Ball{2,T}) - b3 = rand(Ball{3,T}) + b1 = rand(Ball{1}) + b2 = rand(Ball{2}) + b3 = rand(Ball{3}) @test b1 isa Ball @test b2 isa Ball @test b3 isa Ball @@ -572,103 +561,103 @@ @test embeddim(b2) == 2 @test embeddim(b3) == 3 - b = Ball(P2(0, 0), T(1)) - @test sprint(show, b) == "Ball(center: (0.0, 0.0), radius: 1.0)" + b = Ball(point(0, 0), T(1)) + @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), b) == """ - Ball{2,Float32} - ├─ center: Point(0.0f0, 0.0f0) - └─ radius: 1.0f0""" + Ball + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ radius: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), b) == """ - Ball{2,Float64} - ├─ center: Point(0.0, 0.0) - └─ radius: 1.0""" + Ball + ├─ center: Point(x: 0.0 m, y: 0.0 m) + └─ radius: 1.0 m""" end end @testset "Sphere" begin - s = Sphere(P3(0, 0, 0), T(1)) + s = Sphere(point(0, 0, 0), T(1)) @test embeddim(s) == 3 @test paramdim(s) == 2 - @test coordtype(s) == T - @test Meshes.center(s) == P3(0, 0, 0) - @test radius(s) == T(1) - @test extrema(s) == (P3(-1, -1, -1), P3(1, 1, 1)) + @test Meshes.lentype(s) == ℳ + @test Meshes.center(s) == point(0, 0, 0) + @test radius(s) == T(1) * u"m" + @test extrema(s) == (point(-1, -1, -1), point(1, 1, 1)) @test isnothing(boundary(s)) - @test perimeter(s) == zero(T) + @test perimeter(s) == zero(ℳ) - s = Sphere(P3(1, 2, 3), 4) - @test coordtype(s) == T + s = Sphere(point(1, 2, 3), 4) + @test Meshes.lentype(s) == ℳ - s = Sphere(P2(0, 0), T(1)) + s = Sphere(point(0, 0), T(1)) @test embeddim(s) == 2 @test paramdim(s) == 1 - @test coordtype(s) == T - @test Meshes.center(s) == P2(0, 0) - @test radius(s) == T(1) - @test extrema(s) == (P2(-1, -1), P2(1, 1)) + @test Meshes.lentype(s) == ℳ + @test Meshes.center(s) == point(0, 0) + @test radius(s) == T(1) * u"m" + @test extrema(s) == (point(-1, -1), point(1, 1)) @test isnothing(boundary(s)) - s1 = Sphere(P2(0, 0), T(1)) - s2 = Sphere(P2(0, 0)) + s1 = Sphere(point(0, 0), T(1)) + s2 = Sphere(point(0, 0)) s3 = Sphere(T.((0, 0))) @test s1 == s2 == s3 - s = Sphere(P2(0, 0), T(2)) - @test measure(s) ≈ 2π * 2 - @test length(s) ≈ 2π * 2 - @test extrema(s) == (P2(-2, -2), P2(2, 2)) - s = Sphere(P3(0, 0, 0), T(2)) - @test measure(s) ≈ 4π * (2^2) - @test area(s) ≈ 4π * (2^2) - - s = Sphere(P2(0, 0), T(2)) - @test P2(1, 0) ∉ s - @test P2(0, 1) ∉ s - @test P2(2, 0) ∈ s - @test P2(0, 2) ∈ s - @test P2(3, 5) ∉ s - - s = Sphere(P3(0, 0, 0), T(2)) - @test P3(1, 0, 0) ∉ s - @test P3(0, 0, 1) ∉ s - @test P3(2, 0, 0) ∈ s - @test P3(0, 0, 2) ∈ s - @test P3(3, 5, 2) ∉ s + s = Sphere(point(0, 0), T(2)) + @test measure(s) ≈ T(2π) * 2 * u"m" + @test length(s) ≈ T(2π) * 2 * u"m" + @test extrema(s) == (point(-2, -2), point(2, 2)) + s = Sphere(point(0, 0, 0), T(2)) + @test measure(s) ≈ T(4π) * (2^2) * u"m^2" + @test area(s) ≈ T(4π) * (2^2) * u"m^2" + + s = Sphere(point(0, 0), T(2)) + @test point(1, 0) ∉ s + @test point(0, 1) ∉ s + @test point(2, 0) ∈ s + @test point(0, 2) ∈ s + @test point(3, 5) ∉ s + + s = Sphere(point(0, 0, 0), T(2)) + @test point(1, 0, 0) ∉ s + @test point(0, 0, 1) ∉ s + @test point(2, 0, 0) ∈ s + @test point(0, 0, 2) ∈ s + @test point(3, 5, 2) ∉ s # 2D sphere passing through 3 points - s = Sphere(P2(0, 0), P2(0.5, 0), P2(1, 1)) - @test Meshes.center(s) == P2(0.25, 0.75) - @test radius(s) == T(0.7905694150420949) - s = Sphere(P2(0, 0), P2(1, 0), P2(0, 1)) - @test Meshes.center(s) == P2(0.5, 0.5) - @test radius(s) == T(0.7071067811865476) - s = Sphere(P2(0, 0), P2(1, 0), P2(1, 1)) - @test Meshes.center(s) == P2(0.5, 0.5) - @test radius(s) == T(0.7071067811865476) + s = Sphere(point(0, 0), point(0.5, 0), point(1, 1)) + @test Meshes.center(s) == point(0.25, 0.75) + @test radius(s) == T(0.7905694150420949) * u"m" + s = Sphere(point(0, 0), point(1, 0), point(0, 1)) + @test Meshes.center(s) == point(0.5, 0.5) + @test radius(s) == T(0.7071067811865476) * u"m" + s = Sphere(point(0, 0), point(1, 0), point(1, 1)) + @test Meshes.center(s) == point(0.5, 0.5) + @test radius(s) == T(0.7071067811865476) * u"m" # 3D sphere passing through 4 points - s = Sphere(P3(0, 0, 0), P3(5, 0, 1), P3(1, 1, 1), P3(3, 2, 1)) - @test P3(0, 0, 0) ∈ s - @test P3(5, 0, 1) ∈ s - @test P3(1, 1, 1) ∈ s - @test P3(3, 2, 1) ∈ s + s = Sphere(point(0, 0, 0), point(5, 0, 1), point(1, 1, 1), point(3, 2, 1)) + @test point(0, 0, 0) ∈ s + @test point(5, 0, 1) ∈ s + @test point(1, 1, 1) ∈ s + @test point(3, 2, 1) ∈ s O = Meshes.center(s) r = radius(s) - @test isapprox(r, norm(P3(0, 0, 0) - O)) + @test isapprox(r, norm(point(0, 0, 0) - O)) - s = Sphere(P2(0, 0), T(2)) - @test s(T(0)) ≈ P2(2, 0) - @test s(T(0.5)) ≈ P2(-2, 0) + s = Sphere(point(0, 0), T(2)) + @test s(T(0)) ≈ point(2, 0) + @test s(T(0.5)) ≈ point(-2, 0) - s = Sphere(P3(0, 0, 0), T(2)) - @test s(T(0), T(0)) ≈ P3(0, 0, 2) - @test s(T(0.5), T(0.5)) ≈ P3(-2, 0, 0) + s = Sphere(point(0, 0, 0), T(2)) + @test s(T(0), T(0)) ≈ point(0, 0, 2) + @test s(T(0.5), T(0.5)) ≈ point(-2, 0, 0) - s1 = rand(Sphere{1,T}) - s2 = rand(Sphere{2,T}) - s3 = rand(Sphere{3,T}) + s1 = rand(Sphere{1}) + s2 = rand(Sphere{2}) + s3 = rand(Sphere{3}) @test s1 isa Sphere @test s2 isa Sphere @test s3 isa Sphere @@ -676,18 +665,18 @@ @test embeddim(s2) == 2 @test embeddim(s3) == 3 - s = Sphere(P3(0, 0, 0), T(1)) - @test sprint(show, s) == "Sphere(center: (0.0, 0.0, 0.0), radius: 1.0)" + s = Sphere(point(0, 0, 0), T(1)) + @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), s) == """ - Sphere{3,Float32} - ├─ center: Point(0.0f0, 0.0f0, 0.0f0) - └─ radius: 1.0f0""" + Sphere + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + └─ radius: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), s) == """ - Sphere{3,Float64} - ├─ center: Point(0.0, 0.0, 0.0) - └─ radius: 1.0""" + Sphere + ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + └─ radius: 1.0 m""" end end @@ -695,195 +684,195 @@ e = Ellipsoid((T(3), T(2), T(1))) @test embeddim(e) == 3 @test paramdim(e) == 2 - @test coordtype(e) == T - @test radii(e) == (T(3), T(2), T(1)) - @test center(e) == P3(0, 0, 0) + @test Meshes.lentype(e) == ℳ + @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") + @test center(e) == point(0, 0, 0) @test isnothing(boundary(e)) - @test perimeter(e) == zero(T) + @test perimeter(e) == zero(ℳ) e = Ellipsoid((T(3), T(2), T(1))) @test sprint(show, e) == - "Ellipsoid(radii: (3.0, 2.0, 1.0), center: (0.0, 0.0, 0.0), rotation: UniformScaling{Bool}(true))" + "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" if T === Float32 @test sprint(show, MIME("text/plain"), e) == """ - Ellipsoid{3,Float32} - ├─ radii: (3.0f0, 2.0f0, 1.0f0) - ├─ center: Point(0.0f0, 0.0f0, 0.0f0) + Ellipsoid + ├─ radii: (3.0f0 m, 2.0f0 m, 1.0f0 m) + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) └─ rotation: UniformScaling{Bool}(true)""" else @test sprint(show, MIME("text/plain"), e) == """ - Ellipsoid{3,Float64} - ├─ radii: (3.0, 2.0, 1.0) - ├─ center: Point(0.0, 0.0, 0.0) + Ellipsoid + ├─ radii: (3.0 m, 2.0 m, 1.0 m) + ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) └─ rotation: UniformScaling{Bool}(true)""" end end @testset "Disk" begin - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @test embeddim(d) == 3 @test paramdim(d) == 2 - @test coordtype(d) == T + @test Meshes.lentype(d) == ℳ @test plane(d) == p - @test Meshes.center(d) == P3(0, 0, 0) - @test radius(d) == T(2) - @test normal(d) == V3(0, 0, 1) - @test measure(d) == T(π) * T(2)^2 + @test Meshes.center(d) == point(0, 0, 0) + @test radius(d) == T(2) * u"m" + @test normal(d) == vector(0, 0, 1) + @test measure(d) == T(π) * T(2)^2 * u"m^2" @test area(d) == measure(d) - @test P3(0, 0, 0) ∈ d - @test P3(0, 0, 1) ∉ d + @test point(0, 0, 0) ∈ d + @test point(0, 0, 1) ∉ d @test boundary(d) == Circle(p, T(2)) - d = rand(Disk{T}) + d = rand(Disk) @test d isa Disk @test embeddim(d) == 3 - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @test sprint(show, d) == - "Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0)" + "Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), d) == """ - Disk{3,Float32} - ├─ plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 2.0f0""" + Disk + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0f0 m""" else @test sprint(show, MIME("text/plain"), d) == """ - Disk{3,Float64} - ├─ plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 2.0""" + Disk + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0 m""" end end @testset "Circle" begin - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @test embeddim(c) == 3 @test paramdim(c) == 1 - @test coordtype(c) == T + @test Meshes.lentype(c) == ℳ @test plane(c) == p - @test Meshes.center(c) == P3(0, 0, 0) - @test radius(c) == T(2) - @test measure(c) == 2 * T(π) * T(2) + @test Meshes.center(c) == point(0, 0, 0) + @test radius(c) == T(2) * u"m" + @test measure(c) == 2 * T(π) * T(2) * u"m" @test length(c) == measure(c) - @test P3(2, 0, 0) ∈ c - @test P3(0, 2, 0) ∈ c - @test P3(0, 0, 0) ∉ c + @test point(2, 0, 0) ∈ c + @test point(0, 2, 0) ∈ c + @test point(0, 0, 0) ∉ c @test isnothing(boundary(c)) # 3D circumcircle - p1 = P3(0, 4, 0) - p2 = P3(0, -4, 0) - p3 = P3(0, 0, 4) + p1 = point(0, 4, 0) + p2 = point(0, -4, 0) + p3 = point(0, 0, 4) c = Circle(p1, p2, p3) @test p1 ∈ c @test p2 ∈ c @test p3 ∈ c # circle parametrization - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) - @test c(T(0)) ≈ P3(2, 0, 0) - @test c(T(0.25)) ≈ P3(0, 2, 0) - @test c(T(0.5)) ≈ P3(-2, 0, 0) - @test c(T(0.75)) ≈ P3(0, -2, 0) - @test c(T(1)) ≈ P3(2, 0, 0) + @test c(T(0)) ≈ point(2, 0, 0) + @test c(T(0.25)) ≈ point(0, 2, 0) + @test c(T(0.5)) ≈ point(-2, 0, 0) + @test c(T(0.75)) ≈ point(0, -2, 0) + @test c(T(1)) ≈ point(2, 0, 0) - c = rand(Circle{T}) + c = rand(Circle) @test c isa Circle @test embeddim(c) == 3 - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @test sprint(show, c) == - "Circle(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0)" + "Circle(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), c) == """ - Circle{3,Float32} - ├─ plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 2.0f0""" + Circle + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0f0 m""" else @test sprint(show, MIME("text/plain"), c) == """ - Circle{3,Float64} - ├─ plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 2.0""" + Circle + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0 m""" end end @testset "Cylinder" begin - c = Cylinder(Plane(P3(1, 2, 3), V3(0, 0, 1)), Plane(P3(4, 5, 6), V3(0, 0, 1)), T(5)) + c = Cylinder(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) @test embeddim(c) == 3 @test paramdim(c) == 3 - @test coordtype(c) == T - @test radius(c) == T(5) - @test bottom(c) == Plane(P3(1, 2, 3), V3(0, 0, 1)) - @test top(c) == Plane(P3(4, 5, 6), V3(0, 0, 1)) - @test axis(c) == Line(P3(1, 2, 3), P3(4, 5, 6)) + @test Meshes.lentype(c) == ℳ + @test radius(c) == T(5) * u"m" + @test bottom(c) == Plane(point(1, 2, 3), vector(0, 0, 1)) + @test top(c) == Plane(point(4, 5, 6), vector(0, 0, 1)) + @test axis(c) == Line(point(1, 2, 3), point(4, 5, 6)) @test !isright(c) - @test measure(c) == volume(c) ≈ T(5)^2 * pi * T(3) * sqrt(T(3)) - @test P3(1, 2, 3) ∈ c - @test P3(4, 5, 6) ∈ c - @test P3(0.99, 1.99, 2.99) ∉ c - @test P3(4.01, 5.01, 6.01) ∉ c + @test measure(c) == volume(c) ≈ T(5)^2 * pi * T(3) * sqrt(T(3)) * u"m^3" + @test point(1, 2, 3) ∈ c + @test point(4, 5, 6) ∈ c + @test point(0.99, 1.99, 2.99) ∉ c + @test point(4.01, 5.01, 6.01) ∉ c @test !Meshes.hasintersectingplanes(c) @test c(0, 0, 0) ≈ bottom(c)(0, 0) @test c(0, 0, 1) ≈ top(c)(0, 0) @test c(1, 0.25, 0.5) ≈ Point(T(4.330127018922193), T(10.330127018922191), T(4.5)) @test_throws DomainError c(1.1, 0, 0) - c = Cylinder(Plane(P3(0, 0, 0), V3(0, 0, 1)), Plane(P3(0, 0, 1), V3(1, 0, 1)), T(5)) + c = Cylinder(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) - c1 = Cylinder(P3(0, 0, 0), P3(0, 0, 1), T(1)) - c2 = Cylinder(P3(0, 0, 0), P3(0, 0, 1)) + c1 = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) + c2 = Cylinder(point(0, 0, 0), point(0, 0, 1)) c3 = Cylinder(T(1)) @test c1 == c2 == c3 @test c1 ≈ c2 ≈ c3 c = Cylinder(T(1)) - @test coordtype(c) == T + @test Meshes.lentype(c) == ℳ c = Cylinder(1) - @test coordtype(c) == Float64 - - c = Cylinder(P3(0, 0, 0), P3(0, 0, 1), T(1)) - @test radius(c) == T(1) - @test bottom(c) == Plane(P3(0, 0, 0), V3(0, 0, 1)) - @test top(c) == Plane(P3(0, 0, 1), V3(0, 0, 1)) - @test center(c) == P3(0.0, 0.0, 0.5) - @test centroid(c) == P3(0.0, 0.0, 0.5) - @test axis(c) == Line(P3(0, 0, 0), P3(0, 0, 1)) + @test Meshes.lentype(c) == Meshes.Met{Float64} + + c = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) + @test radius(c) == T(1) * u"m" + @test bottom(c) == Plane(point(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(point(0, 0, 1), vector(0, 0, 1)) + @test center(c) == point(0.0, 0.0, 0.5) + @test centroid(c) == point(0.0, 0.0, 0.5) + @test axis(c) == Line(point(0, 0, 0), point(0, 0, 1)) @test isright(c) - @test boundary(c) == CylinderSurface(P3(0, 0, 0), P3(0, 0, 1), T(1)) - @test measure(c) == volume(c) ≈ pi - @test P3(0, 0, 0) ∈ c - @test P3(0, 0, 1) ∈ c - @test P3(1, 0, 0) ∈ c - @test P3(0, 1, 0) ∈ c - @test P3(cosd(60), sind(60), 0.5) ∈ c - @test P3(0, 0, -0.001) ∉ c - @test P3(0, 0, 1.001) ∉ c - @test P3(1, 1, 1) ∉ c - - c = rand(Cylinder{T}) + @test boundary(c) == CylinderSurface(point(0, 0, 0), point(0, 0, 1), T(1)) + @test measure(c) == volume(c) ≈ T(π) * u"m^3" + @test point(0, 0, 0) ∈ c + @test point(0, 0, 1) ∈ c + @test point(1, 0, 0) ∈ c + @test point(0, 1, 0) ∈ c + @test point(cosd(60), sind(60), 0.5) ∈ c + @test point(0, 0, -0.001) ∉ c + @test point(0, 0, 1.001) ∉ c + @test point(1, 1, 1) ∉ c + + c = rand(Cylinder) @test c isa Cylinder @test embeddim(c) == 3 - c = Cylinder(P3(0, 0, 0), P3(0, 0, 1), T(1)) + c = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) @test sprint(show, c) == - "Cylinder(bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 1.0)" + "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), c) == """ - Cylinder{3,Float32} - ├─ bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - ├─ top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 1.0f0""" + Cylinder + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), c) == """ - Cylinder{3,Float64} - ├─ bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - ├─ top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 1.0""" + Cylinder + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0 m""" end end @@ -891,78 +880,78 @@ c = CylinderSurface(T(2)) @test embeddim(c) == 3 @test paramdim(c) == 2 - @test coordtype(c) == T - @test radius(c) == T(2) - @test bottom(c) == Plane(P3(0, 0, 0), V3(0, 0, 1)) - @test top(c) == Plane(P3(0, 0, 1), V3(0, 0, 1)) - @test center(c) == P3(0.0, 0.0, 0.5) - @test centroid(c) == P3(0.0, 0.0, 0.5) - @test axis(c) == Line(P3(0, 0, 0), P3(0, 0, 1)) + @test Meshes.lentype(c) == ℳ + @test radius(c) == T(2) * u"m" + @test bottom(c) == Plane(point(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(point(0, 0, 1), vector(0, 0, 1)) + @test center(c) == point(0.0, 0.0, 0.5) + @test centroid(c) == point(0.0, 0.0, 0.5) + @test axis(c) == Line(point(0, 0, 0), point(0, 0, 1)) @test isright(c) @test isnothing(boundary(c)) - @test measure(c) == area(c) ≈ 2 * T(2)^2 * pi + 2 * T(2) * pi + @test measure(c) == area(c) ≈ (2 * T(2)^2 * pi + 2 * T(2) * pi) * u"m^2" @test !Meshes.hasintersectingplanes(c) - c = CylinderSurface(Plane(P3(0, 0, 0), V3(0, 0, 1)), Plane(P3(0, 0, 1), V3(1, 0, 1)), T(5)) + c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) - c1 = CylinderSurface(P3(0, 0, 0), P3(0, 0, 1), T(1)) - c2 = CylinderSurface(P3(0, 0, 0), P3(0, 0, 1)) + c1 = CylinderSurface(point(0, 0, 0), point(0, 0, 1), T(1)) + c2 = CylinderSurface(point(0, 0, 0), point(0, 0, 1)) c3 = CylinderSurface(T(1)) @test c1 == c2 == c3 @test c1 ≈ c2 ≈ c3 - c = CylinderSurface(Plane(P3(1, 2, 3), V3(0, 0, 1)), Plane(P3(4, 5, 6), V3(0, 0, 1)), T(5)) - @test measure(c) == area(c) ≈ 2 * T(5)^2 * pi + 2 * T(5) * pi * sqrt(3 * T(3)^2) + c = CylinderSurface(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) + @test measure(c) == area(c) ≈ (2 * T(5)^2 * pi + 2 * T(5) * pi * sqrt(3 * T(3)^2)) * u"m^2" c = CylinderSurface(T(1)) - @test c(T(0), T(0)) ≈ P3(1, 0, 0) - @test c(T(0.5), T(0)) ≈ P3(-1, 0, 0) - @test c(T(0), T(1)) ≈ P3(1, 0, 1) - @test c(T(0.5), T(1)) ≈ P3(-1, 0, 1) + @test c(T(0), T(0)) ≈ point(1, 0, 0) + @test c(T(0.5), T(0)) ≈ point(-1, 0, 0) + @test c(T(0), T(1)) ≈ point(1, 0, 1) + @test c(T(0.5), T(1)) ≈ point(-1, 0, 1) c = CylinderSurface(1.0) - @test coordtype(c) == Float64 + @test Meshes.lentype(c) == Meshes.Met{Float64} c = CylinderSurface(1.0f0) - @test coordtype(c) == Float32 + @test Meshes.lentype(c) == Meshes.Met{Float32} c = CylinderSurface(1) - @test coordtype(c) == Float64 + @test Meshes.lentype(c) == Meshes.Met{Float64} - c = rand(CylinderSurface{T}) + c = rand(CylinderSurface) @test c isa CylinderSurface @test embeddim(c) == 3 c = CylinderSurface(T(1)) @test sprint(show, c) == - "CylinderSurface(bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 1.0)" + "CylinderSurface(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), c) == """ - CylinderSurface{3,Float32} - ├─ bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - ├─ top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 1.0f0""" + CylinderSurface + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), c) == """ - CylinderSurface{3,Float64} - ├─ bot: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - ├─ top: Plane(p: (0.0, 0.0, 1.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)) - └─ radius: 1.0""" + CylinderSurface + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0 m""" end end @testset "ParaboloidSurface" begin - p = ParaboloidSurface(P3(0, 0, 0), T(1), T(2)) + p = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) @test embeddim(p) == 3 @test paramdim(p) == 2 - @test coordtype(p) == T - @test focallength(p) == T(2) - @test radius(p) == T(1) - @test axis(p) == Line(P3(0, 0, 0), P3(0, 0, T(2))) - @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) - - p1 = ParaboloidSurface(P3(1, 2, 3), T(1), T(1)) - p2 = ParaboloidSurface(P3(1, 2, 3), T(1)) - p3 = ParaboloidSurface(P3(1, 2, 3)) + @test Meshes.lentype(p) == ℳ + @test focallength(p) == T(2) * u"m" + @test radius(p) == T(1) * u"m" + @test axis(p) == Line(point(0, 0, 0), point(0, 0, T(2))) + @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" + + p1 = ParaboloidSurface(point(1, 2, 3), T(1), T(1)) + p2 = ParaboloidSurface(point(1, 2, 3), T(1)) + p3 = ParaboloidSurface(point(1, 2, 3)) @test p1 == p2 == p3 @test p1 ≈ p2 ≈ p3 @@ -973,224 +962,225 @@ @test p1 ≈ p2 ≈ p3 p = ParaboloidSurface((1.0, 2.0, 3.0), 4.0, 5.0) - @test coordtype(p) == Float64 - @test radius(p) == 4.0 - @test focallength(p) == 5.0 - - p = ParaboloidSurface(P3(1, 5, 2), T(3), T(4)) - @test measure(p) == area(p) ≈ T(128π / 3 * (73√73 / 512 - 1)) - @test p(T(0), T(0)) ≈ P3(1, 5, 2) - @test p(T(1), T(0)) ≈ P3(4, 5, 2 + 3^2 / (4 * 4)) + @test Meshes.lentype(p) == Meshes.Met{Float64} + @test radius(p) == 4.0 * u"m" + @test focallength(p) == 5.0 * u"m" + + p = ParaboloidSurface(point(1, 5, 2), T(3), T(4)) + @test measure(p) == area(p) ≈ T(128π / 3 * (73√73 / 512 - 1)) * u"m^2" + @test p(T(0), T(0)) ≈ point(1, 5, 2) + @test p(T(1), T(0)) ≈ point(4, 5, 2 + 3^2 / (4 * 4)) @test_throws DomainError p(T(-0.1), T(0)) @test_throws DomainError p(T(1.1), T(0)) p = ParaboloidSurface() - @test coordtype(p) == Float64 - @test p(0.0, 0.0) ≈ Point3(0, 0, 0) - @test p(0.5, 0.0) ≈ Point3(0.5, 0, 0.5^2 / 4) - @test p(0.0, 0.5) ≈ Point3(0, 0, 0) - @test p(0.5, 0.5) ≈ Point3(-0.5, 0, 0.5^2 / 4) - - p = ParaboloidSurface(Point3(0, 0, 0)) - @test coordtype(p) == Float64 - p = ParaboloidSurface(Point3f(0, 0, 0)) - @test coordtype(p) == Float32 - - p = rand(ParaboloidSurface{T}) + @test Meshes.lentype(p) == Meshes.Met{Float64} + @test p(0.0, 0.0) ≈ Point(0, 0, 0) + @test p(0.5, 0.0) ≈ Point(0.5, 0, 0.5^2 / 4) + @test p(0.0, 0.5) ≈ Point(0, 0, 0) + @test p(0.5, 0.5) ≈ Point(-0.5, 0, 0.5^2 / 4) + + p = ParaboloidSurface(Point(0.0, 0.0, 0.0)) + @test Meshes.lentype(p) == Meshes.Met{Float64} + p = ParaboloidSurface(Point(0.0f0, 0.0f0, 0.0f0)) + @test Meshes.lentype(p) == Meshes.Met{Float32} + + p = rand(ParaboloidSurface) @test p isa ParaboloidSurface @test embeddim(p) == 3 - p = ParaboloidSurface(P3(0, 0, 0)) - @test sprint(show, p) == "ParaboloidSurface(apex: (0.0, 0.0, 0.0), radius: 1.0, focallength: 1.0)" + p = ParaboloidSurface(point(0, 0, 0), T(1), T(1)) + @test sprint(show, p) == + "ParaboloidSurface(apex: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m, focallength: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), p) == """ - ParaboloidSurface{3,Float32} - ├─ apex: Point(0.0f0, 0.0f0, 0.0f0) - ├─ radius: 1.0f0 - └─ focallength: 1.0f0""" + ParaboloidSurface + ├─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ radius: 1.0f0 m + └─ focallength: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), p) == """ - ParaboloidSurface{3,Float64} - ├─ apex: Point(0.0, 0.0, 0.0) - ├─ radius: 1.0 - └─ focallength: 1.0""" + ParaboloidSurface + ├─ apex: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ radius: 1.0 m + └─ focallength: 1.0 m""" end end @testset "Cone" begin - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = P3(0, 0, 1) + a = point(0, 0, 1) c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 - @test coordtype(c) == T + @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) - c = rand(Cone{T}) + c = rand(Cone) @test c isa Cone @test embeddim(c) == 3 - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = P3(0, 0, 1) + a = point(0, 0, 1) c = Cone(d, a) @test sprint(show, c) == - "Cone(base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0), apex: (0.0, 0.0, 1.0))" + "Cone(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), c) == """ - Cone{3,Float32} - ├─ base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0) - └─ apex: Point(0.0f0, 0.0f0, 1.0f0)""" + Cone + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), c) == """ - Cone{3,Float64} - ├─ base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0) - └─ apex: Point(0.0, 0.0, 1.0)""" + Cone + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end # cone: apex at (5,4,3); base center at (5,1,3) # halfangle: 30° -> radius: sqrt(3) # axis of the cone is parallel to y axis - p = Plane(P3(5, 1, 3), V3(0, 1, 0)) + p = Plane(point(5, 1, 3), vector(0, 1, 0)) d = Disk(p, sqrt(T(3))) - a = P3(5, 4, 3) + a = point(5, 4, 3) c = Cone(d, a) @test rad2deg(Meshes.halfangle(c)) ≈ T(30) - @test Meshes.height(c) ≈ T(3) - - @test P3(5, 1, 3) ∈ c - @test P3(5, 4, 3) ∈ c - @test P3(5, 1, 3 - sqrt(3)) ∈ c - @test P3(5, 1, 3 + sqrt(3)) ∈ c - @test P3(5 - sqrt(3), 1, 3) ∈ c - @test P3(5 + sqrt(3), 1, 3) ∈ c - @test P3(5, 2.5, 3) ∈ c - @test P3(5 + sqrt(3) / 2, 2.5, 3) ∈ c - @test P3(5 - sqrt(3) / 2, 2.5, 3) ∈ c - - @test P3(5, 0.9, 3) ∉ c - @test P3(5, 4.1, 3) ∉ c - @test P3(5, 1, 1) ∉ c - @test P3(5 + sqrt(3) + 0.01, 1, 3) ∉ c - @test P3(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c - @test P3(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c + @test Meshes.height(c) ≈ T(3) * u"m" + + @test point(5, 1, 3) ∈ c + @test point(5, 4, 3) ∈ c + @test point(5, 1, 3 - sqrt(3)) ∈ c + @test point(5, 1, 3 + sqrt(3)) ∈ c + @test point(5 - sqrt(3), 1, 3) ∈ c + @test point(5 + sqrt(3), 1, 3) ∈ c + @test point(5, 2.5, 3) ∈ c + @test point(5 + sqrt(3) / 2, 2.5, 3) ∈ c + @test point(5 - sqrt(3) / 2, 2.5, 3) ∈ c + + @test point(5, 0.9, 3) ∉ c + @test point(5, 4.1, 3) ∉ c + @test point(5, 1, 1) ∉ c + @test point(5 + sqrt(3) + 0.01, 1, 3) ∉ c + @test point(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c + @test point(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c end @testset "ConeSurface" begin - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = P3(0, 0, 1) + a = point(0, 0, 1) s = ConeSurface(d, a) @test embeddim(s) == 3 @test paramdim(s) == 2 - @test coordtype(s) == T + @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) - c = rand(ConeSurface{T}) + c = rand(ConeSurface) @test c isa ConeSurface @test embeddim(c) == 3 - p = Plane(P3(0, 0, 0), V3(0, 0, 1)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = P3(0, 0, 1) + a = point(0, 0, 1) s = ConeSurface(d, a) @test sprint(show, s) == - "ConeSurface(base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0), apex: (0.0, 0.0, 1.0))" + "ConeSurface(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), s) == """ - ConeSurface{3,Float32} - ├─ base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0) - └─ apex: Point(0.0f0, 0.0f0, 1.0f0)""" + ConeSurface + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" else @test sprint(show, MIME("text/plain"), s) == """ - ConeSurface{3,Float64} - ├─ base: Disk(plane: Plane(p: (0.0, 0.0, 0.0), u: (1.0, -0.0, -0.0), v: (-0.0, 1.0, -0.0)), radius: 2.0) - └─ apex: Point(0.0, 0.0, 1.0)""" + ConeSurface + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end end @testset "Frustum" begin - pb = Plane(P3(0, 0, 0), V3(0, 0, 1)) + pb = Plane(point(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(P3(0, 0, 10), V3(0, 0, 1)) + pt = Plane(point(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = Frustum(db, dt) @test embeddim(f) == 3 - @test coordtype(f) == T + @test Meshes.lentype(f) == ℳ @test boundary(f) == FrustumSurface(db, dt) @test_throws AssertionError Frustum(db, db) - f = rand(Frustum{T}) + f = rand(Frustum) @test f isa Frustum f = Frustum(db, dt) - @test P3(0, 0, 0) ∈ f - @test P3(0, 0, 10) ∈ f - @test P3(1, 0, 0) ∈ f - @test P3(2, 0, 10) ∈ f - @test P3(1, 0, 5) ∈ f + @test point(0, 0, 0) ∈ f + @test point(0, 0, 10) ∈ f + @test point(1, 0, 0) ∈ f + @test point(2, 0, 10) ∈ f + @test point(1, 0, 5) ∈ f - @test P3(1, 1, 0) ∉ f - @test P3(2, 2, 10) ∉ f - @test P3(0, 0, -0.01) ∉ f - @test P3(0, 0, 10.01) ∉ f + @test point(1, 1, 0) ∉ f + @test point(2, 2, 10) ∉ f + @test point(0, 0, -0.01) ∉ f + @test point(0, 0, 10.01) ∉ f # reverse order, when top is larger than bottom # the frustum is the same geometry f = Frustum(dt, db) - @test P3(0, 0, 0) ∈ f - @test P3(0, 0, 10) ∈ f - @test P3(1, 0, 0) ∈ f - @test P3(2, 0, 10) ∈ f - @test P3(1, 0, 5) ∈ f - - @test P3(1, 1, 0) ∉ f - @test P3(2, 2, 10) ∉ f - @test P3(0, 0, -0.01) ∉ f - @test P3(0, 0, 10.01) ∉ f + @test point(0, 0, 0) ∈ f + @test point(0, 0, 10) ∈ f + @test point(1, 0, 0) ∈ f + @test point(2, 0, 10) ∈ f + @test point(1, 0, 5) ∈ f + + @test point(1, 1, 0) ∉ f + @test point(2, 2, 10) ∉ f + @test point(0, 0, -0.01) ∉ f + @test point(0, 0, 10.01) ∉ f end @testset "FrustumSurface" begin - pb = Plane(P3(0, 0, 0), V3(0, 0, 1)) + pb = Plane(point(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(P3(0, 0, 10), V3(0, 0, 1)) + pt = Plane(point(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = FrustumSurface(db, dt) @test embeddim(f) == 3 @test paramdim(f) == 2 - @test coordtype(f) == T + @test Meshes.lentype(f) == ℳ @test isnothing(boundary(f)) @test_throws AssertionError FrustumSurface(db, db) - f = rand(FrustumSurface{T}) + f = rand(FrustumSurface) @test f isa FrustumSurface end @testset "Torus" begin t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) - @test P3(1, 1, -1) ∈ t - @test P3(1, 1, 1) ∉ t + @test point(1, 1, -1) ∈ t + @test point(1, 1, 1) ∉ t @test paramdim(t) == 2 - @test Meshes.center(t) == P3(1, 1, 1) - @test normal(t) == V3(1, 0, 0) - @test radii(t) == (T(2), T(1)) - @test axis(t) == Line(P3(1, 1, 1), P3(2, 1, 1)) - @test measure(t) ≈ 8 * T(π)^2 + @test Meshes.center(t) == point(1, 1, 1) + @test normal(t) == vector(1, 0, 0) + @test radii(t) == (T(2) * u"m", T(1) * u"m") + @test axis(t) == Line(point(1, 1, 1), point(2, 1, 1)) + @test measure(t) ≈ 8 * T(π)^2 * u"m^2" @test_throws ArgumentError length(t) @test_throws ArgumentError volume(t) # torus passing through three points - p₁ = P3(0, 0, 0) - p₂ = P3(1, 2, 3) - p₃ = P3(3, 2, 1) + p₁ = point(0, 0, 0) + p₂ = point(1, 2, 3) + p₃ = point(3, 2, 1) t = Torus(p₁, p₂, p₃, T(1)) c = center(t) R, r = radii(t) - @test r == 1 + @test r == T(1) * u"m" @test norm(p₁ - c) ≈ R @test norm(p₂ - c) ≈ R @test norm(p₃ - c) ≈ R @@ -1205,28 +1195,29 @@ q = Torus(c₁, c₂, c₃, 1) @test q == t - t = rand(Torus{T}) + t = rand(Torus) @test t isa Torus @test embeddim(t) == 3 - @test coordtype(t) == T + @test Meshes.lentype(t) == Meshes.Met{Float64} @test isnothing(boundary(t)) - t = Torus(P3(1, 1, 1), V3(1, 0, 0), 2, 1) - @test sprint(show, t) == "Torus(center: (1.0, 1.0, 1.0), normal: (1.0, 0.0, 0.0), major: 2.0, minor: 1.0)" + t = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + @test sprint(show, t) == + "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ - Torus{3,Float32} - ├─ center: Point(1.0f0, 1.0f0, 1.0f0) - ├─ normal: Vec(1.0f0, 0.0f0, 0.0f0) - ├─ major: 2.0f0 - └─ minor: 1.0f0""" + Torus + ├─ center: Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) + ├─ normal: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) + ├─ major: 2.0f0 m + └─ minor: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), t) == """ - Torus{3,Float64} - ├─ center: Point(1.0, 1.0, 1.0) - ├─ normal: Vec(1.0, 0.0, 0.0) - ├─ major: 2.0 - └─ minor: 1.0""" + Torus + ├─ center: Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) + ├─ normal: Vec(1.0 m, 0.0 m, 0.0 m) + ├─ major: 2.0 m + └─ minor: 1.0 m""" end end end diff --git a/test/refinement.jl b/test/refinement.jl index deec9956d..52a3f36b5 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -1,6 +1,6 @@ @testset "Refinement" begin @testset "TriRefinement" begin - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) ref1 = refine(grid, TriRefinement()) ref2 = refine(ref1, TriRefinement()) @@ -14,7 +14,7 @@ end @testset "QuadRefinement" begin - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, QuadRefinement()) @@ -32,8 +32,8 @@ @testset "RegularRefinement" begin # 2D grids - grid = CartesianGrid(P2(0, 0), P2(10, 10), dims=(10, 10)) - tgrid = CartesianGrid(P2(0, 0), P2(10, 10), dims=(20, 20)) + grid = CartesianGrid(point(0, 0), point(10, 10), dims=(10, 10)) + tgrid = CartesianGrid(point(0, 0), point(10, 10), dims=(20, 20)) @test refine(grid, RegularRefinement(2)) == tgrid rgrid = convert(RectilinearGrid, grid) trgrid = convert(RectilinearGrid, tgrid) @@ -43,7 +43,7 @@ @test refine(sgrid, RegularRefinement(2)) == tsgrid # 3D grids - grid = CartesianGrid{T}(3, 3, 3) + grid = cartgrid(3, 3, 3) tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(6, 6, 6)) @test refine(grid, RegularRefinement(2)) == tgrid rgrid = convert(RectilinearGrid, grid) @@ -55,7 +55,7 @@ end @testset "CatmullClark" begin - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -70,7 +70,7 @@ @test_reference "data/catmullclark-1-$T.png" fig end - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -85,7 +85,7 @@ @test_reference "data/catmullclark-2-$T.png" fig end - points = P3[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)] + points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) connec = connect.([(1, 4, 3, 2), (5, 6, 7, 8), (1, 2, 6, 5), (3, 4, 8, 7), (1, 5, 8, 4), (2, 3, 7, 6)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -102,7 +102,7 @@ end @testset "TriSubdivision" begin - points = P3[(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)] + points = point.([(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)]) connec = connect.([(1, 2, 3), (3, 2, 4), (4, 2, 1), (1, 3, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, TriSubdivision()) diff --git a/test/runtests.jl b/test/runtests.jl index 28bd3c600..aa43c6cfc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -59,14 +59,33 @@ end # helper function to read *.ply files containing meshes function readply(T, fname) ply = load_ply(fname) - x = ply["vertex"]["x"] - y = ply["vertex"]["y"] - z = ply["vertex"]["z"] - points = Point{3,T}.(x, y, z) + x = T.(ply["vertex"]["x"]) + y = T.(ply["vertex"]["y"]) + z = T.(ply["vertex"]["z"]) + points = Point.(x, y, z) connec = [connect(Tuple(c .+ 1)) for c in ply["face"]["vertex_indices"]] SimpleMesh(points, connec) end +point(coords...) = point(coords) +point(coords::Tuple) = Point(T.(coords)) + +vector(coords...) = vector(coords) +vector(coords::Tuple) = Vec(T.(coords)) + +cartgrid(dims...) = cartgrid(dims) +function cartgrid(dims::Dims{Dim}) where {Dim} + origin = ntuple(i -> T(0.0), Dim) + spacing = ntuple(i -> T(1.0), Dim) + offset = ntuple(i -> 1, Dim) + CartesianGrid(dims, origin, spacing, offset) +end + +randpoint1(n) = randpoint(1, n) +randpoint2(n) = randpoint(2, n) +randpoint3(n) = randpoint(3, n) +randpoint(Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] + # dummy definitions include("dummy.jl") @@ -119,8 +138,7 @@ testfiles = [ # RUN TESTS WITH SINGLE PRECISION # -------------------------------- T = Float32 -P1, P2, P3 = Point{1,T}, Point{2,T}, Point{3,T} -V1, V2, V3 = Vec{1,T}, Vec{2,T}, Vec{3,T} +ℳ = Meshes.Met{T} @testset "Meshes.jl ($T)" begin for testfile in testfiles println("Testing $testfile...") @@ -132,8 +150,7 @@ end # RUN TESTS WITH DOUBLE PRECISION # -------------------------------- T = Float64 -P1, P2, P3 = Point{1,T}, Point{2,T}, Point{3,T} -V1, V2, V3 = Vec{1,T}, Vec{2,T}, Vec{3,T} +ℳ = Meshes.Met{T} @testset "Meshes.jl ($T)" begin for testfile in testfiles println("Testing $testfile...") diff --git a/test/sampling.jl b/test/sampling.jl index 0337d181b..016bd9456 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -1,212 +1,223 @@ @testset "Sampling" begin @testset "UniformSampling" begin rng = StableRNG(123) - d = CartesianGrid{T}(100, 100) + d = cartgrid(100, 100) s = sample(rng, d, UniformSampling(100)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # availability of option ordered s = sample(rng, d, UniformSampling(100, ordered=true)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") end @testset "WeightedSampling" begin # uniform weights => uniform sampler rng = StableRNG(123) - d = CartesianGrid{T}(100, 100) + d = cartgrid(100, 100) s = sample(rng, d, WeightedSampling(100)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # availability of option ordered s = sample(rng, d, WeightedSampling(100, ordered=true)) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # utility method s = sample(rng, d, 100, ordered=true) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") s = sample(rng, d, 100, fill(1, 10000), ordered=true) μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 - @test isapprox(μ, T[50.0, 50.0], atol=T(10)) + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") end @testset "BallSampling" begin - d = CartesianGrid{T}(100, 100) + d = cartgrid(100, 100) s = sample(d, BallSampling(T(10))) n = nelements(s) x = coordinates(centroid(s, 1)) y = coordinates(centroid(s, 17)) @test n < 100 - @test sqrt(sum((x - y) .^ 2)) ≥ T(10) + @test sqrt(sum((x - y) .^ 2)) ≥ T(10) * u"m" - d = CartesianGrid{T}(100, 100) + d = cartgrid(100, 100) s = sample(d, BallSampling(T(20))) n = nelements(s) x = coordinates(centroid(s, 1)) y = coordinates(centroid(s, 17)) @test n < 50 - @test sqrt(sum((x - y) .^ 2)) ≥ T(20) + @test sqrt(sum((x - y) .^ 2)) ≥ T(20) * u"m" end @testset "BlockSampling" begin - g = CartesianGrid{T}(100, 100) + g = cartgrid(100, 100) s = sample(g, BlockSampling(T(10))) @test nelements(s) == 100 x = coordinates.(centroid.(s)) D = pairwise(Euclidean(), x) d = [D[i, j] for i in 1:length(x) for j in 1:(i - 1)] - @test all(≥(T(10)), d) + @test all(≥(T(10) * u"m"), d) end @testset "RegularSampling" begin - b = Box(P2(0, 0), P2(2, 2)) + b = Box(point(0, 0), point(2, 2)) ps = sample(b, RegularSampling(3)) - @test collect(ps) == P2[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] + @test collect(ps) == point.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]) ps = sample(b, RegularSampling(2, 3)) - @test collect(ps) == P2[(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)] + @test collect(ps) == point.([(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)]) - b = BezierCurve([P2(0, 0), P2(1, 0), P2(1, 1)]) + b = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) ps = sample(b, RegularSampling(4)) - ts = P2[(0.0, 0.0), (0.5555555555555556, 0.1111111111111111), (0.8888888888888888, 0.4444444444444444), (1.0, 1.0)] + ts = + point.([ + (0.0, 0.0), + (0.5555555555555556, 0.1111111111111111), + (0.8888888888888888, 0.4444444444444444), + (1.0, 1.0) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - s = Sphere(P2(0, 0), T(2)) + s = Sphere(point(0, 0), T(2)) ps = sample(s, RegularSampling(4)) - ts = P2[(2, 0), (0, 2), (-2, 0), (0, -2)] + ts = point.([(2, 0), (0, 2), (-2, 0), (0, -2)]) for (p, t) in zip(ps, ts) @test p ≈ t end - s = Sphere(P3(0, 0, 0), T(2)) + s = Sphere(point(0, 0, 0), T(2)) ps = sample(s, RegularSampling(2, 2)) - ts = P3[ - (1.7320508075688772, 0.0, 1.0), - (1.7320508075688772, 0.0, -1.0), - (-1.7320508075688772, 0.0, 1.0), - (-1.7320508075688772, 0.0, -1.0), - (0.0, 0.0, 2.0), - (0.0, 0.0, -2.0) - ] + ts = + point.([ + (1.7320508075688772, 0.0, 1.0), + (1.7320508075688772, 0.0, -1.0), + (-1.7320508075688772, 0.0, 1.0), + (-1.7320508075688772, 0.0, -1.0), + (0.0, 0.0, 2.0), + (0.0, 0.0, -2.0) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - e = Ellipsoid((T(3), T(2), T(1)), P3(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) + e = Ellipsoid((T(3), T(2), T(1)), point(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) ps = sample(e, RegularSampling(2, 2)) - ts = P3[ - (2.725814800973295, 2.225814800973295, -0.5871173070873834), - (1.872261410380021, 2.372261410380021, -1.0871173070873832), - (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), - (-0.725814800973295, -0.22581480097329454, 2.587117307087383), - (1.8535533905932737, 0.8535533905932737, 1.5), - (0.14644660940672627, 1.1464466094067263, 0.4999999999999999) - ] + ts = + point.([ + (2.725814800973295, 2.225814800973295, -0.5871173070873834), + (1.872261410380021, 2.372261410380021, -1.0871173070873832), + (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), + (-0.725814800973295, -0.22581480097329454, 2.587117307087383), + (1.8535533905932737, 0.8535533905932737, 1.5), + (0.14644660940672627, 1.1464466094067263, 0.4999999999999999) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - b = Ball(P2(0, 0), T(2)) + b = Ball(point(0, 0), T(2)) ps = sample(b, RegularSampling(3, 4)) @test all(∈(b), ps) - ts = P2[ - (0.6666666666666666, 0.0), - (1.3333333333333333, 0.0), - (2.0, 0.0), - (0.0, 0.6666666666666666), - (0.0, 1.3333333333333333), - (0.0, 2.0), - (-0.6666666666666666, 0.0), - (-1.3333333333333333, 0.0), - (-2.0, 0.0), - (0.0, -0.6666666666666666), - (0.0, -1.3333333333333333), - (0.0, -2.0), - (0.0, 0.0) - ] + ts = + point.([ + (0.6666666666666666, 0.0), + (1.3333333333333333, 0.0), + (2.0, 0.0), + (0.0, 0.6666666666666666), + (0.0, 1.3333333333333333), + (0.0, 2.0), + (-0.6666666666666666, 0.0), + (-1.3333333333333333, 0.0), + (-2.0, 0.0), + (0.0, -0.6666666666666666), + (0.0, -1.3333333333333333), + (0.0, -2.0), + (0.0, 0.0) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - b = Ball(P2(10, 10), T(2)) + b = Ball(point(10, 10), T(2)) ps = sample(b, RegularSampling(4, 3)) @test all(∈(b), ps) - ts = P2[ - (10.5, 10.0), - (11.0, 10.0), - (11.5, 10.0), - (12.0, 10.0), - (9.75, 10.433012701892219), - (9.5, 10.86602540378444), - (9.25, 11.299038105676658), - (9.0, 11.732050807568877), - (9.75, 9.566987298107781), - (9.5, 9.13397459621556), - (9.25, 8.700961894323342), - (9.0, 8.267949192431121), - (10.0, 10.0) - ] + ts = + point.([ + (10.5, 10.0), + (11.0, 10.0), + (11.5, 10.0), + (12.0, 10.0), + (9.75, 10.433012701892219), + (9.5, 10.86602540378444), + (9.25, 11.299038105676658), + (9.0, 11.732050807568877), + (9.75, 9.566987298107781), + (9.5, 9.13397459621556), + (9.25, 8.700961894323342), + (9.0, 8.267949192431121), + (10.0, 10.0) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - b = Ball(P3(0, 0, 0), T(2)) + b = Ball(point(0, 0, 0), T(2)) ps = sample(b, RegularSampling(3, 2, 3)) @test all(∈(b), ps) - ts = P3[ - (0.5773502691896257, 0.0, 0.3333333333333333), - (1.1547005383792515, 0.0, 0.6666666666666666), - (1.7320508075688772, 0.0, 1.0), - (0.5773502691896256, 0.0, -0.3333333333333335), - (1.1547005383792512, 0.0, -0.666666666666667), - (1.732050807568877, 0.0, -1.0000000000000004), - (-0.288675134594813, 0.4999999999999999, 0.3333333333333333), - (-0.577350269189626, 0.9999999999999998, 0.6666666666666666), - (-0.8660254037844389, 1.4999999999999996, 1.0), - (-0.2886751345948129, 0.4999999999999998, -0.3333333333333335), - (-0.5773502691896258, 0.9999999999999996, -0.666666666666667), - (-0.8660254037844388, 1.4999999999999993, -1.0000000000000004), - (-0.28867513459481264, -0.5000000000000001, 0.3333333333333333), - (-0.5773502691896253, -1.0000000000000002, 0.6666666666666666), - (-0.8660254037844379, -1.5000000000000004, 1.0), - (-0.2886751345948126, -0.5, -0.3333333333333335), - (-0.5773502691896252, -1.0, -0.666666666666667), - (-0.8660254037844378, -1.5000000000000002, -1.0000000000000004), - (0.0, 0.0, 0.0) - ] + ts = + point.([ + (0.5773502691896257, 0.0, 0.3333333333333333), + (1.1547005383792515, 0.0, 0.6666666666666666), + (1.7320508075688772, 0.0, 1.0), + (0.5773502691896256, 0.0, -0.3333333333333335), + (1.1547005383792512, 0.0, -0.666666666666667), + (1.732050807568877, 0.0, -1.0000000000000004), + (-0.288675134594813, 0.4999999999999999, 0.3333333333333333), + (-0.577350269189626, 0.9999999999999998, 0.6666666666666666), + (-0.8660254037844389, 1.4999999999999996, 1.0), + (-0.2886751345948129, 0.4999999999999998, -0.3333333333333335), + (-0.5773502691896258, 0.9999999999999996, -0.666666666666667), + (-0.8660254037844388, 1.4999999999999993, -1.0000000000000004), + (-0.28867513459481264, -0.5000000000000001, 0.3333333333333333), + (-0.5773502691896253, -1.0000000000000002, 0.6666666666666666), + (-0.8660254037844379, -1.5000000000000004, 1.0), + (-0.2886751345948126, -0.5, -0.3333333333333335), + (-0.5773502691896252, -1.0, -0.666666666666667), + (-0.8660254037844378, -1.5000000000000002, -1.0000000000000004), + (0.0, 0.0, 0.0) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - b = Ball(P3(10, 10, 10), T(2)) + b = Ball(point(10, 10, 10), T(2)) ps = sample(b, RegularSampling(3, 2, 3)) @test all(∈(b), ps) # cylinder surface with parallel planes - c = CylinderSurface(Plane(P3(0, 0, 0), V3(0, 0, 1)), Plane(P3(0, 0, 1), V3(0, 0, 1)), T(1)) + c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = coordinates.(ps) xs = getindex.(cs, 1) ys = getindex.(cs, 2) zs = getindex.(cs, 3) @test length(cs) == 200 + 2 - @test all(T(-1) ≤ x ≤ T(1) for x in xs) - @test all(T(-1) ≤ y ≤ T(1) for y in ys) - @test all(T(0) ≤ z ≤ T(1) for z in zs) + @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) + @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) + @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) # cylinder surface with parallel shifted planes - c = CylinderSurface(Plane(P3(0, 0, 0), V3(0, 0, 1)), Plane(P3(1, 1, 1), V3(0, 0, 1)), T(1)) + c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = coordinates.(ps) xs = getindex.(cs, 1) @@ -215,29 +226,39 @@ @test length(cs) == 200 + 2 # cylinder surface with non-parallel planes - c = CylinderSurface(Plane(P3(0, 0, 0), V3(1, 0, 1)), Plane(P3(1, 1, 1), V3(0, 1, 1)), T(1)) + c = CylinderSurface(Plane(point(0, 0, 0), vector(1, 0, 1)), Plane(point(1, 1, 1), vector(0, 1, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = coordinates.(ps) @test length(cs) == 200 + 2 - s = Segment(P2(0, 0), P2(1, 1)) + s = Segment(point(0, 0), point(1, 1)) ps = sample(s, RegularSampling(2)) - @test collect(ps) == P2[(0, 0), (1, 1)] + @test collect(ps) == point.([(0, 0), (1, 1)]) ps = sample(s, RegularSampling(3)) - @test collect(ps) == P2[(0, 0), (0.5, 0.5), (1, 1)] + @test collect(ps) == point.([(0, 0), (0.5, 0.5), (1, 1)]) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) ps = sample(q, RegularSampling(2, 2)) - @test collect(ps) == P2[(0, 0), (1, 0), (0, 1), (1, 1)] + @test collect(ps) == point.([(0, 0), (1, 0), (0, 1), (1, 1)]) ps = sample(q, RegularSampling(3, 3)) - @test collect(ps) == P2[(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)] - - h = - Hexahedron(P3(0, 0, 0), P3(1, 0, 0), P3(1, 1, 0), P3(0, 1, 0), P3(0, 0, 1), P3(1, 0, 1), P3(1, 1, 1), P3(0, 1, 1)) + @test collect(ps) == point.([(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)]) + + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) ps = sample(h, RegularSampling(2, 2, 2)) - @test collect(ps) == P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)] + @test collect(ps) == + point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) ps = sample(h, RegularSampling(3, 2, 2)) - @test collect(ps) == P3[ + @test collect(ps) == + point.([ (0, 0, 0), (0.5, 0, 0), (1, 0, 0), @@ -250,115 +271,119 @@ (0, 1, 1), (0.5, 1, 1), (1, 1, 1) - ] + ]) - torus = Torus(P3(0, 0, 0), V3(1, 0, 0), T(2), T(1)) + torus = Torus(point(0, 0, 0), vector(1, 0, 0), T(2), T(1)) ps = sample(torus, RegularSampling(3, 3)) - ts = P3[ - (0, 0, -3), - (-sqrt(3) / 2, 0, -1.5), - (sqrt(3) / 2, 0, -1.5), - (0, 3sqrt(3) / 2, 1.5), - (-sqrt(3) / 2, 3sqrt(3) / 4, 0.75), - (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), - (0, -3sqrt(3) / 2, 1.5), - (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75), - (sqrt(3) / 2, -3sqrt(3) / 4, 0.75) - ] + ts = + point.([ + (0, 0, -3), + (-sqrt(3) / 2, 0, -1.5), + (sqrt(3) / 2, 0, -1.5), + (0, 3sqrt(3) / 2, 1.5), + (-sqrt(3) / 2, 3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), + (0, -3sqrt(3) / 2, 1.5), + (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, -3sqrt(3) / 4, 0.75) + ]) for (p, t) in zip(ps, ts) @test p ≈ t end - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) points = sample(grid, RegularSampling(100, 200)) @test length(collect(points)) == 20000 end @testset "HomogeneousSampling" begin - s = Segment(P2(0, 0), P2(1, 0)) + s = Segment(point(0, 0), point(1, 0)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa P2 - @test all(0 ≤ coords[1] ≤ 1 for coords in coordinates.(ps)) - @test all(coords[2] == 0 for coords in coordinates.(ps)) + @test first(ps) isa Point{2} + @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in coordinates.(ps)) + @test all(coords[2] == zero(ℳ) for coords in coordinates.(ps)) - s = Segment(P2(0, 0), P2(0, 1)) + s = Segment(point(0, 0), point(0, 1)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa P2 - @test all(coords[1] == 0 for coords in coordinates.(ps)) - @test all(0 ≤ coords[2] ≤ 1 for coords in coordinates.(ps)) + @test first(ps) isa Point{2} + @test all(coords[1] == zero(ℳ) for coords in coordinates.(ps)) + @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in coordinates.(ps)) - s = Segment(P2(0, 0), P2(1, 1)) + s = Segment(point(0, 0), point(1, 1)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa P2 - @test all(0 ≤ coords[1] == coords[2] ≤ 1 for coords in coordinates.(ps)) + @test first(ps) isa Point{2} + @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in coordinates.(ps)) - c = Rope(P2(0, 0), P2(1, 0), P2(0, 1), P2(1, 1)) + c = Rope(point(0, 0), point(1, 0), point(0, 1), point(1, 1)) ps = sample(c, HomogeneousSampling(100)) - @test first(ps) isa P2 - @test all(coords[1] + coords[2] == 1 || (0 ≤ coords[1] ≤ 1 && coords[2] ∈ [0, 1]) for coords in coordinates.(ps)) + @test first(ps) isa Point{2} + @test all( + coords[1] + coords[2] == oneunit(ℳ) || (zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) && coords[2] ∈ [zero(ℳ), oneunit(ℳ)]) for + coords in coordinates.(ps) + ) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) ps = sample(t, HomogeneousSampling(100)) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(t), ps) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) ps = sample(q, HomogeneousSampling(100)) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(q), ps) - b = Ball(P2(10, 10), T(3)) + b = Ball(point(10, 10), T(3)) ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(b), ps) - b = Ball(P3(10, 10, 10), T(10)) + b = Ball(point(10, 10, 10), T(10)) ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa P3 + @test first(ps) isa Point{3} @test all(∈(b), ps) - poly1 = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - poly2 = PolyArea(P2[(1, 1), (2, 1), (2, 2), (1, 2)]) + poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, HomogeneousSampling(100)) - @test all(p -> (P2(0, 0) ⪯ p ⪯ P2(1, 1)) || (P2(1, 1) ⪯ p ⪯ P2(2, 2)), ps) + @test all(p -> (point(0, 0) ⪯ p ⪯ point(1, 1)) || (point(1, 1) ⪯ p ⪯ point(2, 2)), ps) - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) mesh = SimpleMesh(points, connec) ps = sample(mesh, HomogeneousSampling(400)) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(mesh), ps) ps = sample(mesh, HomogeneousSampling(400, 1:nelements(mesh))) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(mesh), ps) end @testset "MinDistanceSampling" begin - poly1 = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) - poly2 = PolyArea(P2[(1, 1), (2, 1), (2, 2), (1, 2)]) + poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, MinDistanceSampling(0.1)) - @test all(p -> (P2(0, 0) ⪯ p ⪯ P2(1, 1)) || (P2(1, 1) ⪯ p ⪯ P2(2, 2)), ps) + @test all(p -> (point(0, 0) ⪯ p ⪯ point(1, 1)) || (point(1, 1) ⪯ p ⪯ point(2, 2)), ps) - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) mesh = SimpleMesh(points, connec) ps = sample(mesh, MinDistanceSampling(0.2)) n = length(ps) - @test first(ps) isa P2 + @test first(ps) isa Point{2} @test all(∈(mesh), ps) - @test all(norm(ps[i] - ps[j]) ≥ 0.2 for i in 1:n for j in (i + 1):n) + @test all(norm(ps[i] - ps[j]) ≥ T(0.2) * u"m" for i in 1:n for j in (i + 1):n) # geometries with almost zero measure # can still be sampled (at least one point) - poly = PolyArea(P2[(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)]) + poly = PolyArea(point.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) ps = sample(poly, MinDistanceSampling(3.2423333333753135e-5)) @test length(ps) > 0 end @testset "RNGs" begin - dom = CartesianGrid{T}(100, 100) + dom = cartgrid(100, 100) for method in [UniformSampling(100), WeightedSampling(100), BallSampling(T(10))] rng = StableRNG(2021) s1 = sample(rng, dom, method) @@ -381,20 +406,20 @@ method = RegularSampling(10) for geom in [ - Box(P2(0, 0), P2(2, 2)) - Sphere(P2(0, 0), T(2)) - Ball(P2(0, 0), T(2)) - Segment(P2(0, 0), P2(1, 1)) - Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + Box(point(0, 0), point(2, 2)) + Sphere(point(0, 0), T(2)) + Ball(point(0, 0), T(2)) + Segment(point(0, 0), point(1, 1)) + Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) Hexahedron( - P3(0, 0, 0), - P3(1, 0, 0), - P3(1, 1, 0), - P3(0, 1, 0), - P3(0, 0, 1), - P3(1, 0, 1), - P3(1, 1, 1), - P3(0, 1, 1) + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) ) ] rng = StableRNG(2021) diff --git a/test/sets.jl b/test/sets.jl index 0af0f7a9d..e70ce488f 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -1,13 +1,13 @@ @testset "Sets" begin @testset "GeometrySet" begin - s = Segment(P2(0, 0), P2(1, 1)) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - p = PolyArea(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + s = Segment(point(0, 0), point(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + p = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) gset = GeometrySet([s, t, p]) - @test [centroid(gset, i) for i in 1:3] == P2[(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)] + @test [centroid(gset, i) for i in 1:3] == point.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) - s = Segment(P2(0, 0), P2(1, 1)) - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + s = Segment(point(0, 0), point(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) geoms = [s, t] gset1 = GeometrySet(geoms) gset2 = GeometrySet(g for g in geoms) @@ -17,14 +17,14 @@ # make sure that eltype is inferred properly # https://github.com/JuliaGeometry/Meshes.jl/issues/643 geoms = Vector{Segment}() - push!(geoms, Segment(P2(0, 0), P2(1, 0))) - push!(geoms, Segment(P2(1, 0), P2(1, 1))) - push!(geoms, Segment(P2(1, 1), P2(0, 0))) + push!(geoms, Segment(point(0, 0), point(1, 0))) + push!(geoms, Segment(point(1, 0), point(1, 1))) + push!(geoms, Segment(point(1, 1), point(0, 0))) gset = GeometrySet(geoms) - @test eltype(gset) <: Segment{2,T} + @test eltype(gset) <: Segment{2} # conversion - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) gset = convert(GeometrySet, grid) @test gset isa GeometrySet @test nelements(gset) == 100 @@ -32,67 +32,67 @@ end @testset "PointSet" begin - pset = PointSet(rand(P1, 100)) + pset = PointSet(randpoint1(100)) @test embeddim(pset) == 1 - @test coordtype(pset) == T + @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: P1 + @test eltype(pset) <: Point{1} - pset = PointSet(rand(P2, 100)) + pset = PointSet(randpoint2(100)) @test embeddim(pset) == 2 - @test coordtype(pset) == T + @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: P2 + @test eltype(pset) <: Point{2} - pset = PointSet(rand(P3, 100)) + pset = PointSet(randpoint3(100)) @test embeddim(pset) == 3 - @test coordtype(pset) == T + @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: P3 + @test eltype(pset) <: Point{3} - pset1 = PointSet([P3(1, 2, 3), P3(4, 5, 6)]) - pset2 = PointSet(P3(1, 2, 3), P3(4, 5, 6)) + pset1 = PointSet([point(1, 2, 3), point(4, 5, 6)]) + pset2 = PointSet(point(1, 2, 3), point(4, 5, 6)) pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) pset5 = PointSet(T[1 4; 2 5; 3 6]) @test pset1 == pset2 == pset3 == pset4 == pset5 for pset in [pset1, pset2, pset3, pset4, pset5] @test embeddim(pset) == 3 - @test coordtype(pset) == T + @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 2 - @test pset[1] == P3(1, 2, 3) - @test pset[2] == P3(4, 5, 6) + @test pset[1] == point(1, 2, 3) + @test pset[2] == point(4, 5, 6) end - pset = PointSet(P2[(0, 0), (1, 0), (0, 1)]) - @test centroid(pset) == P2(1 / 3, 1 / 3) + pset = PointSet(point.([(0, 0), (1, 0), (0, 1)])) + @test centroid(pset) == point(1 / 3, 1 / 3) - pset = PointSet(P2[(1, 0), (0, 1)]) + pset = PointSet(point.([(1, 0), (0, 1)])) @test nelements(pset) == 2 - @test centroid(pset, 1) == P2(1, 0) - @test centroid(pset, 2) == P2(0, 1) + @test centroid(pset, 1) == point(1, 0) + @test centroid(pset, 2) == point(0, 1) - pset = PointSet(P2[(0, 0), (1, 0), (0, 1)]) - @test measure(pset) == zero(T) + pset = PointSet(point.([(0, 0), (1, 0), (0, 1)])) + @test measure(pset) == zero(T) * u"m" # constructor with iterator - points = P2[(1, 0), (0, 1)] + points = point.([(1, 0), (0, 1)]) pset1 = PointSet(points) pset2 = PointSet(p for p in points) @test pset1 == pset2 - pset = PointSet(P2[(1, 0), (0, 1)]) - @test sprint(show, pset) == "2 PointSet{2,$T}" + pset = PointSet(point.([(1, 0), (0, 1)])) + @test sprint(show, pset) == "2 PointSet" if T == Float32 @test sprint(show, MIME"text/plain"(), pset) == """ - 2 PointSet{2,Float32} - ├─ Point(1.0f0, 0.0f0) - └─ Point(0.0f0, 1.0f0)""" + 2 PointSet + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" elseif T == Float64 @test sprint(show, MIME"text/plain"(), pset) == """ - 2 PointSet{2,Float64} - ├─ Point(1.0, 0.0) - └─ Point(0.0, 1.0)""" + 2 PointSet + ├─ Point(x: 1.0 m, y: 0.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" end end end diff --git a/test/sideof.jl b/test/sideof.jl index ad2bc25ec..b8efb9657 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -1,57 +1,57 @@ @testset "sideof" begin - p1, p2, p3 = P2(0, 0), P2(1, 1), P2(0.25, 0.5) - l = Line(P2(0.5, 0.0), P2(0.0, 1.0)) + p1, p2, p3 = point(0, 0), point(1, 1), point(0.25, 0.5) + l = Line(point(0.5, 0.0), point(0.0, 1.0)) @test sideof(p1, l) == LEFT @test sideof(p2, l) == RIGHT @test sideof(p3, l) == ON pts = [p1, p2, p3] @test sideof(pts, l) == [LEFT, RIGHT, ON] - p1, p2, p3 = P2(0.5, 0.5), P2(1.5, 0.5), P2(1, 1) - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + p1, p2, p3 = point(0.5, 0.5), point(1.5, 0.5), point(1, 1) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT @test sideof(p3, c) == IN pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, IN] - points = P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)] + points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)]) connec = connect.([(1, 3, 2), (1, 2, 4), (1, 4, 3), (2, 3, 4)], Triangle) mesh = SimpleMesh(points, connec) - @test sideof(P3(0.25, 0.25, 0.1), mesh) == IN - @test sideof(P3(0.25, 0.25, -0.1), mesh) == OUT - pts = P3[(0.25, 0.25, 0.1), (0.25, 0.25, -0.1)] + @test sideof(point(0.25, 0.25, 0.1), mesh) == IN + @test sideof(point(0.25, 0.25, -0.1), mesh) == OUT + pts = point.([(0.25, 0.25, 0.1), (0.25, 0.25, -0.1)]) @test sideof(pts, mesh) == [IN, OUT] # ray goes through vertex - @test sideof(P3(0.25, 0.25, 0.1), mesh) == IN - @test sideof(P3(0.25, 0.25, -0.1), mesh) == OUT + @test sideof(point(0.25, 0.25, 0.1), mesh) == IN + @test sideof(point(0.25, 0.25, -0.1), mesh) == OUT # ray goes through edge of triangle - @test sideof(P3(0.1, 0.1, 0.1), mesh) == IN - @test sideof(P3(0.1, 0.1, -0.1), mesh) == OUT + @test sideof(point(0.1, 0.1, 0.1), mesh) == IN + @test sideof(point(0.1, 0.1, -0.1), mesh) == OUT # point coincides with edge of triangle - @test sideof(P3(0.5, 0.0, 0.0), mesh) == IN + @test sideof(point(0.5, 0.0, 0.0), mesh) == IN # point coincides with corner of triangle - @test sideof(P3(0.0, 0.0, 0.0), mesh) == IN + @test sideof(point(0.0, 0.0, 0.0), mesh) == IN - points = P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)] + points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) mesh = SimpleMesh(points, connec) # ray collinear with edge - @test sideof(P3(0.0, 0.0, 0.1), mesh) == IN - @test sideof(P3(0.0, 0.0, -0.1), mesh) == OUT + @test sideof(point(0.0, 0.0, 0.1), mesh) == IN + @test sideof(point(0.0, 0.0, -0.1), mesh) == OUT # sideof for meshes that have elements > 3-gons. - points = P3[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1), (1, 1, 0)] + points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1), (1, 1, 0)]) connec = connect.([(1, 2, 4), (1, 4, 3), (2, 3, 4), (1, 2, 5, 3)]) mesh = SimpleMesh(points, connec) - @test sideof(P3(0.25, 0.25, 0.1), mesh) == IN + @test sideof(point(0.25, 0.25, 0.1), mesh) == IN # sideof only defined for surface meshes - points = P3[(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)] + points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4)], [Tetrahedron]) mesh = SimpleMesh(points, connec) - @test_throws AssertionError("winding number only defined for surface meshes") sideof(P3(0, 0, 0), mesh) + @test_throws AssertionError("winding number only defined for surface meshes") sideof(point(0, 0, 0), mesh) end diff --git a/test/simplification.jl b/test/simplification.jl index f8f64f081..50b561ee6 100644 --- a/test/simplification.jl +++ b/test/simplification.jl @@ -1,14 +1,14 @@ @testset "Simplification" begin @testset "DouglasPeucker" begin - c = Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = simplify(c, DouglasPeucker(T(0.1))) s2 = simplify(c, DouglasPeucker(T(0.5))) - @test s1 == Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) - @test s2 == Ring(P2[(0, 0), (1.5, 0.5), (0, 1)]) + @test s1 == Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + @test s2 == Ring(point.([(0, 0), (1.5, 0.5), (0, 1)])) - p = PolyArea(Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + p = PolyArea(Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) s1 = simplify(p, DouglasPeucker(T(0.5))) - @test s1 == PolyArea(Ring(P2[(0, 0), (1.5, 0.5), (0, 1)])) + @test s1 == PolyArea(Ring(point.([(0, 0), (1.5, 0.5), (0, 1)]))) m = Multi([p, p]) s2 = simplify(m, DouglasPeucker(T(0.5))) @test s2 == Multi([s1, s1]) @@ -17,7 +17,7 @@ @test s3 == GeometrySet([s1, s1]) # perform binary search for ϵ tolerance - c = Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = simplify(c, DouglasPeucker(T(0.1))) s2 = simplify(c, DouglasPeucker(max=6)) @test s1 == s2 @@ -27,29 +27,29 @@ end @testset "Selinger" begin - c = Ring(P2[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) s1 = simplify(c, Selinger(0.1)) s2 = simplify(c, Selinger(0.5)) - @test s1 == Ring(P2[(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)]) - @test s2 == Ring(P2[(1, 0), (2, 2), (0, 2), (0, 0)]) + @test s1 == Ring(point.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) + @test s2 == Ring(point.([(1, 0), (2, 2), (0, 2), (0, 0)])) end @testset "Utilities" begin # decimate is a helper function to simplify # geometries with an appropriate method - b = Box(P2(0, 0), P2(1, 1)) + b = Box(point(0, 0), point(1, 1)) s = decimate(b, 1.0) @test s isa Polygon @test nvertices(s) == 3 - @test boundary(s) == Ring(P2[(0, 0), (1, 0), (0, 1)]) + @test boundary(s) == Ring(point.([(0, 0), (1, 0), (0, 1)])) - c = Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = decimate(c, T(0.1)) s2 = decimate(c, T(0.5)) - @test s1 == Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) - @test s2 == Ring(P2[(0, 0), (1.5, 0.5), (0, 1)]) + @test s1 == Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + @test s2 == Ring(point.([(0, 0), (1.5, 0.5), (0, 1)])) - c = Ring(P2[(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]) + c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = decimate(c, T(0.1)) s2 = decimate(c, max=6) @test s1 == s2 diff --git a/test/sorting.jl b/test/sorting.jl index 2fbf2aab5..7e586c4b7 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -1,8 +1,18 @@ @testset "Sorting" begin @testset "DirectionSort" begin - g = CartesianGrid{T}(3, 3) + g = cartgrid(3, 3) s = sort(g, DirectionSort((T(1), T(1)))) @test centroid.(s) == - P2[(0.5, 0.5), (1.5, 0.5), (0.5, 1.5), (2.5, 0.5), (1.5, 1.5), (0.5, 2.5), (2.5, 1.5), (1.5, 2.5), (2.5, 2.5)] + point.([ + (0.5, 0.5), + (1.5, 0.5), + (0.5, 1.5), + (2.5, 0.5), + (1.5, 1.5), + (0.5, 2.5), + (2.5, 1.5), + (1.5, 2.5), + (2.5, 2.5) + ]) end end diff --git a/test/subdomains.jl b/test/subdomains.jl index e6470fadb..ae33c6423 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -1,5 +1,5 @@ @testset "SubDomains" begin - pset = PointSet(rand(P3, 100)) + pset = PointSet(randpoint3(100)) inds = rand(1:100, 3) v = view(pset, inds) @test nelements(v) == 3 @@ -9,7 +9,7 @@ @test centroid(v, i) == p end - grid = CartesianGrid{T}(10, 10) + grid = cartgrid(10, 10) inds = rand(1:100, 3) v = view(grid, inds) @test nelements(v) == 3 @@ -19,7 +19,7 @@ @test centroid(v, i) == centroid(e) end - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) inds = rand(1:4, 3) @@ -32,101 +32,101 @@ end # view of view stores the correct domain - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) v = view(view(g, 11:20), 1:3) - @test v isa SubGrid{2,T} + @test v isa SubGrid{2} @test v[1] == g[11] @test v[2] == g[12] @test v[3] == g[13] # centroid of view of PointSet - points = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) pview = view(PointSet(points), 1:4) - @test centroid(pview) == P2(0.5, 0.5) + @test centroid(pview) == point(0.5, 0.5) # measure of view - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) v = view(g, 1:3) - @test measure(v) ≈ T(3) + @test measure(v) ≈ T(3) * u"m^2" # concatenation with same parent - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) vg = vcat(view(g, 50:70), view(g, 10:30)) - @test vg isa SubGrid{2,T} + @test vg isa SubGrid{2} @test vg == view(g, [50:70; 10:30]) # concatenation with different parents - g1 = CartesianGrid{T}(10, 10) - g2 = CartesianGrid{T}(20, 20) + g1 = cartgrid(10, 10) + g2 = cartgrid(20, 20) vg = vcat(view(g1, 50:70), view(g2, 10:30)) @test vg isa GeometrySet @test vg == GeometrySet([g1[50:70]; g2[10:30]]) # eltype - d1 = CartesianGrid{T}(1000, 1000) - d2 = CartesianGrid{T}(1000, 1000, 1000) - d3 = GeometrySet([P2(0, 0), Box(P2(0, 0), P2(1, 1)), P2(2, 2)]) + d1 = cartgrid(1000, 1000) + d2 = cartgrid(1000, 1000, 1000) + d3 = GeometrySet([point(0, 0), Box(point(0, 0), point(1, 1)), point(2, 2)]) v1 = view(d1, 1:500000) v2 = view(d2, 1:500000000) v3 = view(d3, [1, 3]) - @test eltype(v1) === Quadrangle{2,T} - @test eltype(v2) === Hexahedron{3,T} - @test eltype(v3) === Primitive{2,T} + @test eltype(v1) <: Quadrangle{2} + @test eltype(v2) <: Hexahedron{3} + @test eltype(v3) <: Primitive{2} # show - pset = PointSet(P2.(1:100, 1:100)) + pset = PointSet(point.(1:100, 1:100)) v1 = view(pset, 1:10) v2 = view(pset, [4, 8, 10, 7, 9, 1, 2, 3, 6, 5]) - @test sprint(show, v1) == "10 view(::PointSet{2,$T}, 1:10)" - @test sprint(show, v2) == "10 view(::PointSet{2,$T}, [4, 8, 10, 7, ..., 2, 3, 6, 5])" + @test sprint(show, v1) == "10 view(::PointSet, 1:10)" + @test sprint(show, v2) == "10 view(::PointSet, [4, 8, 10, 7, ..., 2, 3, 6, 5])" if T === Float32 @test sprint(show, MIME"text/plain"(), v1) == """ - 10 view(::PointSet{2,Float32}, 1:10) - ├─ Point(1.0f0, 1.0f0) - ├─ Point(2.0f0, 2.0f0) - ├─ Point(3.0f0, 3.0f0) - ├─ Point(4.0f0, 4.0f0) - ├─ Point(5.0f0, 5.0f0) - ├─ Point(6.0f0, 6.0f0) - ├─ Point(7.0f0, 7.0f0) - ├─ Point(8.0f0, 8.0f0) - ├─ Point(9.0f0, 9.0f0) - └─ Point(10.0f0, 10.0f0)""" + 10 view(::PointSet, 1:10) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + ├─ Point(x: 3.0f0 m, y: 3.0f0 m) + ├─ Point(x: 4.0f0 m, y: 4.0f0 m) + ├─ Point(x: 5.0f0 m, y: 5.0f0 m) + ├─ Point(x: 6.0f0 m, y: 6.0f0 m) + ├─ Point(x: 7.0f0 m, y: 7.0f0 m) + ├─ Point(x: 8.0f0 m, y: 8.0f0 m) + ├─ Point(x: 9.0f0 m, y: 9.0f0 m) + └─ Point(x: 10.0f0 m, y: 10.0f0 m)""" @test sprint(show, MIME"text/plain"(), v2) == """ - 10 view(::PointSet{2,Float32}, [4, 8, 10, 7, ..., 2, 3, 6, 5]) - ├─ Point(4.0f0, 4.0f0) - ├─ Point(8.0f0, 8.0f0) - ├─ Point(10.0f0, 10.0f0) - ├─ Point(7.0f0, 7.0f0) - ├─ Point(9.0f0, 9.0f0) - ├─ Point(1.0f0, 1.0f0) - ├─ Point(2.0f0, 2.0f0) - ├─ Point(3.0f0, 3.0f0) - ├─ Point(6.0f0, 6.0f0) - └─ Point(5.0f0, 5.0f0)""" + 10 view(::PointSet, [4, 8, 10, 7, ..., 2, 3, 6, 5]) + ├─ Point(x: 4.0f0 m, y: 4.0f0 m) + ├─ Point(x: 8.0f0 m, y: 8.0f0 m) + ├─ Point(x: 10.0f0 m, y: 10.0f0 m) + ├─ Point(x: 7.0f0 m, y: 7.0f0 m) + ├─ Point(x: 9.0f0 m, y: 9.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + ├─ Point(x: 3.0f0 m, y: 3.0f0 m) + ├─ Point(x: 6.0f0 m, y: 6.0f0 m) + └─ Point(x: 5.0f0 m, y: 5.0f0 m)""" else @test sprint(show, MIME"text/plain"(), v1) == """ - 10 view(::PointSet{2,Float64}, 1:10) - ├─ Point(1.0, 1.0) - ├─ Point(2.0, 2.0) - ├─ Point(3.0, 3.0) - ├─ Point(4.0, 4.0) - ├─ Point(5.0, 5.0) - ├─ Point(6.0, 6.0) - ├─ Point(7.0, 7.0) - ├─ Point(8.0, 8.0) - ├─ Point(9.0, 9.0) - └─ Point(10.0, 10.0)""" + 10 view(::PointSet, 1:10) + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + ├─ Point(x: 3.0 m, y: 3.0 m) + ├─ Point(x: 4.0 m, y: 4.0 m) + ├─ Point(x: 5.0 m, y: 5.0 m) + ├─ Point(x: 6.0 m, y: 6.0 m) + ├─ Point(x: 7.0 m, y: 7.0 m) + ├─ Point(x: 8.0 m, y: 8.0 m) + ├─ Point(x: 9.0 m, y: 9.0 m) + └─ Point(x: 10.0 m, y: 10.0 m)""" @test sprint(show, MIME"text/plain"(), v2) == """ - 10 view(::PointSet{2,Float64}, [4, 8, 10, 7, ..., 2, 3, 6, 5]) - ├─ Point(4.0, 4.0) - ├─ Point(8.0, 8.0) - ├─ Point(10.0, 10.0) - ├─ Point(7.0, 7.0) - ├─ Point(9.0, 9.0) - ├─ Point(1.0, 1.0) - ├─ Point(2.0, 2.0) - ├─ Point(3.0, 3.0) - ├─ Point(6.0, 6.0) - └─ Point(5.0, 5.0)""" + 10 view(::PointSet, [4, 8, 10, 7, ..., 2, 3, 6, 5]) + ├─ Point(x: 4.0 m, y: 4.0 m) + ├─ Point(x: 8.0 m, y: 8.0 m) + ├─ Point(x: 10.0 m, y: 10.0 m) + ├─ Point(x: 7.0 m, y: 7.0 m) + ├─ Point(x: 9.0 m, y: 9.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + ├─ Point(x: 3.0 m, y: 3.0 m) + ├─ Point(x: 6.0 m, y: 6.0 m) + └─ Point(x: 5.0 m, y: 5.0 m)""" end end diff --git a/test/supportfun.jl b/test/supportfun.jl index f15074471..a480a44bd 100644 --- a/test/supportfun.jl +++ b/test/supportfun.jl @@ -1,20 +1,20 @@ @testset "Support function" begin - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) - @test supportfun(t, V2(1, 0)) == P2(1, 0) - @test supportfun(t, V2(0, 1)) == P2(0, 1) - @test supportfun(t, V2(-1, -1)) == P2(0, 0) - @test supportfun(t, V2(-1, 1)) == P2(0, 1) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test supportfun(t, vector(1, 0)) == point(1, 0) + @test supportfun(t, vector(0, 1)) == point(0, 1) + @test supportfun(t, vector(-1, -1)) == point(0, 0) + @test supportfun(t, vector(-1, 1)) == point(0, 1) - b = Ball(P2(0, 0), T(2)) - @test supportfun(b, V2(1, 1)) ≈ P2(√2, √2) - @test supportfun(b, V2(1, 0)) ≈ P2(2, 0) - @test supportfun(b, V2(0, 1)) ≈ P2(0, 2) - @test supportfun(b, V2(-1, 1)) ≈ P2(-√2, √2) + b = Ball(point(0, 0), T(2)) + @test supportfun(b, vector(1, 1)) ≈ point(√2, √2) + @test supportfun(b, vector(1, 0)) ≈ point(2, 0) + @test supportfun(b, vector(0, 1)) ≈ point(0, 2) + @test supportfun(b, vector(-1, 1)) ≈ point(-√2, √2) - b = Box(P2(0, 0), P2(1, 1)) - @test supportfun(b, V2(1, 1)) ≈ P2(1, 1) - @test supportfun(b, V2(1, 0)) ≈ P2(1, 0) - @test supportfun(b, V2(-1, 0)) ≈ P2(0, 0) - @test supportfun(b, V2(-1, -1)) ≈ P2(0, 0) - @test supportfun(b, V2(-1, 1)) ≈ P2(0, 1) + b = Box(point(0, 0), point(1, 1)) + @test supportfun(b, vector(1, 1)) ≈ point(1, 1) + @test supportfun(b, vector(1, 0)) ≈ point(1, 0) + @test supportfun(b, vector(-1, 0)) ≈ point(0, 0) + @test supportfun(b, vector(-1, -1)) ≈ point(0, 0) + @test supportfun(b, vector(-1, 1)) ≈ point(0, 1) end diff --git a/test/tolerances.jl b/test/tolerances.jl index f2bcd74f0..2e4da9908 100644 --- a/test/tolerances.jl +++ b/test/tolerances.jl @@ -1,10 +1,20 @@ @testset "tolerances" begin - Q = typeof(zero(T) * u"m") + ℒ = ℳ + 𝒜 = typeof(zero(ℳ)^2) + 𝒱 = typeof(zero(ℳ)^3) if T === Float32 @test atol(T) == 1.0f-5 - @test atol(Q) == 1.0f-5 * u"m" + @test atol(ℒ) == 1.0f-5 * u"m" + @test atol(𝒜) == 1.0f-5^2 * u"m^2" + @test atol(𝒱) == 1.0f-5^3 * u"m^3" else - @test atol(T) == 1e-10 - @test atol(Q) == 1e-10 * u"m" + @test atol(T) == 1.0e-10 + @test atol(ℒ) == 1.0e-10 * u"m" + @test atol(𝒜) == 1.0e-10^2 * u"m^2" + @test atol(𝒱) == 1.0e-10^3 * u"m^3" end + @test atol(zero(T)) == atol(T) + @test atol(zero(ℒ)) == atol(ℒ) + @test atol(zero(𝒜)) == atol(𝒜) + @test atol(zero(𝒱)) == atol(𝒱) end diff --git a/test/trajecs.jl b/test/trajecs.jl index 4e883cfaf..73143ef6a 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -1,27 +1,27 @@ @testset "Trajectories" begin @testset "CylindricalTrajectory" begin - s = Segment(P3(0, 0, 0), P3(0, 0, 1)) + s = Segment(point(0, 0, 0), point(0, 0, 1)) c = [s(t) for t in range(T(0), stop=T(1), length=10)] t = CylindricalTrajectory(c) @test eltype(t) <: Cylinder @test nelements(t) == 10 - @test radius(t) == T(1) + @test radius(t) == T(1) * u"m" @test topology(t) == GridTopology(10) - b = BezierCurve([P3(0, 0, 0), P3(3, 3, 0), P3(3, 0, 7)]) + b = BezierCurve([point(0, 0, 0), point(3, 3, 0), point(3, 0, 7)]) c = [b(t) for t in range(T(0), stop=T(1), length=20)] t = CylindricalTrajectory(c, T(2)) @test eltype(t) <: Cylinder @test nelements(t) == 20 - @test radius(t) == T(2) + @test radius(t) == T(2) * u"m" @test topology(t) == GridTopology(20) # trajectory with single cylinder - t = CylindricalTrajectory([P3(0, 0, 0)], T(1)) + t = CylindricalTrajectory([point(0, 0, 0)], T(1)) @test eltype(t) <: Cylinder @test nelements(t) == 1 - @test radius(t) == T(1) + @test radius(t) == T(1) * u"m" @test topology(t) == GridTopology(1) - @test t[1] == Cylinder(P3(0, 0, -0.5), P3(0, 0, 0.5), T(1)) + @test t[1] == Cylinder(point(0, 0, -0.5), point(0, 0, 0.5), T(1)) end end diff --git a/test/transforms.jl b/test/transforms.jl index bb89bde4b..d66873066 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -13,9 +13,9 @@ # ---- f = Rotate(Angle2d(T(π / 2))) - v = V2(1, 0) + v = vector(1, 0) r, c = TB.apply(f, v) - @test r ≈ V2(0, 1) + @test r ≈ vector(0, 1) @test TB.revert(f, r, c) ≈ v # ------ @@ -23,9 +23,9 @@ # ------ f = Rotate(Angle2d(T(π / 2))) - g = P2(1, 0) + g = point(1, 0) r, c = TB.apply(f, g) - @test r ≈ P2(0, 1) + @test r ≈ point(0, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -33,9 +33,9 @@ # -------- f = Rotate(Angle2d(T(π / 2))) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(0, 0), P2(0, 1)) + @test r ≈ Segment(point(0, 0), point(0, 1)) @test TB.revert(f, r, c) ≈ g # ---- @@ -43,27 +43,27 @@ # ---- f = Rotate(Angle2d(T(π / 2))) - g = Box(P2(0, 0), P2(1, 1)) + g = Box(point(0, 0), point(1, 1)) r, c = TB.apply(f, g) @test r isa Quadrangle - @test r ≈ Quadrangle(P2(0, 0), P2(0, 1), P2(-1, 1), P2(-1, 0)) + @test r ≈ Quadrangle(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) q = TB.revert(f, r, c) @test q isa Quadrangle @test q ≈ convert(Quadrangle, g) - f = Rotate(V3(1, 0, 0), V3(0, 1, 0)) - g = Box(P3(0, 0, 0), P3(1, 1, 1)) + f = Rotate(vector(1, 0, 0), vector(0, 1, 0)) + g = Box(point(0, 0, 0), point(1, 1, 1)) r, c = TB.apply(f, g) @test r isa Hexahedron @test r ≈ Hexahedron( - P3(0, 0, 0), - P3(0, 1, 0), - P3(-1, 1, 0), - P3(-1, 0, 0), - P3(0, 0, 1), - P3(0, 1, 1), - P3(-1, 1, 1), - P3(-1, 0, 1) + point(0, 0, 0), + point(0, 1, 0), + point(-1, 1, 0), + point(-1, 0, 0), + point(0, 0, 1), + point(0, 1, 1), + point(-1, 1, 1), + point(-1, 0, 1) ) h = TB.revert(f, r, c) @test h isa Hexahedron @@ -74,15 +74,15 @@ # ---------- f = Rotate(Angle2d(T(π / 2))) - g = Rope(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + g = Rope(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) r, c = TB.apply(f, g) - @test r ≈ Rope(P2(0, 0), P2(0, 1), P2(-1, 1), P2(-1, 0)) + @test r ≈ Rope(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) @test TB.revert(f, r, c) ≈ g f = Rotate(Angle2d(T(π / 2))) - g = Ring(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + g = Ring(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) r, c = TB.apply(f, g) - @test r ≈ Ring(P2(0, 0), P2(0, 1), P2(-1, 1), P2(-1, 0)) + @test r ≈ Ring(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -90,15 +90,15 @@ # --------- f = Rotate(AngleAxis(T(π / 2), T(0), T(0), T(1))) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(0, 0, 0), P3(0, 1, 0), P3(-1, 0, 0)) + @test r ≈ Triangle(point(0, 0, 0), point(0, 1, 0), point(-1, 0, 0)) @test TB.revert(f, r, c) ≈ g - f = Rotate(V3(0, 0, 1), V3(1, 0, 0)) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 0)) + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(0, 0, 0), P3(0, 0, -1), P3(0, 1, 0)) + @test r ≈ Triangle(point(0, 0, 0), point(0, 0, -1), point(0, 1, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -106,9 +106,9 @@ # --------- f = Rotate(Angle2d(T(π / 2))) - p = PolyArea(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + p = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) r, c = TB.apply(f, p) - @test r ≈ PolyArea(P2(0, 0), P2(0, 1), P2(-1, 1), P2(-1, 0)) + @test r ≈ PolyArea(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) @test TB.revert(f, r, c) ≈ p # ---------- @@ -116,7 +116,7 @@ # ---------- f = Rotate(Angle2d(T(π / 2))) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -126,20 +126,20 @@ # PLANE # ------ - f = Rotate(V3(0, 0, 1), V3(1, 0, 0)) - g = Plane(P3(0, 0, 0), V3(0, 0, 1)) + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) + g = Plane(point(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(P3(0, 0, 0), V3(1, 0, 0)) + @test r ≈ Plane(point(0, 0, 0), vector(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- # CYLINDER # --------- - f = Rotate(V3(0, 0, 1), V3(1, 0, 0)) + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(P3(0, 0, 0), P3(1, 0, 0)) + @test r ≈ Cylinder(point(0, 0, 0), point(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -147,9 +147,9 @@ # --------- f = Rotate(Angle2d(T(π / 2))) - d = PointSet([P2(0, 0), P2(1, 0), P2(1, 1)]) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([P2(0, 0), P2(0, 1), P2(-1, 1)]) + @test r ≈ PointSet([point(0, 0), point(0, 1), point(-1, 1)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -157,7 +157,7 @@ # ------------ f = Rotate(Angle2d(T(π / 2))) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -172,7 +172,7 @@ # -------------- f = Rotate(Angle2d(T(π / 2))) - d = CartesianGrid{T}(10, 10) + d = cartgrid(10, 10) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -183,7 +183,7 @@ # ---------------- f = Rotate(Angle2d(T(π / 2))) - d = convert(RectilinearGrid, CartesianGrid{T}(10, 10)) + d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -194,7 +194,7 @@ # --------------- f = Rotate(Angle2d(T(π / 2))) - d = convert(StructuredGrid, CartesianGrid{T}(10, 10)) + d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -205,7 +205,7 @@ # ----------- f = Rotate(Angle2d(T(π / 2))) - p = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -217,9 +217,9 @@ # --------- f = Rotate(T(π / 2)) - v = V2(1, 0) + v = vector(1, 0) r, c = TB.apply(f, v) - @test r ≈ V2(0, 1) + @test r ≈ vector(0, 1) @test TB.revert(f, r, c) ≈ v end @@ -228,18 +228,24 @@ @test TB.isrevertible(Translate) @test TB.isinvertible(Translate) @test TB.inverse(Translate(T(1), T(2))) == Translate(T(-1), T(-2)) - offsets = (T(1), T(2)) + offsets = (T(1) * u"m", T(2) * u"m") f = Translate(offsets) @test TB.parameters(f) == (; offsets) + f = Translate(T(1), T(2)) + @test TB.parameters(f) == (; offsets) + f = Translate(T(1), 2) + @test TB.parameters(f) == (; offsets) + f = Translate(1, 2) + @test TB.parameters(f) == (; offsets) # ---- # VEC # ---- f = Translate(T(1), T(1)) - v = V2(1, 0) + v = vector(1, 0) r, c = TB.apply(f, v) - @test r ≈ V2(1, 0) + @test r ≈ vector(1, 0) @test TB.revert(f, r, c) ≈ v # ------ @@ -247,9 +253,9 @@ # ------ f = Translate(T(1), T(1)) - g = P2(1, 0) + g = point(1, 0) r, c = TB.apply(f, g) - @test r ≈ P2(2, 1) + @test r ≈ point(2, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -257,9 +263,9 @@ # -------- f = Translate(T(1), T(1)) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(1, 1), P2(2, 1)) + @test r ≈ Segment(point(1, 1), point(2, 1)) @test TB.revert(f, r, c) ≈ g # ---- @@ -267,10 +273,10 @@ # ---- f = Translate(T(1), T(1)) - g = Box(P2(0, 0), P2(1, 1)) + g = Box(point(0, 0), point(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(P2(1, 1), P2(2, 2)) + @test r ≈ Box(point(1, 1), point(2, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -278,9 +284,9 @@ # --------- f = Translate(T(1), T(2), T(3)) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 1)) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(1, 2, 3), P3(2, 2, 3), P3(1, 3, 4)) + @test r ≈ Triangle(point(1, 2, 3), point(2, 2, 3), point(1, 3, 4)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -288,7 +294,7 @@ # ---------- f = Translate(T(1), T(1)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -299,9 +305,9 @@ # ------ f = Translate(T(0), T(0), T(1)) - g = Plane(P3(0, 0, 0), V3(0, 0, 1)) + g = Plane(point(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(P3(0, 0, 1), V3(0, 0, 1)) + @test r ≈ Plane(point(0, 0, 1), vector(0, 0, 1)) @test TB.revert(f, r, c) ≈ g # --------- @@ -311,7 +317,7 @@ f = Translate(T(0), T(0), T(1)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(P3(0, 0, 1), P3(0, 0, 2)) + @test r ≈ Cylinder(point(0, 0, 1), point(0, 0, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -319,9 +325,9 @@ # --------- f = Translate(T(1), T(1)) - d = PointSet([P2(0, 0), P2(1, 0), P2(1, 1)]) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([P2(1, 1), P2(2, 1), P2(2, 2)]) + @test r ≈ PointSet([point(1, 1), point(2, 1), point(2, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -329,7 +335,7 @@ # ------------ f = Translate(T(1), T(1)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -344,10 +350,10 @@ # -------------- f = Translate(T(1), T(1)) - d = CartesianGrid{T}(10, 10) + d = cartgrid(10, 10) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(P2(1, 1), P2(11, 11), dims=(10, 10)) + @test r ≈ CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ---------------- @@ -377,7 +383,7 @@ # ----------- f = Translate(T(1), T(1)) - p = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -394,18 +400,22 @@ f = Affine(T[6 3; 10 5], T[1, 1]) @test !TB.isrevertible(f) @test !TB.isinvertible(f) - A, b = Angle2d(T(π / 2)), T[1, 1] + A, b = Angle2d(T(π / 2)), SVector(T(1) * u"m", T(1) * u"m") f = Affine(A, b) @test TB.parameters(f) == (; A, b) + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + @test TB.parameters(f) == (; A, b) + f = Affine(Angle2d(T(π / 2)), [1, 1]) + @test TB.parameters(f) == (; A, b) # ---- # VEC # ---- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - v = V2(1, 0) + v = vector(1, 0) r, c = TB.apply(f, v) - @test r ≈ V2(0, 1) + @test r ≈ vector(0, 1) @test TB.revert(f, r, c) ≈ v # ------ @@ -413,9 +423,9 @@ # ------ f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = P2(1, 0) + g = point(1, 0) r, c = TB.apply(f, g) - @test r ≈ P2(1, 2) + @test r ≈ point(1, 2) @test TB.revert(f, r, c) ≈ g # -------- @@ -423,9 +433,9 @@ # -------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(1, 1), P2(1, 2)) + @test r ≈ Segment(point(1, 1), point(1, 2)) @test TB.revert(f, r, c) ≈ g # ---- @@ -433,27 +443,27 @@ # ---- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Box(P2(0, 0), P2(1, 1)) + g = Box(point(0, 0), point(1, 1)) r, c = TB.apply(f, g) @test r isa Quadrangle - @test r ≈ Quadrangle(P2(1, 1), P2(1, 2), P2(0, 2), P2(0, 1)) + @test r ≈ Quadrangle(point(1, 1), point(1, 2), point(0, 2), point(0, 1)) q = TB.revert(f, r, c) @test q isa Quadrangle @test q ≈ convert(Quadrangle, g) - f = Affine(rotation_between(V3(0, 0, 1), V3(1, 0, 0)), T[1, 2, 3]) - g = Box(P3(0, 0, 0), P3(1, 1, 1)) + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) + g = Box(point(0, 0, 0), point(1, 1, 1)) r, c = TB.apply(f, g) @test r isa Hexahedron @test r ≈ Hexahedron( - P3(1, 2, 3), - P3(1, 2, 2), - P3(1, 3, 2), - P3(1, 3, 3), - P3(2, 2, 3), - P3(2, 2, 2), - P3(2, 3, 2), - P3(2, 3, 3) + point(1, 2, 3), + point(1, 2, 2), + point(1, 3, 2), + point(1, 3, 3), + point(2, 2, 3), + point(2, 2, 2), + point(2, 3, 2), + point(2, 3, 3) ) h = TB.revert(f, r, c) @test h isa Hexahedron @@ -463,10 +473,10 @@ # TRIANGLE # --------- - f = Affine(rotation_between(V3(0, 0, 1), V3(1, 0, 0)), T[1, 2, 3]) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 1)) + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(1, 2, 3), P3(1, 2, 2), P3(2, 3, 3)) + @test r ≈ Triangle(point(1, 2, 3), point(1, 2, 2), point(2, 3, 3)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -474,7 +484,7 @@ # ---------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -484,20 +494,20 @@ # PLANE # ------ - f = Affine(rotation_between(V3(0, 0, 1), V3(1, 0, 0)), T[0, 0, 1]) - g = Plane(P3(0, 0, 0), V3(0, 0, 1)) + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) + g = Plane(point(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(P3(0, 0, 1), V3(1, 0, 0)) + @test r ≈ Plane(point(0, 0, 1), vector(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- # CYLINDER # --------- - f = Affine(rotation_between(V3(0, 0, 1), V3(1, 0, 0)), T[0, 0, 1]) + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(P3(0, 0, 1), P3(1, 0, 1)) + @test r ≈ Cylinder(point(0, 0, 1), point(1, 0, 1)) @test TB.revert(f, r, c) ≈ g # --------- @@ -505,9 +515,9 @@ # --------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = PointSet([P2(0, 0), P2(1, 0), P2(1, 1)]) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([P2(1, 1), P2(1, 2), P2(0, 2)]) + @test r ≈ PointSet([point(1, 1), point(1, 2), point(0, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -515,7 +525,7 @@ # ------------ f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -530,7 +540,7 @@ # -------------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = CartesianGrid{T}(10, 10) + d = cartgrid(10, 10) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -541,7 +551,7 @@ # ---------------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = convert(RectilinearGrid, CartesianGrid{T}(10, 10)) + d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -552,7 +562,7 @@ # --------------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = convert(StructuredGrid, CartesianGrid{T}(10, 10)) + d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @@ -564,10 +574,10 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) s = Rotate(T(π / 2)) → Translate(T(1), T(1)) - v = V2(1, 0) - g1 = P2(1, 0) - g2 = Segment(P2(0, 0), P2(1, 0)) - g3 = Box(P2(0, 0), P2(1, 1)) + v = vector(1, 0) + g1 = point(1, 0) + g2 = Segment(point(0, 0), point(1, 0)) + g3 = Box(point(0, 0), point(1, 1)) @test f(v) ≈ s(v) @test f(g1) ≈ s(g1) @test f(g2) ≈ s(g2) @@ -606,9 +616,9 @@ # ---- f = Scale(T(1), T(2)) - v = V2(1, 1) + v = vector(1, 1) r, c = TB.apply(f, v) - @test r ≈ V2(1, 2) + @test r ≈ vector(1, 2) @test TB.revert(f, r, c) ≈ v # ------ @@ -616,9 +626,9 @@ # ------ f = Scale(T(1), T(2)) - g = P2(1, 1) + g = point(1, 1) r, c = TB.apply(f, g) - @test r ≈ P2(1, 2) + @test r ≈ point(1, 2) @test TB.revert(f, r, c) ≈ g # -------- @@ -626,15 +636,15 @@ # -------- f = Scale(T(1), T(2)) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(0, 0), P2(1, 0)) + @test r ≈ Segment(point(0, 0), point(1, 0)) @test TB.revert(f, r, c) ≈ g f = Scale(T(2), T(1)) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(0, 0), P2(2, 0)) + @test r ≈ Segment(point(0, 0), point(2, 0)) @test TB.revert(f, r, c) ≈ g # ---- @@ -642,10 +652,10 @@ # ---- f = Scale(T(1), T(2)) - g = Box(P2(0, 0), P2(1, 1)) + g = Box(point(0, 0), point(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(P2(0, 0), P2(1, 2)) + @test r ≈ Box(point(0, 0), point(1, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -653,9 +663,9 @@ # --------- f = Scale(T(1), T(2), T(3)) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 1)) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 2, 3)) + @test r ≈ Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 2, 3)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -663,7 +673,7 @@ # ---------- f = Scale(T(1), T(2)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -674,13 +684,13 @@ # ------ f = Scale(T(1), T(1), T(2)) - g = Plane(P3(1, 1, 1), V3(0, 0, 1)) + g = Plane(point(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(P3(1, 1, 2), V3(0, 0, 1)) + @test r ≈ Plane(point(1, 1, 2), vector(0, 0, 1)) @test TB.revert(f, r, c) ≈ g f = Scale(T(2), T(1), T(1)) - g = Plane(P3(1, 1, 1), V3(0, 0, 1)) + g = Plane(point(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g @@ -692,7 +702,7 @@ f = Scale(T(1), T(1), T(2)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(P3(0, 0, 0), P3(0, 0, 2)) + @test r ≈ Cylinder(point(0, 0, 0), point(0, 0, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -700,9 +710,9 @@ # --------- f = Scale(T(1), T(2)) - d = PointSet([P2(0, 0), P2(1, 0), P2(1, 1)]) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([P2(0, 0), P2(1, 0), P2(1, 2)]) + @test r ≈ PointSet([point(0, 0), point(1, 0), point(1, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -710,7 +720,7 @@ # ------------ f = Scale(T(1), T(2)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -725,10 +735,10 @@ # -------------- f = Scale(T(1), T(2)) - d = CartesianGrid(P2(1, 1), P2(11, 11), dims=(10, 10)) + d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(P2(1, 2), P2(11, 22), dims=(10, 10)) + @test r ≈ CartesianGrid(point(1, 2), point(11, 22), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ----------- @@ -736,7 +746,7 @@ # ----------- f = Scale(T(1), T(2)) - p = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -758,9 +768,9 @@ # ---- f = Stretch(T(1), T(2)) - v = V2(1, 1) + v = vector(1, 1) r, c = TB.apply(f, v) - @test r ≈ V2(1, 2) + @test r ≈ vector(1, 2) @test TB.revert(f, r, c) ≈ v # ------ @@ -768,9 +778,9 @@ # ------ f = Stretch(T(1), T(2)) - g = P2(1, 1) + g = point(1, 1) r, c = TB.apply(f, g) - @test r ≈ P2(1, 1) + @test r ≈ point(1, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -778,15 +788,15 @@ # -------- f = Stretch(T(1), T(2)) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(0, 0), P2(1, 0)) + @test r ≈ Segment(point(0, 0), point(1, 0)) @test TB.revert(f, r, c) ≈ g f = Stretch(T(2), T(1)) - g = Segment(P2(0, 0), P2(1, 0)) + g = Segment(point(0, 0), point(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(P2(-0.5, 0), P2(1.5, 0)) + @test r ≈ Segment(point(-0.5, 0), point(1.5, 0)) @test TB.revert(f, r, c) ≈ g # ---- @@ -794,10 +804,10 @@ # ---- f = Stretch(T(1), T(2)) - g = Box(P2(0, 0), P2(1, 1)) + g = Box(point(0, 0), point(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(P2(0, -0.5), P2(1, 1.5)) + @test r ≈ Box(point(0, -0.5), point(1, 1.5)) @test TB.revert(f, r, c) ≈ g # --------- @@ -805,9 +815,9 @@ # --------- f = Stretch(T(1), T(2), T(2)) - g = Triangle(P3(0, 0, 0), P3(1, 0, 0), P3(0, 1, 1)) + g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(P3(0, -1 / 3, -1 / 3), P3(1, -1 / 3, -1 / 3), P3(0, 10 / 6, 10 / 6)) + @test r ≈ Triangle(point(0, -1 / 3, -1 / 3), point(1, -1 / 3, -1 / 3), point(0, 10 / 6, 10 / 6)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -815,7 +825,7 @@ # ---------- f = Stretch(T(1), T(2)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -826,13 +836,13 @@ # ------ f = Stretch(T(1), T(1), T(2)) - g = Plane(P3(1, 1, 1), V3(0, 0, 1)) + g = Plane(point(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g f = Stretch(T(2), T(1), T(1)) - g = Plane(P3(1, 1, 1), V3(0, 0, 1)) + g = Plane(point(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g @@ -844,7 +854,7 @@ f = Stretch(T(1), T(1), T(2)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(P3(0, 0, -0.5), P3(0, 0, 1.5)) + @test r ≈ Cylinder(point(0, 0, -0.5), point(0, 0, 1.5)) @test TB.revert(f, r, c) ≈ g # --------- @@ -852,9 +862,9 @@ # --------- f = Stretch(T(1), T(2)) - d = PointSet([P2(0, 0), P2(1, 0), P2(1, 1)]) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([P2(0, -1 / 3), P2(1, -1 / 3), P2(1, 10 / 6)]) + @test r ≈ PointSet([point(0, -1 / 3), point(1, -1 / 3), point(1, 10 / 6)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -862,7 +872,7 @@ # ------------ f = Stretch(T(1), T(2)) - t = Triangle(P2(0, 0), P2(1, 0), P2(1, 1)) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -877,10 +887,10 @@ # -------------- f = Stretch(T(1), T(2)) - d = CartesianGrid(P2(1, 1), P2(11, 11), dims=(10, 10)) + d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(P2(1, -4), P2(11, 16), dims=(10, 10)) + @test r ≈ CartesianGrid(point(1, -4), point(11, 16), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ----------- @@ -888,7 +898,7 @@ # ----------- f = Stretch(T(1), T(2)) - p = P2[(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -905,9 +915,9 @@ # --------- f = StdCoords() - d = view(PointSet(rand(P2, 100)), 1:50) + d = view(PointSet(randpoint2(100)), 1:50) r, c = TB.apply(f, d) - @test all(sides(boundingbox(r)) .≤ T(1)) + @test all(sides(boundingbox(r)) .≤ oneunit(ℳ)) @test TB.revert(f, r, c) ≈ d r2 = TB.reapply(f, d, c) @test r == r2 @@ -917,16 +927,16 @@ # -------------- f = StdCoords() - d = CartesianGrid(P2(1, 1), P2(11, 11), dims=(10, 10)) + d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(P2(-0.5, -0.5), P2(0.5, 0.5), dims=(10, 10)) + @test r ≈ CartesianGrid(point(-0.5, -0.5), point(0.5, 0.5), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d f = StdCoords() - d = CartesianGrid{T}(10, 20) + d = cartgrid(10, 20) r, c = TB.apply(f, d) - @test r ≈ CartesianGrid(P2(-0.5, -0.5), P2(0.5, 0.5), dims=(10, 20)) + @test r ≈ CartesianGrid(point(-0.5, -0.5), point(0.5, 0.5), dims=(10, 20)) @test TB.revert(f, r, c) ≈ d r2 = TB.reapply(f, d, c) @test r == r2 @@ -934,21 +944,21 @@ @testset "Repair{0}" begin @test !isaffine(Repair) - poly = PolyArea(P2[(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)]) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) rpoly = poly |> Repair{0}() @test nvertices(rpoly) == 4 - @test vertices(rpoly) == P2[(0, 0), (1, 0), (1, 1), (0, 1)] + @test vertices(rpoly) == point.([(0, 0), (1, 0), (1, 1), (0, 1)]) end @testset "Repair{1}" begin # a tetrahedron with an unused vertex - points = P3[(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)] + points = point.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) mesh = SimpleMesh(points, connec) rmesh = mesh |> Repair{1}() @test nvertices(rmesh) == nvertices(mesh) - 1 @test nelements(rmesh) == nelements(mesh) - @test P3(5, 5, 5) ∉ vertices(rmesh) + @test point(5, 5, 5) ∉ vertices(rmesh) end @testset "Repair{2}" begin end @@ -963,7 +973,7 @@ @testset "Repair{7}" begin # mesh with inconsistent orientation - points = rand(P3, 6) + points = randpoint3(6) connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) mesh = SimpleMesh(points, connec) rmesh = mesh |> Repair{7}() @@ -978,30 +988,31 @@ end @testset "Repair{8}" begin - poly = - PolyArea(P2[(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) + poly = PolyArea( + point.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) + ) rpoly = poly |> Repair{8}() @test nvertices(rpoly) == 4 - @test vertices(rpoly) == P2[(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)] + @test vertices(rpoly) == point.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) # degenerate triangle with repeated vertices - poly = PolyArea(P2[(0, 0), (1, 1), (1, 1)]) + poly = PolyArea(point.([(0, 0), (1, 1), (1, 1)])) rpoly = poly |> Repair{8}() @test !hasholes(rpoly) - @test rings(rpoly) == [Ring(P2(0, 0))] - @test vertices(rpoly) == [P2(0, 0)] + @test rings(rpoly) == [Ring(point(0, 0))] + @test vertices(rpoly) == [point(0, 0)] end @testset "Repair{9}" begin - poly = Quadrangle(P3(0, 1, 0), P3(1, 1, 0), P3(1, 0, 0), P3(0, 0, 0)) + poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) bpoly = poly |> Repair{9}() @test bpoly isa Quadrangle @test bpoly == poly end @testset "Repair{10}" begin - outer = Ring(P2[(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)]) - inner = Ring(P2[(1, 1), (1, 2), (2, 2), (2, 1)]) + outer = Ring(point.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) + inner = Ring(point.([(1, 1), (1, 2), (2, 2), (2, 1)])) poly = PolyArea(outer, inner) repair = Repair{10}() rpoly, cache = TB.apply(repair, poly) @@ -1013,36 +1024,39 @@ @testset "Bridge" begin @test !isaffine(Bridge) - δ = T(0.01) + δ = T(0.01) * u"m" f = Bridge(δ) @test TB.parameters(f) == (; δ) + f = Bridge(T(0.01)) + @test TB.parameters(f) == (; δ) # https://github.com/JuliaGeometry/Meshes.jl/issues/566 - outer = Ring(P2(6, 4), P2(6, 7), P2(1, 6), P2(1, 1), P2(5, 2)) - inner₁ = Ring(P2(3, 3), P2(3, 4), P2(4, 3)) - inner₂ = Ring(P2(2, 5), P2(2, 6), P2(3, 5)) + outer = Ring(point(6, 4), point(6, 7), point(1, 6), point(1, 1), point(5, 2)) + inner₁ = Ring(point(3, 3), point(3, 4), point(4, 3)) + inner₂ = Ring(point(2, 5), point(2, 6), point(3, 5)) poly = PolyArea([outer, inner₁, inner₂]) bpoly = poly |> Bridge(T(0.1)) @test !hasholes(bpoly) @test nvertices(bpoly) == 15 # unique and bridges - poly = PolyArea(P2[(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)]) + poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) cpoly = poly |> Repair{0}() |> Bridge() - @test cpoly == PolyArea(P2[(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)]) + @test cpoly == PolyArea(point.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) # basic ngon tests - t = Triangle(P2(0, 0), P2(1, 0), P2(0, 1)) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test (t |> Bridge() |> boundary) == boundary(t) - q = Quadrangle(P2(0, 0), P2(1, 0), P2(1, 1), P2(0, 1)) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test (q |> Bridge() |> boundary) == boundary(q) # bridges between holes - outer = P2[(0, 0), (1, 0), (1, 1), (0, 1)] - hole1 = P2[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] - hole2 = P2[(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] + outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) - @test vertices(poly) == P2[ + @test vertices(poly) == + point.([ (0, 0), (1, 0), (1, 1), @@ -1055,29 +1069,30 @@ (0.6, 0.4), (0.8, 0.4), (0.8, 0.2) - ] + ]) bpoly = poly |> Bridge(T(0.01)) - target = P2[ - (-0.0035355339059327372, 0.0035355339059327372), - (0.19646446609406729, 0.20353553390593274), - (0.2, 0.4), - (0.4, 0.405), - (0.6, 0.405), - (0.8, 0.4), - (0.8, 0.2), - (0.6, 0.2), - (0.6, 0.395), - (0.4, 0.395), - (0.4, 0.2), - (0.20353553390593274, 0.19646446609406729), - (0.0035355339059327372, -0.0035355339059327372), - (1.0, 0.0), - (1.0, 1.0), - (0.0, 1.0) - ] + target = + point.([ + (-0.0035355339059327372, 0.0035355339059327372), + (0.19646446609406729, 0.20353553390593274), + (0.2, 0.4), + (0.4, 0.405), + (0.6, 0.405), + (0.8, 0.4), + (0.8, 0.2), + (0.6, 0.2), + (0.6, 0.395), + (0.4, 0.395), + (0.4, 0.2), + (0.20353553390593274, 0.19646446609406729), + (0.0035355339059327372, -0.0035355339059327372), + (1.0, 0.0), + (1.0, 1.0), + (0.0, 1.0) + ]) @test all(vertices(bpoly) .≈ target) - poly = Quadrangle(P3(0, 1, 0), P3(1, 1, 0), P3(1, 0, 0), P3(0, 0, 0)) + poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) bpoly = poly |> Bridge() @test bpoly isa Quadrangle @test bpoly == poly diff --git a/test/traversing.jl b/test/traversing.jl index 782e9f094..bd0c69f0b 100644 --- a/test/traversing.jl +++ b/test/traversing.jl @@ -1,5 +1,5 @@ @testset "Paths" begin - grid = CartesianGrid{T}(100, 100) + grid = cartgrid(100, 100) for path in [LinearPath(), RandomPath(), ShiftedPath(LinearPath(), 0), SourcePath(1:3)] p = traverse(grid, path) @@ -16,12 +16,12 @@ @test all(1 .≤ collect(p) .≤ 100 * 100) path = RandomPath(StableRNG(123)) - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) @test traverse(grid, path) == [4, 7, 2, 1, 3, 8, 5, 6, 9] end @testset "SourcePath" begin - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) pset = PointSet(centroid.(grid)) for sdomain in [grid, pset] @@ -34,7 +34,7 @@ end @testset "ShiftedPath" begin - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) path = LinearPath() for offset in [0, 1, -1] spath = ShiftedPath(path, offset) @@ -48,10 +48,10 @@ @testset "MultiGridPath" begin path = MultiGridPath() - grid = CartesianGrid{T}(3, 3) + grid = cartgrid(3, 3) @test traverse(grid, path) == [1, 3, 7, 9, 2, 4, 5, 6, 8] - grid = CartesianGrid{T}(3, 4) + grid = cartgrid(3, 4) @test traverse(grid, path) == [1, 3, 10, 12, 2, 7, 8, 9, 4, 5, 6, 11] grid = CartesianGrid(3, 3, 2) @@ -63,11 +63,11 @@ grid = RectilinearGrid(T.(0:0.5:2), T.(0:0.5:2)) @test traverse(grid, path) == [1, 4, 13, 16, 3, 9, 11, 2, 5, 6, 7, 8, 10, 12, 14, 15] - cgrid = CartesianGrid{T}(4, 4) + cgrid = cartgrid(4, 4) rgrid = RectilinearGrid(T.(0:4), T.(0:4)) @test traverse(cgrid, path) == traverse(rgrid, path) - grid = CartesianGrid{T}(3, 4) + grid = cartgrid(3, 4) vgrid = view(grid, 3:10) @test traverse(vgrid, path) == [3, 10, 7, 8, 9, 4, 5, 6] end @@ -81,7 +81,7 @@ for (path, fname) in zip(paths, fnames) for d in (6, 7) - grid = CartesianGrid{T}(d, d) + grid = cartgrid(d, d) elems = [grid[i] for i in traverse(grid, path)] fig = viz(elems, color=1:length(elems)) @test_reference "data/$fname-$(d)x$(d).png" fig diff --git a/test/utils.jl b/test/utils.jl index a72655b9a..a1ddde27e 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,15 +1,23 @@ @testset "Utilities" begin - a, b, c = P2(0, 0), P2(1, 0), P2(0, 1) - @test signarea(a, b, c) == T(0.5) - a, b, c = P2(0, 0), P2(0, 1), P2(1, 0) - @test signarea(a, b, c) == T(-0.5) + a, b, c = point(0, 0), point(1, 0), point(0, 1) + @test signarea(a, b, c) == T(0.5) * u"m^2" + a, b, c = point(0, 0), point(0, 1), point(1, 0) + @test signarea(a, b, c) == T(-0.5) * u"m^2" - normals = [V3(1, 0, 0), V3(0, 1, 0), V3(0, 0, 1), V3(-1, 0, 0), V3(0, -1, 0), V3(0, 0, -1), V3(rand(3) .- 0.5)] + normals = [ + vector(1, 0, 0), + vector(0, 1, 0), + vector(0, 0, 1), + vector(-1, 0, 0), + vector(0, -1, 0), + vector(0, 0, -1), + vector(ntuple(i -> rand() - 0.5, 3)) + ] for n in normals u, v = householderbasis(n) - @test u isa V3 - @test v isa V3 - @test u × v ≈ n ./ norm(n) + @test u isa Vec{3} + @test v isa Vec{3} + @test ustrip.(u × v) ≈ n ./ norm(n) end @test Meshes.mayberound(1.1, 1.0, 0.2) ≈ 1.0 @@ -17,14 +25,14 @@ @test Meshes.mayberound(1.1, 1.0, 0.05) ≈ 1.1 # intersect parameters - p1, p2 = P2(0, 0), P2(1, 1) - p3, p4 = P2(1, 0), P2(0, 1) + p1, p2 = point(0, 0), point(1, 1) + p3, p4 = point(1, 0), point(0, 1) @inferred Meshes.intersectparameters(p1, p2, p3, p4) @inferred Meshes.intersectparameters(p1, p3, p2, p4) @inferred Meshes.intersectparameters(p1, p2, p1, p2) - p1, p2 = P3(0, 0, 0), P3(1, 1, 1) - p3, p4 = P3(1, 0, 0), P3(0, 1, 1) + p1, p2 = point(0, 0, 0), point(1, 1, 1) + p3, p4 = point(1, 0, 0), point(0, 1, 1) @inferred Meshes.intersectparameters(p1, p2, p3, p4) @inferred Meshes.intersectparameters(p1, p3, p2, p4) @inferred Meshes.intersectparameters(p1, p2, p1, p2) diff --git a/test/vectors.jl b/test/vectors.jl index bf2714b73..b28468691 100644 --- a/test/vectors.jl +++ b/test/vectors.jl @@ -1,94 +1,71 @@ @testset "Vectors" begin # vararg constructors - @test eltype(Vec(1, 1)) == Float64 - @test eltype(Vec(1.0, 1.0)) == Float64 - @test eltype(Vec(1.0f0, 1.0f0)) == Float32 - @test eltype(Vec1(1)) == Float64 - @test eltype(Vec2(1, 1)) == Float64 - @test eltype(Vec3(1, 1, 1)) == Float64 - @test eltype(Vec1f(1)) == Float32 - @test eltype(Vec2f(1, 1)) == Float32 - @test eltype(Vec3f(1, 1, 1)) == Float32 + @test eltype(Vec(1, 1)) == Meshes.Met{Float64} + @test eltype(Vec(1.0, 1.0)) == Meshes.Met{Float64} + @test eltype(Vec(1.0f0, 1.0f0)) == Meshes.Met{Float32} # tuple constructors - @test eltype(Vec((1, 1))) == Float64 - @test eltype(Vec((1.0, 1.0))) == Float64 - @test eltype(Vec((1.0f0, 1.0f0))) == Float32 - @test eltype(Vec1((1,))) == Float64 - @test eltype(Vec2((1, 1))) == Float64 - @test eltype(Vec3((1, 1, 1))) == Float64 - @test eltype(Vec1f((1,))) == Float32 - @test eltype(Vec2f((1, 1))) == Float32 - @test eltype(Vec3f((1, 1, 1))) == Float32 - - # parametric constructors - @test eltype(Vec{2,T}(1, 1)) == T - @test eltype(Vec{2,T}((1, 1))) == T + @test eltype(Vec((1, 1))) == Meshes.Met{Float64} + @test eltype(Vec((1.0, 1.0))) == Meshes.Met{Float64} + @test eltype(Vec((1.0f0, 1.0f0))) == Meshes.Met{Float32} # check all 1D Vec constructors, because those tend to make trouble - @test Vec(1) == Vec((1,)) - @test Vec{1,T}(0) == Vec{1,T}((0,)) - @test Vec{1,T}(-2) == Vec{1,T}((-2,)) + @test Vec(T(1)) == Vec((T(1),)) + @test Vec(T(0)) == Vec((T(0),)) + @test Vec(T(-2)) == Vec((T(-2),)) # check that input of mixed coordinate types is allowed and works as expected - @test Vec(1, 0.2) == Vec{2,Float64}(1.0, 0.2) - @test Vec((3.0, 4)) == Vec{2,Float64}(3.0, 4.0) - @test Vec((5.0, 6.0, 7)) == Vec{3,Float64}(5.0, 6.0, 7.0) - @test Vec{2,T}(8, 9.0) == Vec{2,T}((8.0, 9.0)) - @test Vec{2,T}((-1.0, -2)) == Vec{2,T}((-1, -2.0)) - @test Vec{4,T}((0, -1.0, +2, -4.0)) == Vec{4,T}((0.0f0, -1.0f0, +2.0f0, -4.0f0)) + @test Vec(1, 0.2) == Vec(1.0, 0.2) + @test Vec((3.0, 4)) == Vec(3.0, 4.0) + @test Vec((5.0, 6.0, 7)) == Vec(5.0, 6.0, 7.0) + @test Vec(8, T(9.0)) == Vec((T(8.0), T(9.0))) + @test Vec((T(-1.0), -2)) == Vec((T(-1.0), T(-2.0))) + @test Vec((0, T(-1.0), +2, T(-4.0))) == Vec((T(0.0), T(-1.0), T(+2.0), T(-4.0))) # integer coordinates are converted to float - @test eltype(Vec(1)) == Float64 - @test eltype(Vec(1, 2)) == Float64 - @test eltype(Vec(1, 2, 3)) == Float64 - @test Tuple(Vec(1)) == (1.0,) - @test Tuple(Vec(1, 2)) == (1.0, 2.0) - @test Tuple(Vec(1, 2, 3)) == (1.0, 2.0, 3.0) + @test eltype(Vec(1)) == Meshes.Met{Float64} + @test eltype(Vec(1, 2)) == Meshes.Met{Float64} + @test eltype(Vec(1, 2, 3)) == Meshes.Met{Float64} + @test Tuple(Vec(1)) == (1.0u"m",) + @test Tuple(Vec(1, 2)) == (1.0u"m", 2.0u"m") + @test Tuple(Vec(1, 2, 3)) == (1.0u"m", 2.0u"m", 3.0u"m") # Unitful coordinates - vector = Vec(1u"m", 1u"m") - @test unit(eltype(vector)) == u"m" - @test Unitful.numtype(eltype(vector)) === Float64 - vector = Vec(1.0u"m", 1.0u"m") - @test unit(eltype(vector)) == u"m" - @test Unitful.numtype(eltype(vector)) === Float64 - vector = Vec(1.0f0u"m", 1.0f0u"m") - @test unit(eltype(vector)) == u"m" - @test Unitful.numtype(eltype(vector)) === Float32 - - # throws - @test_throws DimensionMismatch Vec{2,T}(1) - @test_throws DimensionMismatch Vec{3,T}((2, 3)) - @test_throws DimensionMismatch Vec{3,T}([2, 3]) - @test_throws DimensionMismatch Vec{-3,T}((4, 5, 6)) - @test_throws DimensionMismatch Vec{-3,T}([4, 5, 6]) + v = Vec(1u"m", 1u"m") + @test unit(eltype(v)) == u"m" + @test Unitful.numtype(eltype(v)) === Float64 + v = Vec(1.0u"m", 1.0u"m") + @test unit(eltype(v)) == u"m" + @test Unitful.numtype(eltype(v)) === Float64 + v = Vec(1.0f0u"m", 1.0f0u"m") + @test unit(eltype(v)) == u"m" + @test Unitful.numtype(eltype(v)) === Float32 # angles between 2D vectors - @test ∠(V2(1, 0), V2(0, 1)) ≈ T(π / 2) - @test ∠(V2(1, 0), V2(0, -1)) ≈ T(-π / 2) - @test ∠(V2(1, 0), V2(-1, 0)) ≈ T(π) - @test ∠(V2(0, 1), V2(-1, 0)) ≈ T(π / 2) - @test ∠(V2(0, 1), V2(0, -1)) ≈ T(π) - @test ∠(V2(0, 1), V2(1, 1)) ≈ T(-π / 4) - @test ∠(V2(0, -1), V2(1, 1)) ≈ T(π * 3 / 4) - @test ∠(V2(-1, -1), V2(1, 1)) ≈ T(π) - @test ∠(V2(-2, 0), V2(2, 0)) ≈ T(π) + @test ∠(vector(1, 0), vector(0, 1)) ≈ T(π / 2) + @test ∠(vector(1, 0), vector(0, -1)) ≈ T(-π / 2) + @test ∠(vector(1, 0), vector(-1, 0)) ≈ T(π) + @test ∠(vector(0, 1), vector(-1, 0)) ≈ T(π / 2) + @test ∠(vector(0, 1), vector(0, -1)) ≈ T(π) + @test ∠(vector(0, 1), vector(1, 1)) ≈ T(-π / 4) + @test ∠(vector(0, -1), vector(1, 1)) ≈ T(π * 3 / 4) + @test ∠(vector(-1, -1), vector(1, 1)) ≈ T(π) + @test ∠(vector(-2, 0), vector(2, 0)) ≈ T(π) # angles between 3D vectors - @test ∠(V3(0, 0, 1), V3(1, 1, 0)) ≈ T(π / 2) - @test ∠(V3(1, 0, 1), V3(1, 1, 0)) ≈ T(π / 3) - @test ∠(V3(-1, -1, 0), V3(1, 1, 0)) ≈ T(π) - @test ∠(V3(0, -1, -1), V3(0, 1, 1)) ≈ T(π) - @test ∠(V3(0, -1, -1), V3(0, 1, 0)) ≈ T(π * 3 / 4) - @test ∠(V3(0, 1, 1), V3(1, 1, 0)) ≈ T(π / 3) + @test ∠(vector(0, 0, 1), vector(1, 1, 0)) ≈ T(π / 2) + @test ∠(vector(1, 0, 1), vector(1, 1, 0)) ≈ T(π / 3) + @test ∠(vector(-1, -1, 0), vector(1, 1, 0)) ≈ T(π) + @test ∠(vector(0, -1, -1), vector(0, 1, 1)) ≈ T(π) + @test ∠(vector(0, -1, -1), vector(0, 1, 0)) ≈ T(π * 3 / 4) + @test ∠(vector(0, 1, 1), vector(1, 1, 0)) ≈ T(π / 3) - v = V2(0, 1) - @test sprint(show, v, context=:compact => true) == "(0.0, 1.0)" + v = vector(0, 1) + @test sprint(show, v, context=:compact => true) == "(0.0 m, 1.0 m)" if T === Float32 - @test sprint(show, v) == "Vec(0.0f0, 1.0f0)" + @test sprint(show, v) == "Vec(0.0f0 m, 1.0f0 m)" else - @test sprint(show, v) == "Vec(0.0, 1.0)" + @test sprint(show, v) == "Vec(0.0 m, 1.0 m)" end @test sprint(show, MIME("text/plain"), v) == sprint(show, v) end diff --git a/test/viewing.jl b/test/viewing.jl index 38f340527..bb3ab22cf 100644 --- a/test/viewing.jl +++ b/test/viewing.jl @@ -1,124 +1,124 @@ @testset "Viewing" begin - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) v = view(g, 1:3) @test parent(v) == g @test parentindices(v) == 1:3 @test parent(g) == g @test parentindices(g) == 1:100 - g = CartesianGrid{T}(10, 10) - b = Box(P2(1, 1), P2(5, 5)) + g = cartgrid(10, 10) + b = Box(point(1, 1), point(5, 5)) v = view(g, b) - @test v == CartesianGrid(P2(0, 0), P2(6, 6), dims=(6, 6)) + @test v == CartesianGrid(point(0, 0), point(6, 6), dims=(6, 6)) p = PointSet(collect(vertices(g))) v = view(p, b) - @test centroid(v, 1) == P2(1, 1) - @test centroid(v, nelements(v)) == P2(5, 5) + @test centroid(v, 1) == point(1, 1) + @test centroid(v, nelements(v)) == point(5, 5) - g = CartesianGrid{T}(10, 10) + g = cartgrid(10, 10) p = PointSet(collect(vertices(g))) - b = Ball(P2(0, 0), T(2)) + b = Ball(point(0, 0), T(2)) v = view(g, b) @test nelements(v) == 4 @test v[1] == g[1] v = view(p, b) @test nelements(v) == 6 - @test coordinates.(v) == V2[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)] + @test coordinates.(v) == vector.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)]) # convex polygons - tri = Triangle(P2(5, 7), P2(10, 12), P2(15, 7)) - pent = Pentagon(P2(6, 1), P2(2, 10), P2(10, 16), P2(18, 10), P2(14, 1)) + tri = Triangle(point(5, 7), point(10, 12), point(15, 7)) + pent = Pentagon(point(6, 1), point(2, 10), point(10, 16), point(18, 10), point(14, 1)) - grid = CartesianGrid{T}(20, 20) + grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[10, 10] ∈ indices(grid, tri) @test linds[10, 6] ∈ indices(grid, pent) - grid = CartesianGrid(P2(-2, -2), P2(20, 20), T.((0.5, 1.5))) + grid = CartesianGrid(point(-2, -2), point(20, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[21, 7] ∈ indices(grid, tri) @test linds[21, 4] ∈ indices(grid, pent) - grid = CartesianGrid(P2(-100, -100), P2(20, 20), T.((2, 2))) + grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[57, 54] ∈ indices(grid, tri) @test linds[55, 53] ∈ indices(grid, pent) # non-convex polygons - poly1 = PolyArea(P2[(3, 3), (9, 9), (3, 15), (17, 15), (17, 3)]) + poly1 = PolyArea(point.([(3, 3), (9, 9), (3, 15), (17, 15), (17, 3)])) poly2 = PolyArea([pointify(pent), pointify(tri)]) - grid = CartesianGrid{T}(20, 20) + grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[12, 6] ∈ indices(grid, poly1) @test linds[10, 3] ∈ indices(grid, poly2) - grid = CartesianGrid(P2(-2, -2), P2(20, 20), T.((0.5, 1.5))) + grid = CartesianGrid(point(-2, -2), point(20, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[22, 6] ∈ indices(grid, poly1) @test linds[17, 4] ∈ indices(grid, poly2) - grid = CartesianGrid(P2(-100, -100), P2(20, 20), T.((2, 2))) + grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[57, 54] ∈ indices(grid, poly1) @test linds[55, 53] ∈ indices(grid, poly2) # rotate - poly1 = poly1 |> Rotate(Angle2d(π / 2)) - poly2 = poly2 |> Rotate(Angle2d(π / 2)) + poly1 = poly1 |> Rotate(Angle2d(T(π / 2))) + poly2 = poly2 |> Rotate(Angle2d(T(π / 2))) - grid = CartesianGrid(P2(-20, 0), P2(0, 20), T.((1, 1))) + grid = CartesianGrid(point(-20, 0), point(0, 20), T.((1, 1))) linds = LinearIndices(size(grid)) @test linds[12, 12] ∈ indices(grid, poly1) @test linds[16, 11] ∈ indices(grid, poly2) - grid = CartesianGrid(P2(-22, -2), P2(0, 20), T.((0.5, 1.5))) + grid = CartesianGrid(point(-22, -2), point(0, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[26, 8] ∈ indices(grid, poly1) @test linds[36, 9] ∈ indices(grid, poly2) - grid = CartesianGrid(P2(-100, -100), P2(20, 20), T.((2, 2))) + grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[46, 57] ∈ indices(grid, poly1) @test linds[48, 55] ∈ indices(grid, poly2) # multi multi = Multi([tri, pent]) - grid = CartesianGrid{T}(20, 20) + grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[10, 10] ∈ indices(grid, multi) @test linds[10, 6] ∈ indices(grid, multi) # clipping - tri = Triangle(P2(-4, 10), P2(5, 19), P2(5, 1)) - grid = CartesianGrid{T}(20, 20) + tri = Triangle(point(-4, 10), point(5, 19), point(5, 1)) + grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[3, 10] ∈ indices(grid, tri) # out of grid - tri = Triangle(P2(-12, 8), P2(-8, 14), P2(-4, 8)) - grid = CartesianGrid{T}(20, 20) + tri = Triangle(point(-12, 8), point(-8, 14), point(-4, 8)) + grid = cartgrid(20, 20) @test isempty(indices(grid, tri)) # chain - seg = Segment(P2(2, 12), P2(16, 18)) - rope = Rope(P2(8, 1), P2(5, 9), P2(9, 13), P2(17, 10)) - ring = Ring(P2(8, 1), P2(5, 9), P2(9, 13), P2(17, 10)) - grid = CartesianGrid{T}(20, 20) + seg = Segment(point(2, 12), point(16, 18)) + rope = Rope(point(8, 1), point(5, 9), point(9, 13), point(17, 10)) + ring = Ring(point(8, 1), point(5, 9), point(9, 13), point(17, 10)) + grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[9, 15] ∈ indices(grid, seg) @test linds[7, 11] ∈ indices(grid, rope) @test linds[12, 5] ∈ indices(grid, ring) # points - p1 = P2(0, 0) - p2 = P2(0.5, 0.5) - p3 = P2(1, 1) - p4 = P2(2, 2) - p5 = P2(10, 10) - p6 = P2(11, 11) - grid = CartesianGrid{T}(10, 10) + p1 = point(0, 0) + p2 = point(0.5, 0.5) + p3 = point(1, 1) + p4 = point(2, 2) + p5 = point(10, 10) + p6 = point(11, 11) + grid = cartgrid(10, 10) linds = LinearIndices(size(grid)) @test linds[1, 1] == only(indices(grid, p1)) @test linds[1, 1] == only(indices(grid, p2)) diff --git a/test/winding.jl b/test/winding.jl index 95235a759..542ce6ff7 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -1,18 +1,18 @@ @testset "winding" begin - p = P2(0.5, 0.5) - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1)]) + p = point(0.5, 0.5) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test winding(p, c) ≈ T(1) @test winding(p, reverse(c)) ≈ T(-1) @test winding([p, p], c) ≈ T[1, 1] - p = P2(0.5, 0.5) - c = Ring(P2[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)]) + p = point(0.5, 0.5) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) @test winding(p, c) ≈ T(2) @test winding(p, reverse(c)) ≈ T(-2) @test winding([p, p], c) ≈ T[2, 2] - m = boundary(Box(P3(0, 0, 0), P3(2, 2, 2))) + m = boundary(Box(point(0, 0, 0), point(2, 2, 2))) @test all(>(0), winding(vertices(m), m)) - @test isapprox(winding(P3(1, 1, 1), m), T(1), atol=atol(T)) - @test isapprox(winding(P3(3, 3, 3), m), T(0), atol=atol(T)) + @test isapprox(winding(point(1, 1, 1), m), T(1), atol=atol(T)) + @test isapprox(winding(point(3, 3, 3), m), T(0), atol=atol(T)) end From 09ccda1757d44010c644da31a0c246f38a4f6f5f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 20 May 2024 09:20:35 -0300 Subject: [PATCH 042/423] Normalize the normal vector of the 3D triangle (#840) --- src/polytopes/ngon.jl | 2 +- test/polytopes.jl | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 6eebd69fb..b112d8712 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -83,7 +83,7 @@ signarea(::Triangle{3}) = error("signed area only defined for triangles embedded function normal(t::Triangle{3}) A, B, C = t.vertices - ucross((B - A), (C - A)) / 2 + unormalize(ucross((B - A), (C - A))) end function (t::Triangle)(u, v) diff --git a/test/polytopes.jl b/test/polytopes.jl index ce8b1a1c5..471f2e80b 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -374,9 +374,11 @@ @test t(T(0.5), T(0.5)) == point(0, 0.5, 0.5) @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) - @test isapprox(normal(t), vector(0.5, 0, 0)) + @test isapprox(normal(t), vector(1, 0, 0)) + @test isapprox(norm(normal(t)), oneunit(ℳ)) t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) - @test isapprox(normal(t), vector(0, -2, 2)) + @test isapprox(normal(t), vector(0, -0.7071067811865475, 0.7071067811865475)) + @test isapprox(norm(normal(t)), oneunit(ℳ)) t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) @@ -688,9 +690,9 @@ @test m isa Mesh @test nvertices(m) == 4 @test nelements(m) == 4 - @test n[1] == vector(0, 0, -0.5) - @test n[2] == vector(0, -0.5, 0) - @test n[3] == vector(-0.5, 0, 0) + @test n[1] == vector(0, 0, -1) + @test n[2] == vector(0, -1, 0) + @test n[3] == vector(-1, 0, 0) @test all(>(T(0) * u"m"), n[4]) @test t(T(0), T(0), T(0)) ≈ point(0, 0, 0) @test t(T(1), T(0), T(0)) ≈ point(1, 0, 0) From 69cc7d2a84753160ef8a592e596428e5abe37af8 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 20 May 2024 10:48:02 -0300 Subject: [PATCH 043/423] Use 'Meshes.atol' in Partition Methods (#841) --- src/partitioning/direction.jl | 8 ++++---- src/partitioning/plane.jl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/partitioning/direction.jl b/src/partitioning/direction.jl index 347e46d1d..6a84b44f6 100644 --- a/src/partitioning/direction.jl +++ b/src/partitioning/direction.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - DirectionPartition(direction; tol=1e-6) + DirectionPartition(direction; [tol]) A method for partitioning spatial objects along a given `direction` with bandwidth tolerance `tol`. @@ -16,13 +16,13 @@ end DirectionPartition(direction::Vec, tol) = DirectionPartition(direction, addunit(tol, u"m")) -DirectionPartition(direction::Vec; tol=1e-6u"m") = DirectionPartition(direction, tol) +DirectionPartition(direction::Vec; tol=atol(eltype(direction))) = DirectionPartition(direction, tol) -DirectionPartition(direction::Tuple; tol=1e-6u"m") = DirectionPartition(Vec(direction), tol) +DirectionPartition(direction::Tuple; kwargs...) = DirectionPartition(Vec(direction); kwargs...) function (p::DirectionPartition)(x, y) δ = x - y d = p.direction - k = ustrip.(δ ⋅ d) + k = ustrip(δ ⋅ d) norm(δ - k * d) < p.tol end diff --git a/src/partitioning/plane.jl b/src/partitioning/plane.jl index 2cee1aa27..5e2399012 100644 --- a/src/partitioning/plane.jl +++ b/src/partitioning/plane.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - PlanePartition(normal; tol=1e-6) + PlanePartition(normal; [tol]) A method for partitioning spatial objects into a family of hyperplanes defined by a `normal` direction. Two points `x` and `y` belong to the same @@ -17,8 +17,8 @@ end PlanePartition(normal::Vec, tol) = PlanePartition(normal, addunit(tol, u"m")) -PlanePartition(normal::Vec; tol=1e-6u"m") = PlanePartition(normal, tol) +PlanePartition(normal::Vec; tol=atol(eltype(normal))) = PlanePartition(normal, tol) -PlanePartition(normal::Tuple; tol=1e-6u"m") = PlanePartition(Vec(normal), tol) +PlanePartition(normal::Tuple; kwargs...) = PlanePartition(Vec(normal); kwargs...) -(p::PlanePartition)(x, y) = abs((x - y) ⋅ p.normal) < p.tol^2 +(p::PlanePartition)(x, y) = abs(udot(x - y, p.normal)) < p.tol From 7cd16d3d9ac41396a6e89eaa1907be483bc82407 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 21 May 2024 08:23:02 -0300 Subject: [PATCH 044/423] Add units in: MetricBall, BallSampling, BlockSampling and MinDistanceSampling (#843) --- src/neighborhoods/metricball.jl | 27 +++++++++++++--------- src/neighborsearch/ball.jl | 2 +- src/neighborsearch/kball.jl | 2 +- src/sampling/ball.jl | 9 +++++--- src/sampling/block.jl | 9 ++++++-- src/sampling/mindistance.jl | 11 ++++++--- test/neighborhoods.jl | 40 ++++++++++++++++++--------------- 7 files changed, 61 insertions(+), 39 deletions(-) diff --git a/src/neighborhoods/metricball.jl b/src/neighborhoods/metricball.jl index bb0acd293..5f9d953c7 100644 --- a/src/neighborhoods/metricball.jl +++ b/src/neighborhoods/metricball.jl @@ -31,20 +31,25 @@ Axis-aligned 3D ellipsoid with radii `(3.0, 2.0, 1.0)`: julia> mahalanobis = MetricBall((3.0, 2.0, 1.0)) ``` """ -struct MetricBall{ℒ,R,M} <: Neighborhood - radii::ℒ +struct MetricBall{Dim,ℒ<:Len,R,M} <: Neighborhood + radii::SVector{Dim,ℒ} rotation::R # state fields metric::M + + MetricBall(radii::SVector{Dim,ℒ}, rotation::R, metric::M) where {Dim,ℒ<:Len,R,M} = + new{Dim,float(ℒ),R,M}(radii, rotation, metric) end -function MetricBall(radii::SVector{Dim,T}, rotation=default_rotation(Val{Dim}(), T)) where {Dim,T} +MetricBall(radii::SVector, rotation, metric) = MetricBall(addunit(radii, u"m"), rotation, metric) + +function MetricBall(radii::SVector{Dim,ℒ}, rotation=nothing) where {Dim,ℒ<:Len} # scaling matrix - Λ = Diagonal(one(T) ./ radii .^ 2) + Λ = Diagonal((oneunit(ℒ) ./ radii) .^ 2) # rotation matrix - R = rotation + R = isnothing(rotation) ? default_rotation(Val(Dim), numtype(ℒ)) : rotation # anisotropy matrix M = Symmetric(R * Λ * R') @@ -52,17 +57,17 @@ function MetricBall(radii::SVector{Dim,T}, rotation=default_rotation(Val{Dim}(), # Mahalanobis metric metric = Mahalanobis(M) - MetricBall(radii, rotation, metric) + MetricBall(radii, R, metric) end -MetricBall(radii::NTuple{Dim,T}, rotation=default_rotation(Val{Dim}(), T)) where {Dim,T} = - MetricBall(SVector(radii), rotation) +MetricBall(radii::SVector, rotation=nothing) = MetricBall(addunit(radii, u"m"), rotation) + +MetricBall(radii::Tuple, rotation=nothing) = MetricBall(SVector(radii), rotation) # avoid silent calls to inner constructor -MetricBall(radii::AbstractVector{T}, rotation=default_rotation(Val{length(radii)}(), T)) where {T} = - MetricBall(SVector{length(radii),T}(radii), rotation) +MetricBall(radii::AbstractVector{T}, rotation=nothing) where {T} = MetricBall(SVector{length(radii),T}(radii), rotation) -MetricBall(radius::T, metric=Euclidean()) where {T<:Number} = MetricBall(SVector(radius), nothing, metric) +MetricBall(radius::Number, metric=Euclidean()) = MetricBall(SVector(radius), nothing, metric) default_rotation(::Val{2}, T) = one(Angle2d{T}) default_rotation(::Val{3}, T) = one(QuatRotation{T}) diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index 71cca8969..58872284c 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -29,7 +29,7 @@ function search(pₒ::Point, method::BallSearch; mask=nothing) tree = method.tree dmax = radius(method.ball) - inds = inrange(tree, ustrip.(coordinates(pₒ)), dmax) + inds = inrange(tree, ustrip.(coordinates(pₒ)), ustrip(dmax)) if isnothing(mask) inds diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 7af409e20..9db95ea25 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -38,7 +38,7 @@ function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; ma inds, dists = knn(tree, ustrip.(coordinates(pₒ)), k, true) # keep neighbors inside ball - keep = dists .≤ dmax + keep = dists .≤ ustrip(dmax) # possibly mask some of the neighbors isnothing(mask) || (keep .*= mask[inds]) diff --git a/src/sampling/ball.jl b/src/sampling/ball.jl index 625777200..1874f24b8 100644 --- a/src/sampling/ball.jl +++ b/src/sampling/ball.jl @@ -13,13 +13,16 @@ according to a norm-ball of given `radius`. * `metric` - Metric for the ball (default to `Euclidean()`) * `maxsize` - Maximum size of the resulting sample (default to none) """ -struct BallSampling{T,M} <: DiscreteSamplingMethod - radius::T +struct BallSampling{ℒ<:Len,M} <: DiscreteSamplingMethod + radius::ℒ metric::M maxsize::Union{Int,Nothing} + BallSampling(radius::ℒ, metric::M, maxsize) where {ℒ<:Len,M} = new{float(ℒ),M}(radius, metric, maxsize) end -BallSampling(radius; metric=Euclidean(), maxsize=nothing) = BallSampling(radius, metric, maxsize) +BallSampling(radius::Len; metric=Euclidean(), maxsize=nothing) = BallSampling(radius, metric, maxsize) + +BallSampling(radius; kwargs...) = BallSampling(addunit(radius, u"m"); kwargs...) function sampleinds(rng::AbstractRNG, d::Domain, method::BallSampling) radius = method.radius diff --git a/src/sampling/block.jl b/src/sampling/block.jl index 5bce56a83..7238926ee 100644 --- a/src/sampling/block.jl +++ b/src/sampling/block.jl @@ -12,10 +12,15 @@ A method for sampling objects that are `sides` apart using a Alternatively, specify the sides `side₁`, `side₂`, ..., `sideₙ`. """ -struct BlockSampling{S} <: DiscreteSamplingMethod - sides::S +struct BlockSampling{Dim,ℒ<:Len} <: DiscreteSamplingMethod + sides::NTuple{Dim,ℒ} + BlockSampling(sides::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} = new{Dim,float(ℒ)}(sides) end +BlockSampling(sides::NTuple{Dim,Len}) where {Dim} = BlockSampling(promote(sides...)) + +BlockSampling(sides::Tuple) = BlockSampling(addunit.(sides, u"m")) + BlockSampling(sides...) = BlockSampling(sides) function sampleinds(::AbstractRNG, d::Domain, method::BlockSampling) diff --git a/src/sampling/mindistance.jl b/src/sampling/mindistance.jl index f87177992..b95b22685 100644 --- a/src/sampling/mindistance.jl +++ b/src/sampling/mindistance.jl @@ -24,13 +24,18 @@ or blue noise sampling in the computer graphics community. * Medeiros et al. 2014. [Fast adaptive blue noise on polygonal surfaces] (https://www.sciencedirect.com/science/article/abs/pii/S1524070313000313) """ -struct MinDistanceSampling{T,M} <: ContinuousSamplingMethod - α::T - ρ::T +struct MinDistanceSampling{ℒ<:Len,M} <: ContinuousSamplingMethod + α::ℒ + ρ::ℒ δ::Int metric::M + MinDistanceSampling(α::ℒ, ρ::ℒ, δ, metric::M) where {ℒ<:Len,M} = new{float(ℒ),M}(α, ρ, δ, metric) end +MinDistanceSampling(α::Len, ρ::Len, δ, metric) = MinDistanceSampling(promote(α, ρ)..., δ, metric) + +MinDistanceSampling(α, ρ, δ, metric) = MinDistanceSampling(addunit(α, u"m"), addunit(ρ, u"m"), δ, metric) + MinDistanceSampling(α::T; ρ=T(0.65), δ=100, metric=Euclidean()) where {T} = MinDistanceSampling(α, ρ, δ, metric) function sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) diff --git a/test/neighborhoods.jl b/test/neighborhoods.jl index 4a6019298..8e3dacf45 100644 --- a/test/neighborhoods.jl +++ b/test/neighborhoods.jl @@ -4,46 +4,50 @@ b = MetricBall(T(1 / 2)) r = radius(b) m = metric(b) - @test evaluate(m, T[0], T[0]) ≤ r - @test evaluate(m, T[0], T[1]) > r - @test radii(b) == T[1 / 2] + @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r + @test evaluate(m, T[0] * u"m", T[1] * u"m") > r + @test radii(b) == T[1 / 2] * u"m" b = MetricBall(T(1)) r = radius(b) m = metric(b) - @test evaluate(m, T[0, 0], T[0, 0]) ≤ r - @test evaluate(m, T[0, 0], T[1, 0]) ≤ r - @test evaluate(m, T[0, 0], T[0, 1]) ≤ r + @test evaluate(m, T[0, 0] * u"m", T[0, 0] * u"m") ≤ r + @test evaluate(m, T[0, 0] * u"m", T[1, 0] * u"m") ≤ r + @test evaluate(m, T[0, 0] * u"m", T[0, 1] * u"m") ≤ r @test isisotropic(b) - @test sprint(show, b) == "MetricBall(1.0, Euclidean)" + if T === Float32 + @test sprint(show, b) == "MetricBall(1.0f0 m, Euclidean)" + else + @test sprint(show, b) == "MetricBall(1.0 m, Euclidean)" + end # Chebyshev metric b = MetricBall(T(1 / 2), Chebyshev()) r = radius(b) m = metric(b) - @test evaluate(m, T[0], T[0]) ≤ r - @test evaluate(m, T[0], T[1]) > r + @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r + @test evaluate(m, T[0] * u"m", T[1] * u"m") > r - for r in [1.0, 2.0, 3.0, 4.0, 5.0] + for r in T[1.0, 2.0, 3.0, 4.0, 5.0] b = MetricBall(r, Chebyshev()) r = radius(b) m = metric(b) - for i in 0.0:1.0:r, j in 0.0:1.0:r - @test evaluate(m, T[0, 0], T[i, j]) ≤ r + for i in zero(r):oneunit(r):r, j in zero(r):oneunit(r):r + @test evaluate(m, T[0, 0] * u"m", [i, j]) ≤ r end end # 2D simple test of default convention m = metric(MetricBall(T.((1, 1)))) - @test evaluate(m, T[1, 0], T[0, 0]) == evaluate(m, T[0, 1], T[0, 0]) + @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") == evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") m = metric(MetricBall(T.((1, 2)))) - @test evaluate(m, T[1, 0], T[0, 0]) != evaluate(m, T[0, 1], T[0, 0]) + @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") != evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") # 3D simple test of default convention m = metric(MetricBall(T.((1.0, 0.5, 0.5)), RotZYX(T(-π / 4), T(0), T(0)))) - @test evaluate(m, [1.0, 1.0, 0.0], [0.0, 0.0, 0.0]) ≈ √T(8) - @test evaluate(m, [-1.0, 1.0, 0.0], [0.0, 0.0, 0.0]) ≈ √T(2) + @test evaluate(m, T[1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(8) * u"m" + @test evaluate(m, T[-1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(2) * u"m" # make sure the correct constructor is called m = metric(MetricBall(T[1.0, 0.5, 0.2], RotXYX(T(0), T(0), T(0)))) @@ -52,8 +56,8 @@ # make sure the angle is clockwise m = metric(MetricBall(T[20.0, 5.0], Angle2d(T(π / 2)))) @test m isa Mahalanobis - @test evaluate(m, [1.0, 0.0], [0.0, 0.0]) ≈ T(0.2) - @test evaluate(m, [0.0, 1.0], [0.0, 0.0]) ≈ T(0.05) + @test evaluate(m, T[1.0, 0.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.2) * u"m" + @test evaluate(m, T[0.0, 1.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.05) * u"m" # basic multiplication @test 2MetricBall(T(1)) == MetricBall(T(2)) From adaeb9230e23a32c5782334e6f5eee186d3f882e Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 21 May 2024 10:38:23 -0300 Subject: [PATCH 045/423] Add helper functions: `ispositive`, `isnegative`, `isnonpositive`, `isnonnegative` (#844) * Add helper functions: ispositive, isnegative, isnonpositive, isnonnegative * Remove unused variables --- src/intersections/rays.jl | 5 ++--- src/partitioning/bisectpoint.jl | 2 +- src/predicates/in.jl | 19 ++++++++----------- src/predicates/intersects.jl | 16 +++++++--------- src/utils.jl | 7 ++++++- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/intersections/rays.jl b/src/intersections/rays.jl index aa3cd1285..013e06a6c 100644 --- a/src/intersections/rays.jl +++ b/src/intersections/rays.jl @@ -11,7 +11,6 @@ # 5. overlap with colliding vectors (NegOverlapping -> Segment) # 6. do not overlap nor intersect (NotIntersecting -> Nothing) function intersection(f, ray₁::Ray{Dim}, ray₂::Ray{Dim}) where {Dim} - ℒ = lentype(ray₁) a, b = ray₁(0), ray₁(1) c, d = ray₂(0), ray₂(1) @@ -28,8 +27,8 @@ function intersection(f, ray₁::Ray{Dim}, ray₂::Ray{Dim}) where {Dim} return @IT NotIntersecting nothing f #CASE 6 # collinear elseif r == rₐ == 1 - if (b - a) ⋅ (d - c) ≥ zero(ℒ)^2 # rays aligned in same direction - if (a - c) ⋅ (b - a) ≥ zero(ℒ)^2 # origin of ray₁ ∈ ray₂ + if isnonnegative((b - a) ⋅ (d - c)) # rays aligned in same direction + if isnonnegative((a - c) ⋅ (b - a)) # origin of ray₁ ∈ ray₂ return @IT PosOverlapping ray₁ f # CASE 4: ray₁ else return @IT PosOverlapping ray₂ f # CASE 4: ray₂ diff --git a/src/partitioning/bisectpoint.jl b/src/partitioning/bisectpoint.jl index 7dd3bba16..676502820 100644 --- a/src/partitioning/bisectpoint.jl +++ b/src/partitioning/bisectpoint.jl @@ -27,7 +27,7 @@ function partitioninds(::AbstractRNG, domain::Domain, method::BisectPointPartiti left, right = Int[], Int[] for location in 1:nelements(domain) pₒ = centroid(domain, location) - if (pₒ - p) ⋅ n < zero(lentype(domain))^2 + if isnegative((pₒ - p) ⋅ n) push!(left, location) else push!(right, location) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 753860b91..0d278bfd8 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -16,10 +16,10 @@ function Base.in(p::Point{Dim}, s::Segment{Dim}) where {Dim} # segment ab if and only if vectors satisfy 0 ≤ ap ⋅ ab ≤ ||ab||² a, b = vertices(s) ab, ap = b - a, p - a - iscollinear(a, b, p) && zero(lentype(p))^2 ≤ ab ⋅ ap ≤ ab ⋅ ab + iscollinear(a, b, p) && (abap = ab ⋅ ap; isnonnegative(abap) && abap ≤ ab ⋅ ab) end -Base.in(p::Point, r::Ray) = p ∈ Line(r(0), r(1)) && (p - r(0)) ⋅ (r(1) - r(0)) ≥ zero(lentype(p))^2 +Base.in(p::Point, r::Ray) = p ∈ Line(r(0), r(1)) && isnonnegative((p - r(0)) ⋅ (r(1) - r(0))) function Base.in(p::Point, l::Line) w = norm(l(1) - l(0)) @@ -64,33 +64,30 @@ function Base.in(p::Point{3}, c::Circle) end function Base.in(p::Point{3}, c::Cone) - z = zero(lentype(p))^2 a = apex(c) b = center(base(c)) ax = a - b - (a - p) ⋅ ax ≥ z || return false - (b - p) ⋅ ax ≤ z || return false + isnonnegative((a - p) ⋅ ax) || return false + isnonpositive((b - p) ⋅ ax) || return false ∠(b, a, p) ≤ halfangle(c) end function Base.in(p::Point{3}, c::Cylinder) - z = zero(lentype(p))^2 b = bottom(c)(0, 0) t = top(c)(0, 0) r = radius(c) a = t - b - (p - b) ⋅ a ≥ z || return false - (p - t) ⋅ a ≤ z || return false + isnonnegative((p - b) ⋅ a) || return false + isnonpositive((p - t) ⋅ a) || return false norm((p - b) × a) / norm(a) ≤ r end function Base.in(p::Point{3}, f::Frustum) - z = zero(lentype(p))^2 t = center(top(f)) b = center(bottom(f)) ax = b - t - (p - t) ⋅ ax ≥ z || return false - (p - b) ⋅ ax ≤ z || return false + isnonnegative((p - t) ⋅ ax) || return false + isnonpositive((p - b) ⋅ ax) || return false # axial distance of p ad = (p - t) ⋅ normalize(ax) adrel = ad / norm(ax) diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index a7dd283de..27e2c1e89 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -97,7 +97,7 @@ function intersects(g₁::Geometry{Dim}, g₂::Geometry{Dim}) where {Dim} d = O - P while true P = minkowskipoint(g₁, g₂, d) - if (P - O) ⋅ d < zero(ℒ)^2 + if isnegative((P - O) ⋅ d) return false end push!(points, P) @@ -124,7 +124,6 @@ See also [`intersects`](@ref). function gjk! end function gjk!(O::Point{2}, points) - ℒ = lentype(O) # line segment case if length(points) == 2 B, A = points @@ -139,10 +138,10 @@ function gjk!(O::Point{2}, points) AO = O - A ABᵀ = -perphint(AB, AC) ACᵀ = -perphint(AC, AB) - if ABᵀ ⋅ AO > zero(ℒ)^2 + if ispositive(ABᵀ ⋅ AO) popat!(points, 1) # pop C d = ABᵀ - elseif ACᵀ ⋅ AO > zero(ℒ)^2 + elseif ispositive(ACᵀ ⋅ AO) popat!(points, 2) # pop B d = ACᵀ else @@ -153,7 +152,6 @@ function gjk!(O::Point{2}, points) end function gjk!(O::Point{3}, points) - ℒ = lentype(O) # line segment case if length(points) == 2 B, A = points @@ -167,7 +165,7 @@ function gjk!(O::Point{3}, points) AC = C - A AO = O - A ABCᵀ = ucross(AB, AC) - if ABCᵀ ⋅ AO < zero(ℒ)^2 + if isnegative(ABCᵀ ⋅ AO) points[1], points[2] = points[2], points[1] ABCᵀ = -ABCᵀ end @@ -192,13 +190,13 @@ function gjk!(O::Point{3}, points) ABCᵀ = ucross(AB, AC) ADBᵀ = ucross(AD, AB) ACDᵀ = ucross(AC, AD) - if ABCᵀ ⋅ AO > zero(ℒ)^2 + if ispositive(ABCᵀ ⋅ AO) popat!(points, 1) # pop D d = ABCᵀ - elseif ADBᵀ ⋅ AO > zero(ℒ)^2 + elseif ispositive(ADBᵀ ⋅ AO) popat!(points, 2) # pop C d = ADBᵀ - elseif ACDᵀ ⋅ AO > zero(ℒ)^2 + elseif ispositive(ACDᵀ ⋅ AO) popat!(points, 3) # pop B d = ACDᵀ else diff --git a/src/utils.jl b/src/utils.jl index a887a3e77..a086af82f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -77,7 +77,7 @@ function svdbasis(p::AbstractVector{<:Point{3}}) u = Vec(U[:, 1]...) v = Vec(U[:, 2]...) n = Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)) - (u × v) ⋅ n < zero(ℒ)^3 ? (v, u) : (u, v) + isnegative((u × v) ⋅ n) ? (v, u) : (u, v) end """ @@ -161,6 +161,11 @@ isapproxequal(x, y) = isapprox(x, y, atol=atol(x)) isapproxzero(x) = isapprox(x, zero(x), atol=atol(x)) isapproxone(x) = isapprox(x, oneunit(x), atol=atol(x)) +ispositive(x) = x > zero(x) +isnegative(x) = x < zero(x) +isnonpositive(x) = x ≤ zero(x) +isnonnegative(x) = x ≥ zero(x) + # Function wrappers that handle units # The result units of some operations, such as dot and cross, # are treated in a special way to handle Meshes.jl use cases From 9b4576e878cfdc8b29d26b86adcc6612f2cb73fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 11:15:14 -0300 Subject: [PATCH 046/423] :robot: Format .jl files (#845) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/predicates/in.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 0d278bfd8..12989fa5b 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -16,7 +16,8 @@ function Base.in(p::Point{Dim}, s::Segment{Dim}) where {Dim} # segment ab if and only if vectors satisfy 0 ≤ ap ⋅ ab ≤ ||ab||² a, b = vertices(s) ab, ap = b - a, p - a - iscollinear(a, b, p) && (abap = ab ⋅ ap; isnonnegative(abap) && abap ≤ ab ⋅ ab) + iscollinear(a, b, p) && (abap = ab ⋅ ap; + isnonnegative(abap) && abap ≤ ab ⋅ ab) end Base.in(p::Point, r::Ray) = p ∈ Line(r(0), r(1)) && isnonnegative((p - r(0)) ⋅ (r(1) - r(0))) From 0871e24a50a543b8594a3e64bf9a59278679f8b4 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 22 May 2024 09:06:53 -0300 Subject: [PATCH 047/423] Refactor: Add the `to` function and return a `CRS` in the `coordinates` function (#846) * Refactor: Add the 'fromorigin' function and return a 'CRS' in the 'coordinates' function * fromorigin -> to * Apply suggestions --- ext/geometryset.jl | 4 +-- ext/grid.jl | 4 +-- ext/grid/cartesian.jl | 2 +- ext/grid/structured.jl | 4 +-- ext/simplemesh.jl | 8 +++--- ext/subcartesiangrid.jl | 2 +- src/Meshes.jl | 1 + src/boundary.jl | 8 +++--- src/boundingboxes.jl | 10 ++++---- src/clamping.jl | 6 ++--- src/complement.jl | 2 +- src/discretization/dehn.jl | 2 +- src/distances.jl | 2 +- src/domains.jl | 4 +-- src/hulls/graham.jl | 4 +-- src/hulls/jarvis.jl | 2 +- src/intersections/boxes.jl | 8 +++--- src/intersections/planes.jl | 6 ++--- src/intersections/rays.jl | 4 +-- src/mesh/cartesiangrid.jl | 2 +- src/mesh/rectilineargrid.jl | 2 +- src/multigeoms.jl | 4 +-- src/neighborsearch/ball.jl | 4 +-- src/neighborsearch/kball.jl | 4 +-- src/neighborsearch/knearest.jl | 4 +-- src/partitioning.jl | 4 +-- src/partitioning/bisectfraction.jl | 4 +-- src/partitioning/block.jl | 4 +-- src/polytopes.jl | 4 +-- src/polytopes/hexahedron.jl | 20 ++++++++------- src/polytopes/ngon.jl | 8 +++--- src/polytopes/segment.jl | 2 +- src/polytopes/tetrahedron.jl | 4 +-- src/predicates/in.jl | 8 +++--- src/predicates/intersects.jl | 2 +- src/primitives/bezier.jl | 6 ++--- src/primitives/box.jl | 2 +- src/primitives/circle.jl | 8 +++--- src/primitives/cylindersurface.jl | 8 +++--- src/primitives/point.jl | 23 ++++++++++------- src/refinement/catmullclark.jl | 18 +++++++------- src/refinement/quad.jl | 8 +++--- src/refinement/regular.jl | 4 +-- src/refinement/tri.jl | 4 +-- src/sets.jl | 2 +- src/sorting/direction.jl | 2 +- src/transforms.jl | 2 +- src/transforms/affine.jl | 2 +- src/transforms/repair.jl | 2 +- src/transforms/smoothing.jl | 2 +- src/transforms/stdcoords.jl | 2 +- src/transforms/stretch.jl | 2 +- src/traversing/source.jl | 4 +-- src/utils.jl | 2 +- src/vectors.jl | 11 ++++++++ test/discretization.jl | 6 ++--- test/partitioning.jl | 8 +++--- test/primitives.jl | 6 ++--- test/sampling.jl | 40 +++++++++++++++--------------- test/viewing.jl | 2 +- 60 files changed, 179 insertions(+), 160 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 23139c5e3..7ed33d11f 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -48,7 +48,7 @@ function Makie.plot!(plot::Viz{<:Tuple{PointSet}}) # get geometries and coordinates geoms = Makie.@lift parent($pset) - coords = Makie.@lift map(g -> ustrip.(coordinates(g)), $geoms) + coords = Makie.@lift map(g -> ustrip.(to(g)), $geoms) # visualize point set Makie.scatter!(plot, coords, color=colorant, markersize=pointsize, overdraw=true) @@ -157,6 +157,6 @@ function asmakie(poly::Polygon) end end -asmakie(p::Point{Dim}) where {Dim} = Makie.Point{Dim}(ustrip.(Tuple(coordinates(p)))) +asmakie(p::Point{Dim}) where {Dim} = Makie.Point{Dim}(ustrip.(Tuple(to(p)))) asmakie(v::Vec{Dim}) where {Dim} = Makie.Vec{Dim}(ustrip.(Tuple(v))) diff --git a/ext/grid.jl b/ext/grid.jl index 2b1da0f6f..7e7752521 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -40,8 +40,8 @@ function Makie.data_limits(plot::Viz{<:Tuple{Grid}}) Makie.Rect3f([pmin, pmax]) end -aspoint3f(p::Point{2}) = Makie.Point3f(ustrip.(coordinates(p))..., 0) -aspoint3f(p::Point{3}) = Makie.Point3f(ustrip.(coordinates(p))...) +aspoint3f(p::Point{2}) = Makie.Point3f(ustrip.(to(p))..., 0) +aspoint3f(p::Point{3}) = Makie.Point3f(ustrip.(to(p))...) # ---------------- # SPECIALIZATIONS diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index 554bd467e..ad9809edd 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -20,7 +20,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 # origin, spacing and size of grid - or = Makie.@lift ustrip.(coordinates(minimum($grid))) + or = Makie.@lift ustrip.(to(minimum($grid))) sp = Makie.@lift ustrip.(spacing($grid)) sz = Makie.@lift size($grid) diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index eec861c39..020455653 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -47,7 +47,7 @@ function structuredsegments(grid) for j in axes(cinds, 2) for i in axes(cinds, 1) p = vertex(grid, cinds[i, j]) - c = ustrip.(Tuple(coordinates(p))) + c = ustrip.(Tuple(to(p))) push!(coords, c) end push!(coords, (NaN, NaN)) @@ -56,7 +56,7 @@ function structuredsegments(grid) for i in axes(cinds, 1) for j in axes(cinds, 2) p = vertex(grid, cinds[i, j]) - c = ustrip.(Tuple(coordinates(p))) + c = ustrip.(Tuple(to(p))) push!(coords, c) end push!(coords, (NaN, NaN)) diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index ad02295f3..16825a490 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -72,7 +72,7 @@ function vizmesh2D!(plot) elems = elements(topo) # coordinates of vertices - coords = map(p -> ustrip.(coordinates(p)), verts) + coords = map(p -> ustrip.(to(p)), verts) # fan triangulation (assume convexity) tris4elem = map(elems) do elem @@ -153,7 +153,7 @@ function vizmesh2D!(plot) topo = topology($mesh) nvert = nvertices($mesh) verts = vertices($mesh) - coords = map(p -> ustrip.(coordinates(p)), verts) + coords = map(p -> ustrip.(to(p)), verts) # use a sophisticated data structure # to extract the edges from the n-gons @@ -203,7 +203,7 @@ function segmentsof(topo, vert) T = Unitful.numtype(Meshes.lentype(p)) Dim = embeddim(p) nan = SVector(ntuple(i -> T(NaN), Dim)) - xs = map(p -> ustrip.(coordinates(p)), vert) + xs = map(p -> ustrip.(to(p)), vert) coords = map(elements(topo)) do e inds = indices(e) @@ -214,7 +214,7 @@ function segmentsof(topo, vert) end function segmentsof(topo::GridTopology, vert) - xs = map(p -> ustrip.(coordinates(p)), vert) + xs = map(p -> ustrip.(to(p)), vert) ip = first(isperiodic(topo)) ip ? [xs; [first(xs)]] : xs end diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index b87d192d3..233db603b 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -21,7 +21,7 @@ function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) sp = ustrip.(spacing(grid)) # coordinates of centroids - coord(e) = ustrip.(coordinates(centroid(e))) + coord(e) = ustrip.(to(centroid(e))) coords = [coord(e) .+ sp ./ 2 for e in $subgrid] # rectangle marker diff --git a/src/Meshes.jl b/src/Meshes.jl index 35fed8800..2a3b03bad 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -158,6 +158,7 @@ export Horner, DeCasteljau, coordinates, + to, radius, radii, plane, diff --git a/src/boundary.jl b/src/boundary.jl index 75c637560..17f1f37d4 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -26,15 +26,15 @@ boundary(::Plane) = nothing boundary(b::Box{1}) = Multi([minimum(b), maximum(b)]) function boundary(b::Box{2}) - A = coordinates(minimum(b)) - B = coordinates(maximum(b)) + A = to(minimum(b)) + B = to(maximum(b)) v = Point.([(A[1], A[2]), (B[1], A[2]), (B[1], B[2]), (A[1], B[2])]) Ring(v) end function boundary(b::Box{3}) - A = coordinates(minimum(b)) - B = coordinates(maximum(b)) + A = to(minimum(b)) + B = to(maximum(b)) v = Point.([ (A[1], A[2], A[3]), diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 98ddcff71..a65ff4bdf 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -34,9 +34,9 @@ function boundingbox(r::Ray) upper(p, v) = v > zero(v) ? typemax(p) : p p = r(0) v = r(1) - r(0) - l = lower.(coordinates(p), v) - u = upper.(coordinates(p), v) - Box(Point(l), Point(u)) + l = lower.(to(p), v) + u = upper.(to(p), v) + Box(Point(coordinates(l)), Point(coordinates(u))) end function boundingbox(s::Sphere{Dim}) where {Dim} @@ -94,9 +94,9 @@ function _pboxes(points) xmin = MVector(ntuple(i -> typemax(ℒ), Dim)) xmax = MVector(ntuple(i -> typemin(ℒ), Dim)) for p in points - x = coordinates(p) + x = to(p) @. xmin = min(x, xmin) @. xmax = max(x, xmax) end - Box(Point(Vec(xmin)), Point(Vec(xmax))) + Box(Point(coordinates(xmin)), Point(coordinates(xmax))) end diff --git a/src/clamping.jl b/src/clamping.jl index d1f954227..5ad5434a6 100644 --- a/src/clamping.jl +++ b/src/clamping.jl @@ -11,9 +11,9 @@ For each dimension, coordinates outside of the box are moved to the nearest edge of the box. The point and box must have an equal number of dimensions. """ function Base.clamp(point::Point{Dim}, box::Box{Dim}) where {Dim} - x = coordinates(point) - lo = coordinates(minimum(box)) - hi = coordinates(maximum(box)) + x = to(point) + lo = to(minimum(box)) + hi = to(maximum(box)) ntuple(Dim) do i clamp(x[i], lo[i], hi[i]) end |> Point diff --git a/src/complement.jl b/src/complement.jl index 5c1c5f31b..c8e692622 100644 --- a/src/complement.jl +++ b/src/complement.jl @@ -13,7 +13,7 @@ respect to its bounding box. function _boxboundary(g) ℒ = lentype(g) b = boundingbox(g) - c = coordinates(center(b)) + c = to(center(b)) l = sides(b) α = (l .+ 2atol(ℒ)) ./ l t = Translate(-c...) → Scale(α) → Translate(c...) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index 6735fe81c..1b860c95f 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -37,7 +37,7 @@ function dehn1899(v::AbstractVector{<:Point}, inds) if n > 3 # split chain # find lowerleft vertex - i = first(sortperm(coordinates.(v[I]))) + i = first(sortperm(to.(v[I]))) # left/right chains linds = (i - 1):(i + 1) diff --git a/src/distances.jl b/src/distances.jl index 2a7e71fd4..668115975 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -39,4 +39,4 @@ end Evaluate pre-metric between coordinates of `point1` and `point2`. """ -evaluate(d::PreMetric, p1::Point, p2::Point) = evaluate(d, coordinates(p1), coordinates(p2)) +evaluate(d::PreMetric, p1::Point, p2::Point) = evaluate(d, to(p1), to(p2)) diff --git a/src/domains.jl b/src/domains.jl index a11dd36cb..97ea93c5d 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -92,13 +92,13 @@ Return the centroid of the `domain`, i.e. the centroid of all its element's centroids. """ function centroid(d::Domain) - coords(i) = coordinates(centroid(d, i)) + coords(i) = to(centroid(d, i)) volume(i) = measure(element(d, i)) n = nelements(d) x = coords.(1:n) w = volume.(1:n) all(iszero, w) && (w = ones(eltype(w), n)) - Point(Vec(sum(w .* x) / sum(w))) + Point(coordinates(sum(w .* x) / sum(w))) end """ diff --git a/src/hulls/graham.jl b/src/hulls/graham.jl index a5238a18d..b1523f853 100644 --- a/src/hulls/graham.jl +++ b/src/hulls/graham.jl @@ -35,7 +35,7 @@ function hull(points, ::GrahamScan) n == 2 && return Segment(p[1], p[2]) # sort points lexicographically - p = p[sortperm(coordinates.(p))] + p = p[sortperm(to.(p))] # sort points by polar angle O = p[1] @@ -45,7 +45,7 @@ function hull(points, ::GrahamScan) q = q[sortperm(θ)] # skip collinear points at beginning - y(p) = coordinates(p)[2] + y(p) = to(p)[2] i = findfirst(qᵢ -> y(qᵢ) ≠ y(O), q) # all points are collinear, return segment diff --git a/src/hulls/jarvis.jl b/src/hulls/jarvis.jl index ba9391451..681aed148 100644 --- a/src/hulls/jarvis.jl +++ b/src/hulls/jarvis.jl @@ -35,7 +35,7 @@ function hull(points, ::JarvisMarch) n == 2 && return Segment(p[1], p[2]) # find bottom-left point - i = argmin(l -> coordinates(p[l]), 1:n) + i = argmin(l -> to(p[l]), 1:n) # candidates for next point 𝒞 = [1:(i - 1); (i + 1):n] diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 11cde1904..3e86f14a5 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -10,12 +10,12 @@ # 4. do not overlap nor intersect (NotIntersecting -> Nothing) function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} # retrieve corner points - m1, M1 = coordinates.(extrema(box₁)) - m2, M2 = coordinates.(extrema(box₂)) + m1, M1 = to.(extrema(box₁)) + m2, M2 = to.(extrema(box₂)) # relevant vertices - u = Point(max.(m1, m2)) - v = Point(min.(M1, M2)) + u = Point(coordinates(max.(m1, m2))) + v = Point(coordinates(min.(M1, M2))) # auxiliary variables δ = v - u diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index 1ac4b8385..fd053b53d 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -7,8 +7,8 @@ function intersection(f, plane1::Plane, plane2::Plane) u = unit(lentype(plane1)) n1 = ustrip.(normal(plane1)) n2 = ustrip.(normal(plane2)) - o1 = ustrip.(coordinates(plane1.p)) - o2 = ustrip.(coordinates(plane2.p)) + o1 = ustrip.(to(plane1.p)) + o2 = ustrip.(to(plane2.p)) n1n2 = n1 ⋅ n2 if isapproxone(abs(n1n2)) @@ -22,7 +22,7 @@ function intersection(f, plane1::Plane, plane2::Plane) c2 = (h2 - h1 * n1n2) / (1 - n1n2^2) p1 = (c1 * n1) + (c2 * n2) p2 = p1 + d - return @IT Intersecting Line(Point(Vec(p1 * u)), Point(Vec(p2 * u))) f + return @IT Intersecting Line(Point(coordinates(p1 * u)), Point(coordinates(p2 * u))) f end end diff --git a/src/intersections/rays.jl b/src/intersections/rays.jl index 013e06a6c..5489449cf 100644 --- a/src/intersections/rays.jl +++ b/src/intersections/rays.jl @@ -101,8 +101,8 @@ end function intersection(f, ray::Ray{Dim}, box::Box{Dim}) where {Dim} ℒ = lentype(ray) invdir = inv.(ray(1) - ray(0)) - lo, up = coordinates.(extrema(box)) - orig = coordinates(ray(0)) + lo, up = to.(extrema(box)) + orig = to(ray(0)) T = numtype(ℒ) tmin = zero(T) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 50c0e0499..fe38c8e71 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -136,7 +136,7 @@ offset(g::CartesianGrid) = g.offset function xyz(g::CartesianGrid{Dim}) where {Dim} dims = size(g) spac = spacing(g) - orig = coordinates(minimum(g)) + orig = to(minimum(g)) ntuple(Dim) do i o, s, d = orig[i], spac[i], dims[i] range(start=o, step=s, length=(d + 1)) diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 92ce07445..b69f2930d 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -52,7 +52,7 @@ function centroid(g::RectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) - Point((coordinates(p1) + coordinates(p2)) / 2) + Point(coordinates((to(p1) + to(p2)) / 2)) end function Base.getindex(g::RectilinearGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 5c7a51ce6..639f8659e 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -48,8 +48,8 @@ function Base.unique!(m::Multi) end function centroid(m::Multi) - cs = coordinates.(centroid.(m.geoms)) - Point(sum(cs) / length(cs)) + cs = to.(centroid.(m.geoms)) + Point(coordinates(sum(cs) / length(cs))) end rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index 58872284c..05d59bcca 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -18,7 +18,7 @@ end function BallSearch(domain::D, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) BallSearch{D,B,typeof(tree)}(domain, ball, tree) end @@ -29,7 +29,7 @@ function search(pₒ::Point, method::BallSearch; mask=nothing) tree = method.tree dmax = radius(method.ball) - inds = inrange(tree, ustrip.(coordinates(pₒ)), ustrip(dmax)) + inds = inrange(tree, ustrip.(to(pₒ)), ustrip(dmax)) if isnothing(mask) inds diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 9db95ea25..943c152d1 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -20,7 +20,7 @@ end function KBallSearch(domain::D, k::Int, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) KBallSearch{D,B,typeof(tree)}(domain, k, ball, tree) end @@ -35,7 +35,7 @@ function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; ma dmax = radius(method.ball) k = method.k - inds, dists = knn(tree, ustrip.(coordinates(pₒ)), k, true) + inds, dists = knn(tree, ustrip.(to(pₒ)), k, true) # keep neighbors inside ball keep = dists .≤ ustrip(dmax) diff --git a/src/neighborsearch/knearest.jl b/src/neighborsearch/knearest.jl index b2e93e37d..830927cec 100644 --- a/src/neighborsearch/knearest.jl +++ b/src/neighborsearch/knearest.jl @@ -18,7 +18,7 @@ struct KNearestSearch{D<:Domain,T} <: BoundedNeighborSearchMethod end function KNearestSearch(domain::D, k::Int; metric=Euclidean()) where {D<:Domain} - xs = [ustrip.(coordinates(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] tree = metric isa MinkowskiMetric ? KDTree(xs, metric) : BallTree(xs, metric) KNearestSearch{D,typeof(tree)}(domain, k, tree) end @@ -32,7 +32,7 @@ function searchdists!(neighbors, distances, pₒ::Point, method::KNearestSearch; tree = method.tree k = method.k - inds, dists = knn(tree, ustrip.(coordinates(pₒ)), k, true) + inds, dists = knn(tree, ustrip.(to(pₒ)), k, true) if isnothing(mask) nneigh = k diff --git a/src/partitioning.jl b/src/partitioning.jl index 447738640..1c848108c 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -70,11 +70,11 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::SPredicateParti subsets = Vector{Int}[] for i in randperm(rng, nelms) p = centroid(domain, i) - x = coordinates(p) + x = to(p) inserted = false for subset in subsets q = centroid(domain, subset[1]) - y = coordinates(q) + y = to(q) if method(x, y) push!(subset, i) inserted = true diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index 4156c4fa4..4febb43ab 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -27,7 +27,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP bbox = boundingbox(domain) n = method.normal f = method.fraction - c = coordinates(center(bbox)) + c = to(center(bbox)) d = diagonal(bbox) # maximum number of bisections @@ -41,7 +41,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP while iter < maxiter m = (a + b) / 2 - bisectpoint = BisectPointPartition(n, Point(m)) + bisectpoint = BisectPointPartition(n, Point(coordinates(m))) subsets, metadata = partitioninds(rng, domain, bisectpoint) g = length(subsets[1]) / nelements(domain) diff --git a/src/partitioning/block.jl b/src/partitioning/block.jl index 5c67a0606..ac5113b4b 100644 --- a/src/partitioning/block.jl +++ b/src/partitioning/block.jl @@ -43,7 +43,7 @@ function partitioninds(::AbstractRNG, domain::Domain, method::BlockPartition) nblocks = @. nleft + nright # top left corner of first block - start = coordinates(ce) .- nleft .* psides + start = to(ce) .- nleft .* psides subsets = [Int[] for i in 1:prod(nblocks)] @@ -51,7 +51,7 @@ function partitioninds(::AbstractRNG, domain::Domain, method::BlockPartition) linear = LinearIndices(Dims(nblocks)) for j in 1:nelements(domain) - coords = coordinates(centroid(domain, j)) + coords = to(centroid(domain, j)) # find block coordinates c = @. floor(Int, (coords - start) / psides) + 1 diff --git a/src/polytopes.jl b/src/polytopes.jl index 3cad87fa9..5f26e9069 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -88,7 +88,7 @@ Closed chains remain closed. function Base.unique!(c::Chain) # sort vertices lexicographically verts = vertices(open(c)) - perms = sortperm(coordinates.(verts)) + perms = sortperm(to.(verts)) # remove true duplicates keep = Int[] @@ -228,7 +228,7 @@ nvertices(p::Polytope) = nvertices(typeof(p)) Return the centroid of the `polytope`. """ -centroid(p::Polytope) = Point(sum(coordinates, vertices(p)) / length(vertices(p))) +centroid(p::Polytope) = Point(coordinates(sum(to, vertices(p)) / length(vertices(p)))) """ unique(polytope) diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index adf090700..67a695f72 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -18,16 +18,18 @@ function (h::Hexahedron)(u, v, w) if (u < 0 || u > 1) || (v < 0 || v > 1) || (w < 0 || w > 1) throw(DomainError((u, v, w), "h(u, v, w) is not defined for u, v, w outside [0, 1]³.")) end - A1, A2, A4, A3, A5, A6, A8, A7 = coordinates.(h.vertices) + A1, A2, A4, A3, A5, A6, A8, A7 = to.(h.vertices) Point( - (1 - u) * (1 - v) * (1 - w) * A1 + - u * (1 - v) * (1 - w) * A2 + - (1 - u) * v * (1 - w) * A3 + - u * v * (1 - w) * A4 + - (1 - u) * (1 - v) * w * A5 + - u * (1 - v) * w * A6 + - (1 - u) * v * w * A7 + - u * v * w * A8 + coordinates( + (1 - u) * (1 - v) * (1 - w) * A1 + + u * (1 - v) * (1 - w) * A2 + + (1 - u) * v * (1 - w) * A3 + + u * v * (1 - w) * A4 + + (1 - u) * (1 - v) * w * A5 + + u * (1 - v) * w * A6 + + (1 - u) * v * w * A7 + + u * v * w * A8 + ) ) end diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index b112d8712..6ec6a1c06 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -91,8 +91,8 @@ function (t::Triangle)(u, v) if (u < 0 || u > 1) || (v < 0 || v > 1) || (w < 0 || w > 1) throw(DomainError((u, v), "invalid barycentric coordinates for triangle.")) end - v₁, v₂, v₃ = coordinates.(t.vertices) - Point(v₁ * w + v₂ * u + v₃ * v) + v₁, v₂, v₃ = to.(t.vertices) + Point(coordinates(v₁ * w + v₂ * u + v₃ * v)) end # ------------ @@ -104,6 +104,6 @@ function (q::Quadrangle)(u, v) if (u < 0 || u > 1) || (v < 0 || v > 1) throw(DomainError((u, v), "q(u, v) is not defined for u, v outside [0, 1]².")) end - c₀₀, c₀₁, c₁₁, c₁₀ = coordinates.(q.vertices) - Point(c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v) + c₀₀, c₀₁, c₁₁, c₁₀ = to.(q.vertices) + Point(coordinates(c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v)) end diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index 0e683ccad..a13bdd1fe 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -23,7 +23,7 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] function center(s::Segment) a, b = extrema(s) - Point((coordinates(a) + coordinates(b)) / 2) + Point(coordinates((to(a) + to(b)) / 2)) end Base.isapprox(s₁::Segment, s₂::Segment; kwargs...) = diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index 29d042949..a44c8a296 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -19,8 +19,8 @@ function (t::Tetrahedron)(u, v, w) if (u < 0 || u > 1) || (v < 0 || v > 1) || (w < 0 || w > 1) || (z < 0 || z > 1) throw(DomainError((u, v, w), "invalid barycentric coordinates for tetrahedron.")) end - v₁, v₂, v₃, v₄ = coordinates.(t.vertices) - Point(v₁ * z + v₂ * u + v₃ * v + v₄ * w) + v₁, v₂, v₃, v₄ = to.(t.vertices) + Point(coordinates(v₁ * z + v₂ * u + v₃ * v + v₄ * w)) end Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Tetrahedron{Dim}}) where {Dim} = diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 12989fa5b..2e8c7adb9 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -113,10 +113,10 @@ end function Base.in(p::Point{2}, t::Triangle{2}) # given coordinates a, b, c = vertices(t) - x₁, y₁ = coordinates(a) - x₂, y₂ = coordinates(b) - x₃, y₃ = coordinates(c) - x, y = coordinates(p) + x₁, y₁ = to(a) + x₂, y₂ = to(b) + x₃, y₃ = to(c) + x, y = to(p) # barycentric coordinates λ₁ = ((y₂ - y₃) * (x - x₃) + (x₃ - x₂) * (y - y₃)) / ((y₂ - y₃) * (x₁ - x₃) + (x₃ - x₂) * (y₁ - y₃)) diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 27e2c1e89..411a1b9a2 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -245,7 +245,7 @@ intersects(m::Multi, c::Chain) = intersects(c, m) # ------------------ # support point in Minkowski difference -minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(supportfun(g₁, d) - supportfun(g₂, -d)) +minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(coordinates(supportfun(g₁, d) - supportfun(g₂, -d))) # origin of coordinate system minkowskiorigin(Dim, ℒ) = Point(ntuple(i -> zero(ℒ), Dim)) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 221c22bd4..94a9de9de 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -89,7 +89,7 @@ function (curve::BezierCurve)(t, ::Horner) cs = curve.controls t̄ = one(T) - t n = degree(curve) - pₙ = coordinates(last(cs)) + pₙ = to(last(cs)) aₙ = pₙ # initialization with i = n + 1, so bᵢ₋₁ = bₙ = aₙ @@ -98,14 +98,14 @@ function (curve::BezierCurve)(t, ::Horner) t̄ⁿ⁻ⁱ = one(T) for i in n:-1:1 cᵢ₋₁ *= i / (n - i + one(T)) - pᵢ₋₁ = coordinates(cs[i]) + pᵢ₋₁ = to(cs[i]) t̄ⁿ⁻ⁱ *= t̄ aᵢ₋₁ = cᵢ₋₁ * pᵢ₋₁ * t̄ⁿ⁻ⁱ bᵢ₋₁ = aᵢ₋₁ + bᵢ₋₁ * t end b₀ = bᵢ₋₁ - Point(b₀) + Point(coordinates(b₀)) end Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{BezierCurve{Dim}}) where {Dim} = diff --git a/src/primitives/box.jl b/src/primitives/box.jl index b40c5d105..8ff443453 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -39,7 +39,7 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -center(b::Box) = Point((coordinates(b.max) + coordinates(b.min)) / 2) +center(b::Box) = Point(coordinates((to(b.max) + to(b.min)) / 2)) diagonal(b::Box) = norm(b.max - b.min) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 50c5ca311..34dcd0552 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -28,13 +28,13 @@ A circle passing through points `p1`, `p2` and `p3`. function Circle(p1::Point{3}, p2::Point{3}, p3::Point{3}) v12 = p2 - p1 v13 = p3 - p1 - m12 = coordinates(p1 + v12 / 2) - m13 = coordinates(p1 + v13 / 2) + m12 = to(p1 + v12 / 2) + m13 = to(p1 + v13 / 2) n⃗ = normal(Plane(p1, p2, p3)) - F = coordinates(p1) ⋅ n⃗ + F = to(p1) ⋅ n⃗ M = transpose([n⃗ v12 v13]) u = [F, m12 ⋅ v12, m13 ⋅ v13] - O = Point(Vec(uinv(M) * u)) + O = Point(coordinates(uinv(M) * u)) r = norm(p1 - O) Circle(Plane(O, n⃗), r) end diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 0fe29cda0..0605468bd 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -65,9 +65,9 @@ bottom(c::CylinderSurface) = c.bot top(c::CylinderSurface) = c.top function center(c::CylinderSurface) - a = coordinates(c.bot(0, 0)) - b = coordinates(c.top(0, 0)) - Point((a .+ b) ./ 2) + a = to(c.bot(0, 0)) + b = to(c.top(0, 0)) + Point(coordinates((a .+ b) ./ 2)) end axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) @@ -120,7 +120,7 @@ function (c::CylinderSurface)(φ, z) pₜ = Point(rcφ, rsφ, zₜ) p = pᵦ + T(z) * (pₜ - pᵦ) - o + Q' * coordinates(p) + o + Q' * to(p) end Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{CylinderSurface}) = diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 427afd69c..6580359e8 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -39,9 +39,8 @@ struct Point{Dim,C<:CRS} <: Primitive{Dim} end # convenience constructors -Point(coords...) = Point(Cartesian(coords...)) -Point(coords::Tuple) = Point(Cartesian(coords...)) -Point(coords::Vec) = Point(Cartesian(Tuple(coords))) +Point(coords...) = Point(Cartesian(coords)) +Point(coords::Tuple) = Point(Cartesian(coords)) paramdim(::Type{<:Point}) = 0 @@ -57,10 +56,16 @@ Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) """ coordinates(point) -Return the coordinates of the `point` with respect to the -canonical Euclidean basis. +Return the coordinates of the `point`. """ -coordinates(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues(A.coords)) +coordinates(A::Point) = A.coords + +""" + to(point) + +Return the vector from the origin to the `point`. +""" +to(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues(A.coords)) """ -(A::Point, B::Point) @@ -68,7 +73,7 @@ coordinates(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues Return the [`Vec`](@ref) associated with the direction from point `B` to point `A`. """ --(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = coordinates(A) - coordinates(B) +-(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = to(A) - to(B) """ +(A::Point, v::Vec) @@ -77,7 +82,7 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(A) + v) ++(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(to(A) + v)) +(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A + v """ @@ -87,7 +92,7 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(A) - v) +-(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(to(A) - v)) -(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A - v """ diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index ba2dc54ce..77d4ad4ad 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -33,8 +33,8 @@ function refine(mesh, ::CatmullClark) ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) - cₒ = sum(coordinates, ps) / length(ps) - Point(cₒ) + cₒ = sum(to, ps) / length(ps) + Point(coordinates(cₒ)) end # add midpoints of edges @@ -43,10 +43,10 @@ function refine(mesh, ::CatmullClark) fpts = map(1:nfacets(t)) do edge ps = view(epts, ∂₁₂(edge)) qs = view(points, ∂₁₀(edge)) - ∑p = sum(coordinates, ps) - ∑q = sum(coordinates, qs) + ∑p = sum(to, ps) + ∑q = sum(to, qs) M = length(ps) + length(qs) - Point((∑p + ∑q) / M) + Point(coordinates((∑p + ∑q) / M)) end # move original vertices @@ -54,21 +54,21 @@ function refine(mesh, ::CatmullClark) ∂₀₀ = Adjacency{0}(t) vpts = map(1:nvertices(t)) do u # original point - P = coordinates(points[u]) + P = to(points[u]) # average of centroids ps = view(epts, ∂₀₂(u)) - F = sum(coordinates, ps) / length(ps) + F = sum(to, ps) / length(ps) # average of midpoints vs = ∂₀₀(u) n = length(vs) R = sum(vs) do v uv = view(points, [u, v]) - sum(coordinates, uv) / 2 + sum(to, uv) / 2 end / n - Point((F + 2R + (n - 3)P) / n) + Point(coordinates((F + 2R + (n - 3)P) / n)) end # new points in refined mesh diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index 7819f653c..38450bc00 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -22,16 +22,16 @@ function refine(mesh, ::QuadRefinement) ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) - cₒ = sum(coordinates, ps) / length(ps) - Point(cₒ) + cₒ = sum(to, ps) / length(ps) + Point(coordinates(cₒ)) end # add midpoints of edges ∂₁₀ = Boundary{1,0}(t) fpts = map(1:nfacets(t)) do edge ps = view(points, ∂₁₀(edge)) - cₒ = sum(coordinates, ps) / length(ps) - Point(cₒ) + cₒ = sum(to, ps) / length(ps) + Point(coordinates(cₒ)) end # original vertices diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 793271446..89ce1acf8 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -55,7 +55,7 @@ function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) catᵢ(A...) = cat(A..., dims=Val(1)) catⱼ(A...) = cat(A..., dims=Val(2)) - mat(quad) = [coordinates(quad(u, v)) for u in us, v in vs] + mat(quad) = [to(quad(u, v)) for u in us, v in vs] M = [mat(grid[i, j]) for i in 1:sᵢ, j in 1:sⱼ] C = mapreduce(catⱼ, 1:sⱼ) do j @@ -83,7 +83,7 @@ function _XYZ(grid::StructuredGrid{3}, factors::Dims{3}) catⱼ(A...) = cat(A..., dims=Val(2)) catₖ(A...) = cat(A..., dims=Val(3)) - mat(hex) = [coordinates(hex(u, v, w)) for u in us, v in vs, w in ws] + mat(hex) = [to(hex(u, v, w)) for u in us, v in vs, w in ws] M = [mat(grid[i, j, k]) for i in 1:sᵢ, j in 1:sⱼ, k in 1:sₖ] C = mapreduce(catₖ, 1:sₖ) do k diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 2bf4554fe..05403f7da 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -25,8 +25,8 @@ function refine(mesh, ::TriRefinement) ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) - cₒ = sum(coordinates, ps) / length(ps) - Point(cₒ) + cₒ = sum(to, ps) / length(ps) + Point(coordinates(cₒ)) end # original vertices diff --git a/src/sets.jl b/src/sets.jl index 545aead09..0da3d2cd7 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -73,4 +73,4 @@ PointSet(points) = PointSet(map(identity, points)) centroid(d::PointSet, ind::Int) = d[ind] -centroid(d::PointSet) = Point(sum(coordinates, d) / nelements(d)) +centroid(d::PointSet) = Point(coordinates(sum(to, d) / nelements(d))) diff --git a/src/sorting/direction.jl b/src/sorting/direction.jl index c9ea03f89..4ca9650db 100644 --- a/src/sorting/direction.jl +++ b/src/sorting/direction.jl @@ -17,7 +17,7 @@ function sortinds(domain::Domain, method::DirectionSort) v = method.direction t = map(1:nelements(domain)) do i c = centroid(domain, i) - u = coordinates(c) + u = to(c) (u ⋅ v) / (v ⋅ v) end sortperm(t) diff --git a/src/transforms.jl b/src/transforms.jl index 419a8773e..d8f56df5e 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -70,7 +70,7 @@ applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} = applycoord(::CoordinateTransform, x) = x # special treatment for Point -applycoord(t::CoordinateTransform, p::Point) = Point(applycoord(t, coordinates(p))) +applycoord(t::CoordinateTransform, p::Point) = Point(coordinates(applycoord(t, to(p)))) # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 5ccdd16cd..db57414ec 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -55,7 +55,7 @@ end applycoord(t::Affine, v::Vec) = t.A * v -applycoord(t::Affine, p::Point) = Point(t.A * coordinates(p) + t.b) +applycoord(t::Affine, p::Point) = Point(coordinates(t.A * to(p) + t.b)) # -------------- # SPECIAL CASES diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 33cbaac7f..55431ab1f 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -124,7 +124,7 @@ apply(::Repair{9}, poly::Ngon) = poly, [] function repair9(r::AbstractVector{<:Ring}) # sort vertices lexicographically verts = vertices.(r) - coord = coordinates.(reduce(vcat, verts)) + coord = to.(reduce(vcat, verts)) vperm = sortperm(sortperm(coord)) # each ring has its own set of indices diff --git a/src/transforms/smoothing.jl b/src/transforms/smoothing.jl index a650d8794..23d2a2db3 100644 --- a/src/transforms/smoothing.jl +++ b/src/transforms/smoothing.jl @@ -47,7 +47,7 @@ function _smooth(mesh, L, n, λ, μ; revert=false) points = vertices(mesh) # matrix with coordinates (nvertices x ndims) - X = reduce(hcat, coordinates.(points)) |> transpose + X = reduce(hcat, to.(points)) |> transpose # choose between apply and revert mode λ₁, λ₂ = revert ? (-μ, -λ) : (λ, μ) diff --git a/src/transforms/stdcoords.jl b/src/transforms/stdcoords.jl index aabd29281..7dee2e7ed 100644 --- a/src/transforms/stdcoords.jl +++ b/src/transforms/stdcoords.jl @@ -34,7 +34,7 @@ reapply(t::StdCoords, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) function _stdcoords(t, g) b = boundingbox(g) - t = Translate(coordinates(center(b))...) + t = Translate(to(center(b))...) s = Scale(ustrip.(sides(b))) inverse(t) → inverse(s) end diff --git a/src/transforms/stretch.jl b/src/transforms/stretch.jl index 6eb6cb96a..a47f2b600 100644 --- a/src/transforms/stretch.jl +++ b/src/transforms/stretch.jl @@ -47,7 +47,7 @@ revert(t::Stretch, g::GeometryOrDomain, c) = revert(c[1], g, c[2]) reapply(t::Stretch, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) function _stretch(t, g) - o = coordinates(_origin(g)) + o = to(_origin(g)) Translate(-o...) → Scale(t.factors) → Translate(o...) end diff --git a/src/traversing/source.jl b/src/traversing/source.jl index 106599545..fd6384c0c 100644 --- a/src/traversing/source.jl +++ b/src/traversing/source.jl @@ -23,7 +23,7 @@ function traverse(domain, path::SourcePath) @assert length(sources) ≤ nelements(domain) "more sources than points in object" # fit search tree - xs = [ustrip.(coordinates(centroid(domain, s))) for s in sources] + xs = [ustrip.(to(centroid(domain, s))) for s in sources] kdtree = KDTree(xs) # other locations that are not sources @@ -35,7 +35,7 @@ function traverse(domain, path::SourcePath) # compute distances to sources dists = [] for batch in batches - coords = [ustrip.(coordinates(centroid(domain, b))) for b in batch] + coords = [ustrip.(to(centroid(domain, b))) for b in batch] _, ds = knn(kdtree, coords, length(sources), true) append!(dists, ds) end diff --git a/src/utils.jl b/src/utils.jl index a086af82f..d20acafc4 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -70,7 +70,7 @@ See . """ function svdbasis(p::AbstractVector{<:Point{3}}) ℒ = lentype(eltype(p)) - X = reduce(hcat, coordinates.(p)) + X = reduce(hcat, to.(p)) μ = sum(X, dims=2) / size(X, 2) Z = X .- μ U = usvd(Z).U diff --git a/src/vectors.jl b/src/vectors.jl index aeb1f968d..28d613694 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -57,6 +57,13 @@ function StaticArrays.similar_type(::Type{<:Vec}, ::Type{T}, ::Size{S}) where {T isone(N) && T <: Len ? Vec{L,T} : SArray{Tuple{S...},T,N,L} end +""" + coordinates(vec) + +Return the coordinates of the `vec`. +""" +coordinates(vec::StaticVector) = Cartesian(Tuple(vec)) + """ ∠(u, v) @@ -79,6 +86,10 @@ end ∠(u::Vec{3}, v::Vec{3}) = atan(norm(u × v), u ⋅ v) # discard sign +# ----------- +# IO METHODS +# ----------- + function Base.show(io::IO, v::Vec) if get(io, :compact, false) print(io, v.coords) diff --git a/test/discretization.jl b/test/discretization.jl index 12cdd2c40..ec5c48dae 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -475,14 +475,14 @@ sphere = Sphere(point(0, 0, 0), T(1)) mesh = simplexify(sphere) @test eltype(mesh) <: Triangle - xs = coordinates.(vertices(mesh)) + xs = to.(vertices(mesh)) @test all(x -> norm(x) ≈ oneunit(ℳ), xs) # triangulation of cylinder surfaces cylsurf = CylinderSurface(T(1)) mesh = simplexify(cylsurf) @test eltype(mesh) <: Triangle - xs = coordinates.(vertices(mesh)) + xs = to.(vertices(mesh)) @test all(x -> -oneunit(ℳ) ≤ x[1] ≤ oneunit(ℳ), xs) @test all(x -> -oneunit(ℳ) ≤ x[2] ≤ oneunit(ℳ), xs) @test all(x -> zero(ℳ) ≤ x[3] ≤ oneunit(ℳ), xs) @@ -491,7 +491,7 @@ ball = Ball(point(0, 0), T(1)) mesh = simplexify(ball) @test eltype(mesh) <: Triangle - xs = coordinates.(vertices(mesh)) + xs = to.(vertices(mesh)) @test all(x -> norm(x) ≤ oneunit(ℳ) + eps(T) * u"m", xs) # triangulation of meshes diff --git a/test/partitioning.jl b/test/partitioning.jl index 4ed11aabf..14dbb96b4 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -154,8 +154,8 @@ # all points in p1 are below those in p2 pts1 = [centroid(p1, i) for i in 1:nelements(p1)] pts2 = [centroid(p2, i) for i in 1:nelements(p2)] - X1 = reduce(hcat, coordinates.(pts1)) - X2 = reduce(hcat, coordinates.(pts2)) + X1 = reduce(hcat, to.(pts1)) + X2 = reduce(hcat, to.(pts2)) M1 = maximum(X1, dims=2) m2 = minimum(X2, dims=2) @test all(X1[2, j] < m2[2] for j in 1:size(X1, 2)) @@ -187,8 +187,8 @@ # all points in p1 are to the left of p2 pts1 = [centroid(p1, i) for i in 1:nelements(p1)] pts2 = [centroid(p2, i) for i in 1:nelements(p2)] - X1 = reduce(hcat, coordinates.(pts1)) - X2 = reduce(hcat, coordinates.(pts2)) + X1 = reduce(hcat, to.(pts1)) + X2 = reduce(hcat, to.(pts2)) M1 = maximum(X1, dims=2) m2 = minimum(X2, dims=2) @test all(X1[1, j] < m2[1] for j in 1:size(X1, 2)) diff --git a/test/primitives.jl b/test/primitives.jl index a300904fd..520c3bbf3 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -10,9 +10,9 @@ @test Meshes.lentype(Point((T(1), T(1)))) == ℳ @test Meshes.lentype(Point(T(1), T(1))) == ℳ - @test coordinates(point(1)) == vector(1) - @test coordinates(point(1, 2)) == vector(1, 2) - @test coordinates(point(1, 2, 3)) == vector(1, 2, 3) + @test to(point(1)) == vector(1) + @test to(point(1, 2)) == vector(1, 2) + @test to(point(1, 2, 3)) == vector(1, 2, 3) @test point(1) - point(1) == vector(0) @test point(1, 2) - point(1, 1) == vector(0, 1) diff --git a/test/sampling.jl b/test/sampling.jl index 016bd9456..d0b129f94 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -3,13 +3,13 @@ rng = StableRNG(123) d = cartgrid(100, 100) s = sample(rng, d, UniformSampling(100)) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # availability of option ordered s = sample(rng, d, UniformSampling(100, ordered=true)) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") end @@ -19,23 +19,23 @@ rng = StableRNG(123) d = cartgrid(100, 100) s = sample(rng, d, WeightedSampling(100)) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # availability of option ordered s = sample(rng, d, WeightedSampling(100, ordered=true)) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") # utility method s = sample(rng, d, 100, ordered=true) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") s = sample(rng, d, 100, fill(1, 10000), ordered=true) - μ = mean(coordinates.([centroid(s, i) for i in 1:nelements(s)])) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) @test nelements(s) == 100 @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") end @@ -44,16 +44,16 @@ d = cartgrid(100, 100) s = sample(d, BallSampling(T(10))) n = nelements(s) - x = coordinates(centroid(s, 1)) - y = coordinates(centroid(s, 17)) + x = to(centroid(s, 1)) + y = to(centroid(s, 17)) @test n < 100 @test sqrt(sum((x - y) .^ 2)) ≥ T(10) * u"m" d = cartgrid(100, 100) s = sample(d, BallSampling(T(20))) n = nelements(s) - x = coordinates(centroid(s, 1)) - y = coordinates(centroid(s, 17)) + x = to(centroid(s, 1)) + y = to(centroid(s, 17)) @test n < 50 @test sqrt(sum((x - y) .^ 2)) ≥ T(20) * u"m" end @@ -62,7 +62,7 @@ g = cartgrid(100, 100) s = sample(g, BlockSampling(T(10))) @test nelements(s) == 100 - x = coordinates.(centroid.(s)) + x = to.(centroid.(s)) D = pairwise(Euclidean(), x) d = [D[i, j] for i in 1:length(x) for j in 1:(i - 1)] @test all(≥(T(10) * u"m"), d) @@ -207,7 +207,7 @@ # cylinder surface with parallel planes c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) - cs = coordinates.(ps) + cs = to.(ps) xs = getindex.(cs, 1) ys = getindex.(cs, 2) zs = getindex.(cs, 3) @@ -219,7 +219,7 @@ # cylinder surface with parallel shifted planes c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) - cs = coordinates.(ps) + cs = to.(ps) xs = getindex.(cs, 1) ys = getindex.(cs, 2) zs = getindex.(cs, 3) @@ -228,7 +228,7 @@ # cylinder surface with non-parallel planes c = CylinderSurface(Plane(point(0, 0, 0), vector(1, 0, 1)), Plane(point(1, 1, 1), vector(0, 1, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) - cs = coordinates.(ps) + cs = to.(ps) @test length(cs) == 200 + 2 s = Segment(point(0, 0), point(1, 1)) @@ -300,26 +300,26 @@ s = Segment(point(0, 0), point(1, 0)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} - @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in coordinates.(ps)) - @test all(coords[2] == zero(ℳ) for coords in coordinates.(ps)) + @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in to.(ps)) + @test all(coords[2] == zero(ℳ) for coords in to.(ps)) s = Segment(point(0, 0), point(0, 1)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} - @test all(coords[1] == zero(ℳ) for coords in coordinates.(ps)) - @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in coordinates.(ps)) + @test all(coords[1] == zero(ℳ) for coords in to.(ps)) + @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) s = Segment(point(0, 0), point(1, 1)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} - @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in coordinates.(ps)) + @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) c = Rope(point(0, 0), point(1, 0), point(0, 1), point(1, 1)) ps = sample(c, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all( coords[1] + coords[2] == oneunit(ℳ) || (zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) && coords[2] ∈ [zero(ℳ), oneunit(ℳ)]) for - coords in coordinates.(ps) + coords in to.(ps) ) t = Triangle(point(0, 0), point(1, 0), point(0, 1)) diff --git a/test/viewing.jl b/test/viewing.jl index bb3ab22cf..a6ff6ebd7 100644 --- a/test/viewing.jl +++ b/test/viewing.jl @@ -24,7 +24,7 @@ @test v[1] == g[1] v = view(p, b) @test nelements(v) == 6 - @test coordinates.(v) == vector.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)]) + @test to.(v) == vector.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)]) # convex polygons tri = Triangle(point(5, 7), point(10, 12), point(15, 7)) From d805045478f8d0cb91ca42eb92461fe4cb77b2e4 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 22 May 2024 10:09:37 -0300 Subject: [PATCH 048/423] Rename `coordinates` to `coords` (#847) * Rename 'coordinates' to 'coords' * Fix code --- src/Meshes.jl | 7 ++++++- src/boundingboxes.jl | 4 ++-- src/coords.jl | 17 +++++++++++++++++ src/domains.jl | 6 +++--- src/intersections/boxes.jl | 4 ++-- src/intersections/planes.jl | 2 +- src/mesh/rectilineargrid.jl | 2 +- src/multigeoms.jl | 2 +- src/partitioning/bisectfraction.jl | 2 +- src/polytopes.jl | 2 +- src/polytopes/hexahedron.jl | 2 +- src/polytopes/ngon.jl | 4 ++-- src/polytopes/segment.jl | 2 +- src/polytopes/tetrahedron.jl | 2 +- src/predicates/intersects.jl | 2 +- src/primitives/bezier.jl | 2 +- src/primitives/box.jl | 2 +- src/primitives/circle.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/point.jl | 11 ++--------- src/refinement/catmullclark.jl | 6 +++--- src/refinement/quad.jl | 4 ++-- src/refinement/tri.jl | 2 +- src/sets.jl | 2 +- src/transforms.jl | 2 +- src/transforms/affine.jl | 2 +- src/vectors.jl | 7 ------- 27 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 src/coords.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 2a3b03bad..f992ce9d4 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -54,6 +54,9 @@ include("vectors.jl") # geometries include("geometries.jl") +# coordinates +include("coords.jl") + # topological objects include("connectivities.jl") include("topologies.jl") @@ -157,7 +160,6 @@ export degree, Horner, DeCasteljau, - coordinates, to, radius, radii, @@ -215,6 +217,9 @@ export MultiPolygon, MultiPolyhedron, + # coordinates + coords, + # connectivities Connectivity, paramdim, diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index a65ff4bdf..fa47639c2 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -36,7 +36,7 @@ function boundingbox(r::Ray) v = r(1) - r(0) l = lower.(to(p), v) u = upper.(to(p), v) - Box(Point(coordinates(l)), Point(coordinates(u))) + Box(Point(coords(l)), Point(coords(u))) end function boundingbox(s::Sphere{Dim}) where {Dim} @@ -98,5 +98,5 @@ function _pboxes(points) @. xmin = min(x, xmin) @. xmax = max(x, xmax) end - Box(Point(coordinates(xmin)), Point(coordinates(xmax))) + Box(Point(coords(xmin)), Point(coords(xmax))) end diff --git a/src/coords.jl b/src/coords.jl new file mode 100644 index 000000000..749008ca5 --- /dev/null +++ b/src/coords.jl @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + coords(point) + +Return the coordinates of the `point`. +""" +coords(A::Point) = A.coords + +""" + coords(vec) + +Return the coordinates of the `vec`. +""" +coords(vec::StaticVector) = Cartesian(Tuple(vec)) diff --git a/src/domains.jl b/src/domains.jl index 97ea93c5d..daee76c63 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -92,13 +92,13 @@ Return the centroid of the `domain`, i.e. the centroid of all its element's centroids. """ function centroid(d::Domain) - coords(i) = to(centroid(d, i)) + vector(i) = to(centroid(d, i)) volume(i) = measure(element(d, i)) n = nelements(d) - x = coords.(1:n) + x = vector.(1:n) w = volume.(1:n) all(iszero, w) && (w = ones(eltype(w), n)) - Point(coordinates(sum(w .* x) / sum(w))) + Point(coords(sum(w .* x) / sum(w))) end """ diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 3e86f14a5..4a1b5afc1 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -14,8 +14,8 @@ function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} m2, M2 = to.(extrema(box₂)) # relevant vertices - u = Point(coordinates(max.(m1, m2))) - v = Point(coordinates(min.(M1, M2))) + u = Point(coords(max.(m1, m2))) + v = Point(coords(min.(M1, M2))) # auxiliary variables δ = v - u diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index fd053b53d..691513cd5 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -22,7 +22,7 @@ function intersection(f, plane1::Plane, plane2::Plane) c2 = (h2 - h1 * n1n2) / (1 - n1n2^2) p1 = (c1 * n1) + (c2 * n2) p2 = p1 + d - return @IT Intersecting Line(Point(coordinates(p1 * u)), Point(coordinates(p2 * u))) f + return @IT Intersecting Line(Point(coords(p1 * u)), Point(coords(p2 * u))) f end end diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index b69f2930d..b1d21c884 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -52,7 +52,7 @@ function centroid(g::RectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) - Point(coordinates((to(p1) + to(p2)) / 2)) + Point(coords((to(p1) + to(p2)) / 2)) end function Base.getindex(g::RectilinearGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 639f8659e..ce5f6a1a0 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -49,7 +49,7 @@ end function centroid(m::Multi) cs = to.(centroid.(m.geoms)) - Point(coordinates(sum(cs) / length(cs))) + Point(coords(sum(cs) / length(cs))) end rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index 4febb43ab..785b5012a 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -41,7 +41,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP while iter < maxiter m = (a + b) / 2 - bisectpoint = BisectPointPartition(n, Point(coordinates(m))) + bisectpoint = BisectPointPartition(n, Point(coords(m))) subsets, metadata = partitioninds(rng, domain, bisectpoint) g = length(subsets[1]) / nelements(domain) diff --git a/src/polytopes.jl b/src/polytopes.jl index 5f26e9069..efd9612f5 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -228,7 +228,7 @@ nvertices(p::Polytope) = nvertices(typeof(p)) Return the centroid of the `polytope`. """ -centroid(p::Polytope) = Point(coordinates(sum(to, vertices(p)) / length(vertices(p)))) +centroid(p::Polytope) = Point(coords(sum(to, vertices(p)) / length(vertices(p)))) """ unique(polytope) diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index 67a695f72..4ffc836d0 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -20,7 +20,7 @@ function (h::Hexahedron)(u, v, w) end A1, A2, A4, A3, A5, A6, A8, A7 = to.(h.vertices) Point( - coordinates( + coords( (1 - u) * (1 - v) * (1 - w) * A1 + u * (1 - v) * (1 - w) * A2 + (1 - u) * v * (1 - w) * A3 + diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 6ec6a1c06..2233a17ea 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -92,7 +92,7 @@ function (t::Triangle)(u, v) throw(DomainError((u, v), "invalid barycentric coordinates for triangle.")) end v₁, v₂, v₃ = to.(t.vertices) - Point(coordinates(v₁ * w + v₂ * u + v₃ * v)) + Point(coords(v₁ * w + v₂ * u + v₃ * v)) end # ------------ @@ -105,5 +105,5 @@ function (q::Quadrangle)(u, v) throw(DomainError((u, v), "q(u, v) is not defined for u, v outside [0, 1]².")) end c₀₀, c₀₁, c₁₁, c₁₀ = to.(q.vertices) - Point(coordinates(c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v)) + Point(coords(c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v)) end diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index a13bdd1fe..b91d0713e 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -23,7 +23,7 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] function center(s::Segment) a, b = extrema(s) - Point(coordinates((to(a) + to(b)) / 2)) + Point(coords((to(a) + to(b)) / 2)) end Base.isapprox(s₁::Segment, s₂::Segment; kwargs...) = diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index a44c8a296..dfaeaa027 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -20,7 +20,7 @@ function (t::Tetrahedron)(u, v, w) throw(DomainError((u, v, w), "invalid barycentric coordinates for tetrahedron.")) end v₁, v₂, v₃, v₄ = to.(t.vertices) - Point(coordinates(v₁ * z + v₂ * u + v₃ * v + v₄ * w)) + Point(coords(v₁ * z + v₂ * u + v₃ * v + v₄ * w)) end Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Tetrahedron{Dim}}) where {Dim} = diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 411a1b9a2..e7f572bbb 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -245,7 +245,7 @@ intersects(m::Multi, c::Chain) = intersects(c, m) # ------------------ # support point in Minkowski difference -minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(coordinates(supportfun(g₁, d) - supportfun(g₂, -d))) +minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(coords(supportfun(g₁, d) - supportfun(g₂, -d))) # origin of coordinate system minkowskiorigin(Dim, ℒ) = Point(ntuple(i -> zero(ℒ), Dim)) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 94a9de9de..cc8184c20 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -105,7 +105,7 @@ function (curve::BezierCurve)(t, ::Horner) end b₀ = bᵢ₋₁ - Point(coordinates(b₀)) + Point(coords(b₀)) end Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{BezierCurve{Dim}}) where {Dim} = diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 8ff443453..f37c10cda 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -39,7 +39,7 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -center(b::Box) = Point(coordinates((to(b.max) + to(b.min)) / 2)) +center(b::Box) = Point(coords((to(b.max) + to(b.min)) / 2)) diagonal(b::Box) = norm(b.max - b.min) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 34dcd0552..ebcf57a13 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -34,7 +34,7 @@ function Circle(p1::Point{3}, p2::Point{3}, p3::Point{3}) F = to(p1) ⋅ n⃗ M = transpose([n⃗ v12 v13]) u = [F, m12 ⋅ v12, m13 ⋅ v13] - O = Point(coordinates(uinv(M) * u)) + O = Point(coords(uinv(M) * u)) r = norm(p1 - O) Circle(Plane(O, n⃗), r) end diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 0605468bd..80dd451ec 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -67,7 +67,7 @@ top(c::CylinderSurface) = c.top function center(c::CylinderSurface) a = to(c.bot(0, 0)) b = to(c.top(0, 0)) - Point(coordinates((a .+ b) ./ 2)) + Point(coords((a .+ b) ./ 2)) end axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 6580359e8..c233cfca4 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -53,13 +53,6 @@ center(p::Point) = p Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) = isapprox(A.coords, B.coords; atol, kwargs...) -""" - coordinates(point) - -Return the coordinates of the `point`. -""" -coordinates(A::Point) = A.coords - """ to(point) @@ -82,7 +75,7 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(to(A) + v)) ++(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) + v)) +(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A + v """ @@ -92,7 +85,7 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coordinates(to(A) - v)) +-(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) - v)) -(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A - v """ diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index 77d4ad4ad..b078d3c0d 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -34,7 +34,7 @@ function refine(mesh, ::CatmullClark) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coordinates(cₒ)) + Point(coords(cₒ)) end # add midpoints of edges @@ -46,7 +46,7 @@ function refine(mesh, ::CatmullClark) ∑p = sum(to, ps) ∑q = sum(to, qs) M = length(ps) + length(qs) - Point(coordinates((∑p + ∑q) / M)) + Point(coords((∑p + ∑q) / M)) end # move original vertices @@ -68,7 +68,7 @@ function refine(mesh, ::CatmullClark) sum(to, uv) / 2 end / n - Point(coordinates((F + 2R + (n - 3)P) / n)) + Point(coords((F + 2R + (n - 3)P) / n)) end # new points in refined mesh diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index 38450bc00..06a0a8a52 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -23,7 +23,7 @@ function refine(mesh, ::QuadRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coordinates(cₒ)) + Point(coords(cₒ)) end # add midpoints of edges @@ -31,7 +31,7 @@ function refine(mesh, ::QuadRefinement) fpts = map(1:nfacets(t)) do edge ps = view(points, ∂₁₀(edge)) cₒ = sum(to, ps) / length(ps) - Point(coordinates(cₒ)) + Point(coords(cₒ)) end # original vertices diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 05403f7da..5f4c84c7c 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -26,7 +26,7 @@ function refine(mesh, ::TriRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coordinates(cₒ)) + Point(coords(cₒ)) end # original vertices diff --git a/src/sets.jl b/src/sets.jl index 0da3d2cd7..4e6cae331 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -73,4 +73,4 @@ PointSet(points) = PointSet(map(identity, points)) centroid(d::PointSet, ind::Int) = d[ind] -centroid(d::PointSet) = Point(coordinates(sum(to, d) / nelements(d))) +centroid(d::PointSet) = Point(coords(sum(to, d) / nelements(d))) diff --git a/src/transforms.jl b/src/transforms.jl index d8f56df5e..318ae79e1 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -70,7 +70,7 @@ applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} = applycoord(::CoordinateTransform, x) = x # special treatment for Point -applycoord(t::CoordinateTransform, p::Point) = Point(coordinates(applycoord(t, to(p)))) +applycoord(t::CoordinateTransform, p::Point) = Point(coords(applycoord(t, to(p)))) # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index db57414ec..25a032b7b 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -55,7 +55,7 @@ end applycoord(t::Affine, v::Vec) = t.A * v -applycoord(t::Affine, p::Point) = Point(coordinates(t.A * to(p) + t.b)) +applycoord(t::Affine, p::Point) = Point(coords(t.A * to(p) + t.b)) # -------------- # SPECIAL CASES diff --git a/src/vectors.jl b/src/vectors.jl index 28d613694..f296e5272 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -57,13 +57,6 @@ function StaticArrays.similar_type(::Type{<:Vec}, ::Type{T}, ::Size{S}) where {T isone(N) && T <: Len ? Vec{L,T} : SArray{Tuple{S...},T,N,L} end -""" - coordinates(vec) - -Return the coordinates of the `vec`. -""" -coordinates(vec::StaticVector) = Cartesian(Tuple(vec)) - """ ∠(u, v) From c05ae089d8e884ffc33858ae8800e51f51a70dc4 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 22 May 2024 10:40:45 -0300 Subject: [PATCH 049/423] Update documentation (#848) * Update documentation * Apply suggestions * Fix typo --- docs/src/geometries/primitives.md | 2 +- docs/src/index.md | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 8c7c3521d..239a2b656 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -24,7 +24,7 @@ Point ``` ```@docs -coordinates(::Point) +to(::Point) -(::Point, ::Point) +(::Point, ::Vec) -(::Point, ::Vec) diff --git a/docs/src/index.md b/docs/src/index.md index 65a4a31d9..a41594324 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -116,10 +116,10 @@ B = Point(3.0, 3.0) B - A ``` -They can't be added, but their coordinates can: +They can't be added, but the vectors from the origin to the points can: ```@example overview -coordinates(A) + coordinates(B) +to(A) + to(B) ``` We can add a point to a vector though, and get a new point: @@ -128,6 +128,14 @@ We can add a point to a vector though, and get a new point: A + Vec(1, 1) ``` +Every point has well-defined coordinates: + +```@example overview +coords(A) +``` + +which can be converted with [CoordRefSystems.jl](https://github.com/JuliaEarth/CoordRefSystems.jl). + And finally, we can create points at random with: ```@example overview From 99d2d40cbeeab4ea09dad7b856f32dc7a7a8de49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 22 May 2024 10:42:35 -0300 Subject: [PATCH 050/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c6f7bcf8e..272b8af55 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.42.2" +version = "0.43.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 7ca176b8721e6e12f33c98a30dde5f28799ca1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 23 May 2024 16:40:29 -0300 Subject: [PATCH 051/423] Update docs --- docs/src/index.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index a41594324..88ae58b03 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -77,12 +77,12 @@ import CairoMakie as Mke ### Points and vectors -A [`Point`](@ref) is defined by its coordinates in a global reference system. The type of the -coordinates is determined automatically based on the specified literals. -`Integer` coordinates are converted to `Float64` to fulfill the requirements of most -geometric processing algorithms, which would be undefined in a discrete scale. +A [`Point`](@ref) is defined by its coordinates in a coordinate reference system +from [CoordRefSystems.jl](https://github.com/JuliaEarth/CoordRefSystems.jl). By +default, a `Cartesian` coordinates with `NoDatum` are used. -A vector [`Vec`](@ref) follows the same pattern. It can be constructed with the `Vec` constructor. +`Integer` coordinates are converted to `Float64` to fulfill the requirements of most +geometric processing algorithms, which would be undefined in a discrete scale: ```@example overview Point(0.0, 1.0) # double precision as expected @@ -128,20 +128,12 @@ We can add a point to a vector though, and get a new point: A + Vec(1, 1) ``` -Every point has well-defined coordinates: +Every point and vector has well-defined coordinates: ```@example overview coords(A) ``` -which can be converted with [CoordRefSystems.jl](https://github.com/JuliaEarth/CoordRefSystems.jl). - -And finally, we can create points at random with: - -```@example overview -rand(Point{2}) -``` - ### Primitives Primitive geometries such as [`Box`](@ref), [`Ball`](@ref), [`Sphere`](@ref), From f51764c7eb587f2444f95d007d0425257a7cf9d9 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 27 May 2024 14:45:03 -0300 Subject: [PATCH 052/423] Fix 'radius(ball)' (#850) --- src/neighborhoods/metricball.jl | 2 +- test/neighborhoods.jl | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/neighborhoods/metricball.jl b/src/neighborhoods/metricball.jl index 5f9d953c7..1ff1e2579 100644 --- a/src/neighborhoods/metricball.jl +++ b/src/neighborhoods/metricball.jl @@ -102,7 +102,7 @@ and `||v|| > r, ∀ v ∉ ball``. """ function radius(ball::MetricBall) r = first(ball.radii) - ball.metric isa Mahalanobis ? one(r) : r + ball.metric isa Mahalanobis ? oneunit(r) : r end """ diff --git a/test/neighborhoods.jl b/test/neighborhoods.jl index 8e3dacf45..aacf9459a 100644 --- a/test/neighborhoods.jl +++ b/test/neighborhoods.jl @@ -38,14 +38,20 @@ end # 2D simple test of default convention - m = metric(MetricBall(T.((1, 1)))) + b = MetricBall(T.((1, 1))) + m = metric(b) + @test radius(b) == oneunit(ℳ) @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") == evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") - m = metric(MetricBall(T.((1, 2)))) + b = MetricBall(T.((1, 2))) + m = metric(b) + @test radius(b) == oneunit(ℳ) @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") != evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") # 3D simple test of default convention - m = metric(MetricBall(T.((1.0, 0.5, 0.5)), RotZYX(T(-π / 4), T(0), T(0)))) + b = MetricBall(T.((1.0, 0.5, 0.5)), RotZYX(T(-π / 4), T(0), T(0))) + m = metric(b) + @test radius(b) == oneunit(ℳ) @test evaluate(m, T[1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(8) * u"m" @test evaluate(m, T[-1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(2) * u"m" From c4a712c5e7066f388cb0025883b0b3e87b85fc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 27 May 2024 14:45:21 -0300 Subject: [PATCH 053/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 272b8af55..c72c699ca 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.0" +version = "0.43.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ed26e27c27266c122fd5c4ed3e47eb9289517e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 27 May 2024 18:27:50 -0300 Subject: [PATCH 054/423] Fix type annotation in Makie extension --- ext/geometryset.jl | 2 +- src/predicates/intersects.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 7ed33d11f..22d102d5f 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -90,7 +90,7 @@ function vizgset2D!(plot, geoms, colorant) showfacets2D!(plot, geoms) end -const PolygonLike{Dim,T} = Union{Polygon{Dim,T},MultiPolygon{Dim,T}} +const PolygonLike{Dim} = Union{Polygon{Dim},MultiPolygon{Dim}} function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) showsegments = plot[:showsegments] diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index e7f572bbb..7096f76af 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -108,7 +108,7 @@ function intersects(g₁::Geometry{Dim}, g₂::Geometry{Dim}) where {Dim} end """ - gjk!(O::Point{Dim,T}, points) where {Dim,T} + gjk!(O::Point{Dim}, points) where {Dim} Perform one iteration of the GJK algorithm. From d054ddaefc571aa34ead1988453b93b43c1ba17c Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 29 May 2024 10:33:32 -0300 Subject: [PATCH 055/423] Generalize the 'to' function to work with any 'CRS' (#851) --- src/primitives/point.jl | 25 ++++++++++++------------- test/Project.toml | 1 + test/primitives.jl | 10 ++++++++++ test/runtests.jl | 1 + 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index c233cfca4..bfc71f14b 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -38,9 +38,8 @@ struct Point{Dim,C<:CRS} <: Primitive{Dim} Point(coords::C) where {C<:CRS} = new{CoordRefSystems.ndims(coords),C}(coords) end -# convenience constructors -Point(coords...) = Point(Cartesian(coords)) -Point(coords::Tuple) = Point(Cartesian(coords)) +# convenience constructor +Point(coords...) = Point(Cartesian(coords...)) paramdim(::Type{<:Point}) = 0 @@ -58,7 +57,7 @@ Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) Return the vector from the origin to the `point`. """ -to(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues(A.coords)) +to(A::Point) = Vec(CoordRefSystems.cvalues(convert(Cartesian, A.coords))) """ -(A::Point, B::Point) @@ -66,7 +65,7 @@ to(A::Point{Dim,<:Cartesian}) where {Dim} = Vec(CoordRefSystems.cvalues(A.coords Return the [`Vec`](@ref) associated with the direction from point `B` to point `A`. """ --(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = to(A) - to(B) +-(A::Point{Dim}, B::Point{Dim}) where {Dim} = to(A) - to(B) """ +(A::Point, v::Vec) @@ -75,8 +74,8 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) + v)) -+(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A + v ++(A::Point{Dim}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) + v)) ++(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A + v """ -(A::Point, v::Vec) @@ -85,8 +84,8 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim,<:Cartesian}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) - v)) --(v::Vec{Dim}, A::Point{Dim,<:Cartesian}) where {Dim} = A - v +-(A::Point{Dim}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) - v)) +-(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A - v """ ⪯(A::Point, B::Point) @@ -96,10 +95,10 @@ at a reference (or start) point `A`. Generalized inequality for non-negative orthant Rⁿ₊. """ -⪯(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(≥(0u"m"), B - A) -⪰(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(≥(0u"m"), A - B) -≺(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(>(0u"m"), B - A) -≻(A::Point{Dim,<:Cartesian}, B::Point{Dim,<:Cartesian}) where {Dim} = all(>(0u"m"), A - B) +⪯(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x ≥ zero(x), B - A) +⪰(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x ≥ zero(x), A - B) +≺(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x > zero(x), B - A) +≻(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x > zero(x), A - B) """ ∠(A, B, C) diff --git a/test/Project.toml b/test/Project.toml index 872aa9b5b..c32ce5155 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,6 +2,7 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" CircularArrays = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" +CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/test/primitives.jl b/test/primitives.jl index 520c3bbf3..7e77a0013 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -86,6 +86,16 @@ @test unit(Meshes.lentype(p)) == u"m" @test Unitful.numtype(Meshes.lentype(p)) === Float32 + # `to` function + p1 = point(1, 1) + p2 = point(1, 1, 1) + p3 = Point(Polar(T(√2), T(π / 4))) + p4 = Point(Cylindrical(T(√2), T(π / 4), T(1))) + @test to(p1) == vector(1, 1) + @test to(p2) == vector(1, 1, 1) + @test to(p3) ≈ vector(1, 1) + @test to(p4) ≈ vector(1, 1, 1) + # generalized inequality @test point(1, 1) ⪯ point(1, 1) @test !(point(1, 1) ≺ point(1, 1)) diff --git a/test/runtests.jl b/test/runtests.jl index aa43c6cfc..62e721bdc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,7 @@ using Tables using Distances using Statistics using LinearAlgebra +using CoordRefSystems using CategoricalArrays using CircularArrays using StaticArrays From ed22640d9ad9be8f1d7b84ccb395be0eb00f66d0 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 29 May 2024 15:20:21 -0300 Subject: [PATCH 056/423] Add `assertion` function (#852) * Add 'assertion' function * Apply suggestions * Use 'LazyString' in the 'Connectivity' assertion --- src/connectivities.jl | 4 ++-- src/hulls/graham.jl | 2 +- src/hulls/jarvis.jl | 2 +- src/matrices.jl | 2 +- src/partitioning/block.jl | 2 +- src/partitioning/fraction.jl | 2 +- src/partitioning/uniform.jl | 2 +- src/primitives/box.jl | 2 +- src/primitives/frustum.jl | 4 ++-- src/primitives/frustumsurface.jl | 4 ++-- src/refinement/tri.jl | 2 +- src/refinement/trisubdivision.jl | 2 +- src/topologies/halfedge.jl | 2 +- src/toporelations/adjacency.jl | 2 +- src/toporelations/boundary.jl | 2 +- src/toporelations/coboundary.jl | 2 +- src/traversing/source.jl | 6 +++--- src/utils.jl | 7 +++++++ src/winding.jl | 2 +- 19 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/connectivities.jl b/src/connectivities.jl index 80194d354..923e6b69c 100644 --- a/src/connectivities.jl +++ b/src/connectivities.jl @@ -14,9 +14,9 @@ struct Connectivity{PL<:Polytope,N} indices::NTuple{N,Int} function Connectivity{PL,N}(indices) where {PL,N} - @assert nvertices(PL) == N "cannot create a $PL with $N vertices" + assertion(nvertices(PL) == N, lazy"cannot create a $PL with $N vertices") if PL <: Ngon - @assert N ≥ 3 "Ngon requires 3 or more vertices" + assertion(N ≥ 3, "Ngon requires 3 or more vertices") end new(indices) end diff --git a/src/hulls/graham.jl b/src/hulls/graham.jl index b1523f853..a30b443bb 100644 --- a/src/hulls/graham.jl +++ b/src/hulls/graham.jl @@ -24,7 +24,7 @@ function hull(points, ::GrahamScan) ℒ = lentype(pₒ) T = numtype(ℒ) - @assert Dim == 2 "Graham's scan only defined in 2D" + assertion(Dim == 2, "Graham's scan only defined in 2D") # remove duplicates p = unique(points) diff --git a/src/hulls/jarvis.jl b/src/hulls/jarvis.jl index 681aed148..ee77912c1 100644 --- a/src/hulls/jarvis.jl +++ b/src/hulls/jarvis.jl @@ -24,7 +24,7 @@ function hull(points, ::JarvisMarch) Dim = embeddim(pₒ) ℒ = lentype(pₒ) - @assert Dim == 2 "Jarvis's march only defined in 2D" + assertion(Dim == 2, "Jarvis's march only defined in 2D") # remove duplicates p = unique(points) diff --git a/src/matrices.jl b/src/matrices.jl index e370a53fc..8a609e9af 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -36,7 +36,7 @@ function laplacematrix(mesh; weights=:cotangent) uniformlaplacian!(L, 𝒩) elseif weights == :cotangent v = vertices(ℳ) - @assert eltype(ℳ) <: Triangle "cotangent weights only defined for triangle meshes" + assertion(eltype(ℳ) <: Triangle, "cotangent weights only defined for triangle meshes") cotangentlaplacian!(L, 𝒩, v) else throw(ArgumentError("invalid discretization weights")) diff --git a/src/partitioning/block.jl b/src/partitioning/block.jl index ac5113b4b..d931c08aa 100644 --- a/src/partitioning/block.jl +++ b/src/partitioning/block.jl @@ -31,7 +31,7 @@ function partitioninds(::AbstractRNG, domain::Domain, method::BlockPartition) bsides = sides(bbox) Dim = length(bsides) - @assert all(psides .≤ bsides) "invalid block sides" + assertion(all(psides .≤ bsides), "invalid block sides") # bounding box properties ce = centroid(bbox) diff --git a/src/partitioning/fraction.jl b/src/partitioning/fraction.jl index 6d33bd3e9..86535e907 100644 --- a/src/partitioning/fraction.jl +++ b/src/partitioning/fraction.jl @@ -13,7 +13,7 @@ struct FractionPartition <: PartitionMethod shuffle::Bool function FractionPartition(fraction, shuffle) - @assert 0 < fraction < 1 "fraction must be in interval (0,1)" + assertion(0 < fraction < 1, "fraction must be in interval (0,1)") new(fraction, shuffle) end end diff --git a/src/partitioning/uniform.jl b/src/partitioning/uniform.jl index a9dd0c5d1..4260d332a 100644 --- a/src/partitioning/uniform.jl +++ b/src/partitioning/uniform.jl @@ -20,7 +20,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::UniformPartitio n = nelements(domain) k = method.k - @assert k ≤ n "number of subsets must be smaller than number of points" + assertion(k ≤ n, "number of subsets must be smaller than number of points") inds = method.shuffle ? shuffle(rng, 1:n) : collect(1:n) subsets = collect(Iterators.partition(inds, n ÷ k)) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index f37c10cda..d3a8778e5 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -20,7 +20,7 @@ struct Box{Dim,P<:Point{Dim}} <: Primitive{Dim} max::P function Box{Dim,P}(min, max) where {Dim,P<:Point{Dim}} - @assert min ⪯ max "`min` must be less than or equal to `max`" + assertion(min ⪯ max, "`min` must be less than or equal to `max`") new(min, max) end end diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index 3e0c32cba..2fe181597 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -18,8 +18,8 @@ struct Frustum{D<:Disk} <: Primitive{3} bn = normal(plane(bot)) tn = normal(plane(top)) a = bn ⋅ tn - @assert a ≈ oneunit(a) "Bottom and top plane must be parallel" - @assert center(bot) ≉ center(top) "Bottom and top centers need to be distinct" + assertion(a ≈ oneunit(a), "Bottom and top plane must be parallel") + assertion(center(bot) ≉ center(top), "Bottom and top centers need to be distinct") new(bot, top) end end diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 5707b69c7..8df12f246 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -18,8 +18,8 @@ struct FrustumSurface{D<:Disk} <: Primitive{3} bn = normal(plane(bot)) tn = normal(plane(top)) a = bn ⋅ tn - @assert a ≈ oneunit(a) "Bottom and top plane must be parallel" - @assert center(bot) ≉ center(top) "Bottom and top centers need to be distinct" + assertion(a ≈ oneunit(a), "Bottom and top plane must be parallel") + assertion(center(bot) ≉ center(top), "Bottom and top centers need to be distinct") new(bot, top) end end diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 5f4c84c7c..cf18a1f27 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -11,7 +11,7 @@ A n-gon is subdivided into n triangles. struct TriRefinement <: RefinementMethod end function refine(mesh, ::TriRefinement) - @assert paramdim(mesh) == 2 "TriRefinement only defined for surface meshes" + assertion(paramdim(mesh) == 2, "TriRefinement only defined for surface meshes") (eltype(mesh) <: Triangle) || return simplexify(mesh) # retrieve geometry and topology diff --git a/src/refinement/trisubdivision.jl b/src/refinement/trisubdivision.jl index 10822a1e1..9ffdb0c99 100644 --- a/src/refinement/trisubdivision.jl +++ b/src/refinement/trisubdivision.jl @@ -17,7 +17,7 @@ then subdividing each triangle into four triangles. struct TriSubdivision <: RefinementMethod end function refine(mesh, ::TriSubdivision) - @assert paramdim(mesh) == 2 "TriSubdivision only defined for surface meshes" + assertion(paramdim(mesh) == 2, "TriSubdivision only defined for surface meshes") # triangulate mesh if necessary tmesh = eltype(mesh) <: Triangle ? mesh : simplexify(mesh) diff --git a/src/topologies/halfedge.jl b/src/topologies/halfedge.jl index 71b2d889c..07a1e84c8 100644 --- a/src/topologies/halfedge.jl +++ b/src/topologies/halfedge.jl @@ -130,7 +130,7 @@ function HalfEdgeTopology(halves::AbstractVector{Tuple{HalfEdge,HalfEdge}}) end function HalfEdgeTopology(elems::AbstractVector{<:Connectivity}; sort=true) - @assert all(e -> paramdim(e) == 2, elems) "invalid element for half-edge topology" + assertion(all(e -> paramdim(e) == 2, elems), "invalid element for half-edge topology") # sort elements to make sure that they # are traversed in adjacent-first order diff --git a/src/toporelations/adjacency.jl b/src/toporelations/adjacency.jl index 498dad2e6..04ee5ea80 100644 --- a/src/toporelations/adjacency.jl +++ b/src/toporelations/adjacency.jl @@ -15,7 +15,7 @@ function Adjacency{P}(topology) where {P} D = paramdim(topology) T = typeof(topology) - @assert D ≥ P "invalid adjacency relation" + assertion(D ≥ P, "invalid adjacency relation") Adjacency{P,D,T}(topology) end diff --git a/src/toporelations/boundary.jl b/src/toporelations/boundary.jl index 32be63989..da36718de 100644 --- a/src/toporelations/boundary.jl +++ b/src/toporelations/boundary.jl @@ -16,7 +16,7 @@ function Boundary{P,Q}(topology) where {P,Q} D = paramdim(topology) T = typeof(topology) - @assert D ≥ P > Q "invalid boundary relation" + assertion(D ≥ P > Q, "invalid boundary relation") Boundary{P,Q,D,T}(topology) end diff --git a/src/toporelations/coboundary.jl b/src/toporelations/coboundary.jl index 90283589c..9409f71fc 100644 --- a/src/toporelations/coboundary.jl +++ b/src/toporelations/coboundary.jl @@ -16,7 +16,7 @@ function Coboundary{P,Q}(topology) where {P,Q} D = paramdim(topology) T = typeof(topology) - @assert P < Q ≤ D "invalid coboundary relation" + assertion(P < Q ≤ D, "invalid coboundary relation") Coboundary{P,Q,D,T}(topology) end diff --git a/src/traversing/source.jl b/src/traversing/source.jl index fd6384c0c..2fb4854d5 100644 --- a/src/traversing/source.jl +++ b/src/traversing/source.jl @@ -18,9 +18,9 @@ SourcePath(sources) = SourcePath(sources, 10^3) function traverse(domain, path::SourcePath) sources = path.sources batchsize = path.batchsize - @assert allunique(sources) "non-unique sources" - @assert all(1 .≤ sources .≤ nelements(domain)) "sources must be valid locations" - @assert length(sources) ≤ nelements(domain) "more sources than points in object" + assertion(allunique(sources), "non-unique sources") + assertion(all(1 .≤ sources .≤ nelements(domain)), "sources must be valid locations") + assertion(length(sources) ≤ nelements(domain), "more sources than points in object") # fit search tree xs = [ustrip.(to(centroid(domain, s))) for s in sources] diff --git a/src/utils.jl b/src/utils.jl index d20acafc4..0ce336e1d 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -5,6 +5,13 @@ # auxiliary type for dispatch purposes const GeometryOrDomain = Union{Geometry,Domain} +""" + assertion(cond, msg) + +Throws an `AssertionError(msg)` if `cond` is `false`. +""" +assertion(cond, msg) = cond || throw(AssertionError(msg)) + """ fitdims(dims, D) diff --git a/src/winding.jl b/src/winding.jl index e1ad0549f..77cbd8e9b 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -34,7 +34,7 @@ winding(point::Point{2}, ring::Ring{2}) = winding((point,), ring) |> first # Jacobson et al 2013. function winding(points, mesh::Mesh{3}) - @assert paramdim(mesh) == 2 "winding number only defined for surface meshes" + assertion(paramdim(mesh) == 2, "winding number only defined for surface meshes") (eltype(mesh) <: Triangle) || return winding(points, simplexify(mesh)) function w(p) From ba4c693bd0e57ca709fccc56174e567ce8086d25 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 29 May 2024 16:27:10 -0300 Subject: [PATCH 057/423] Overload 'rand' methods for 'Geometry' types (#853) --- docs/src/algorithms/boundingbox.md | 2 +- docs/src/algorithms/hulls.md | 2 +- docs/src/domains/sets.md | 4 ++-- docs/src/geometries/primitives.md | 2 +- docs/src/visualization.md | 2 +- src/geometries.jl | 8 ++++++++ src/polytopes/hexahedron.jl | 2 +- src/polytopes/ngon.jl | 3 +-- src/polytopes/polyarea.jl | 2 +- src/polytopes/pyramid.jl | 3 +-- src/polytopes/ring.jl | 6 +++--- src/polytopes/rope.jl | 3 +-- src/polytopes/segment.jl | 3 +-- src/polytopes/tetrahedron.jl | 2 +- src/primitives/ball.jl | 2 +- src/primitives/bezier.jl | 3 +-- src/primitives/box.jl | 2 +- src/primitives/circle.jl | 2 +- src/primitives/cone.jl | 2 +- src/primitives/conesurface.jl | 3 +-- src/primitives/cylinder.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/disk.jl | 2 +- src/primitives/ellipsoid.jl | 2 +- src/primitives/frustum.jl | 2 +- src/primitives/frustumsurface.jl | 2 +- src/primitives/line.jl | 3 +-- src/primitives/paraboloidsurface.jl | 2 +- src/primitives/plane.jl | 3 +-- src/primitives/point.jl | 3 +-- src/primitives/ray.jl | 2 +- src/primitives/sphere.jl | 2 +- src/primitives/torus.jl | 2 +- 33 files changed, 43 insertions(+), 44 deletions(-) diff --git a/docs/src/algorithms/boundingbox.md b/docs/src/algorithms/boundingbox.md index 3f78b80c8..614ee86b5 100644 --- a/docs/src/algorithms/boundingbox.md +++ b/docs/src/algorithms/boundingbox.md @@ -10,7 +10,7 @@ boundingbox ``` ```@example boundingbox -pset = PointSet([rand(Point{2}) for _ in 1:100]) +pset = PointSet(rand(Point{2}, 100)) bbox = boundingbox(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/algorithms/hulls.md b/docs/src/algorithms/hulls.md index 6bd2feca5..2e2fddb4d 100644 --- a/docs/src/algorithms/hulls.md +++ b/docs/src/algorithms/hulls.md @@ -14,7 +14,7 @@ JarvisMarch ``` ```@example hull -pset = PointSet([rand(Point{2}) for _ in 1:100]) +pset = PointSet(rand(Point{2}, 100)) chul = convexhull(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/domains/sets.md b/docs/src/domains/sets.md index 6c5e6a55d..11caa2ee5 100644 --- a/docs/src/domains/sets.md +++ b/docs/src/domains/sets.md @@ -13,7 +13,7 @@ GeometrySet ``` ```@example sets -GeometrySet([rand(Ball{3}) for _ in 1:3]) |> viz +GeometrySet(rand(Ball{3}, 3)) |> viz ``` ```@docs @@ -21,5 +21,5 @@ PointSet ``` ```@example sets -PointSet([rand(Point{2}) for _ in 1:100]) |> viz +PointSet(rand(Point{2}, 100)) |> viz ``` \ No newline at end of file diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 239a2b656..0d88ba495 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -20,7 +20,7 @@ Point ``` ```@example primitives -[rand(Point{3}) for _ in 1:100] |> viz +rand(Point{3}, 100) |> viz ``` ```@docs diff --git a/docs/src/visualization.md b/docs/src/visualization.md index 14dcaaa36..c3ddb8829 100644 --- a/docs/src/visualization.md +++ b/docs/src/visualization.md @@ -19,7 +19,7 @@ viz! We can visualize a single geometry or multiple geometries in a vector: ```@example viz -triangles = [rand(Triangle{2}) for _ in 1:10] +triangles = rand(Triangle{2}, 10) viz(triangles, color = 1:10) ``` diff --git a/src/geometries.jl b/src/geometries.jl index c8ca9103d..07fcd437d 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -51,6 +51,14 @@ bounding box of the `geometry`. """ Base.extrema(g::Geometry) = extrema(boundingbox(g)) +# ------- +# RANDOM +# ------- + +Random.rand(::Type{G}) where {G<:Geometry} = rand(Random.default_rng(), G) +Random.rand(::Type{G}, n::Int) where {G<:Geometry} = rand(Random.default_rng(), G, n) +Random.rand(rng::Random.AbstractRNG, ::Type{G}, n::Int) where {G<:Geometry} = [rand(rng, G) for _ in 1:n] + # ----------- # IO METHODS # ----------- diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index 4ffc836d0..bd811d34b 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -33,5 +33,5 @@ function (h::Hexahedron)(u, v, w) ) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Hexahedron{Dim}}) where {Dim} = +Random.rand(rng::Random.AbstractRNG, ::Type{Hexahedron{Dim}}) where {Dim} = Hexahedron(ntuple(i -> rand(rng, Point{Dim}), 8)) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 2233a17ea..3c75b01eb 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -67,8 +67,7 @@ innerangles(ngon::Ngon) = innerangles(boundary(ngon)) signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ngon{N,Dim}}) where {N,Dim} = - Ngon{N}(ntuple(i -> rand(rng, Point{Dim}), N)) +Random.rand(rng::Random.AbstractRNG, ::Type{Ngon{N,Dim}}) where {N,Dim} = Ngon{N}(ntuple(i -> rand(rng, Point{Dim}), N)) # ---------- # TRIANGLES diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 8b119f343..d8a301e28 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -118,4 +118,4 @@ function Base.show(io::IO, ::MIME"text/plain", p::PolyArea) end end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:PolyArea{Dim}}) where {Dim} = PolyArea(rand(rng, Ring{Dim})) +Random.rand(rng::Random.AbstractRNG, ::Type{PolyArea{Dim}}) where {Dim} = PolyArea(rand(rng, Ring{Dim})) diff --git a/src/polytopes/pyramid.jl b/src/polytopes/pyramid.jl index b10ecae53..da59a89dc 100644 --- a/src/polytopes/pyramid.jl +++ b/src/polytopes/pyramid.jl @@ -14,5 +14,4 @@ nvertices(::Type{<:Pyramid}) = 5 Base.isapprox(p₁::Pyramid, p₂::Pyramid; kwargs...) = all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Pyramid{Dim}}) where {Dim} = - Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) +Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid{Dim}}) where {Dim} = Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index cf84c9369..b21ca101e 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -47,10 +47,10 @@ Base.open(r::Ring) = open(Rope(parent(r.vertices))) # do not change which vertex comes first for closed chains Base.reverse!(r::Ring) = (reverse!(@view r.vertices[(begin + 1):end]); r) -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Ring{Dim}}) where {Dim} - v = [rand(rng, Point{Dim}) for _ in 1:rand(rng, 3:50)] +function Random.rand(rng::Random.AbstractRNG, ::Type{Ring{Dim}}) where {Dim} + v = rand(rng, Point{Dim}, rand(rng, 3:50)) while first(v) == last(v) - v = [rand(rng, Point{Dim}) for _ in 1:rand(rng, 3:50)] + v = rand(rng, Point{Dim}, rand(rng, 3:50)) end Ring(v) end diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index 603328166..45752203c 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -32,5 +32,4 @@ Base.open(r::Rope) = r Base.reverse!(r::Rope) = (reverse!(r.vertices); r) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{<:Rope{Dim}}) where {Dim} = - Rope([rand(rng, Point{Dim}) for _ in 1:rand(rng, 2:50)]) +Random.rand(rng::Random.AbstractRNG, ::Type{Rope{Dim}}) where {Dim} = Rope(rand(rng, Point{Dim}, rand(rng, 2:50))) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index b91d0713e..bc1b61b0a 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -37,5 +37,4 @@ function (s::Segment)(t) a + t * (b - a) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Segment{Dim}}) where {Dim} = - Segment(ntuple(i -> rand(rng, Point{Dim}), 2)) +Random.rand(rng::Random.AbstractRNG, ::Type{Segment{Dim}}) where {Dim} = Segment(ntuple(i -> rand(rng, Point{Dim}), 2)) diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index dfaeaa027..fd7baf79d 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -23,5 +23,5 @@ function (t::Tetrahedron)(u, v, w) Point(coords(v₁ * z + v₂ * u + v₃ * v + v₄ * w)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Tetrahedron{Dim}}) where {Dim} = +Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron{Dim}}) where {Dim} = Tetrahedron(ntuple(i -> rand(rng, Point{Dim}), 4)) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index 9fe6d7e68..b3951b499 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -63,5 +63,5 @@ function (b::Ball{3})(ρ, θ, φ) c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ball{Dim}}) where {Dim} = +Random.rand(rng::Random.AbstractRNG, ::Type{Ball{Dim}}) where {Dim} = Ball(rand(rng, Point{Dim}), rand(rng, Met{Float64})) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index cc8184c20..08c4f96f5 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -108,8 +108,7 @@ function (curve::BezierCurve)(t, ::Horner) Point(coords(b₀)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{BezierCurve{Dim}}) where {Dim} = - BezierCurve([rand(rng, Point{Dim}) for _ in 1:5]) +Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve{Dim}}) where {Dim} = BezierCurve(rand(rng, Point{Dim}, 5)) # ----------- # IO METHODS diff --git a/src/primitives/box.jl b/src/primitives/box.jl index d3a8778e5..19065b387 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -54,7 +54,7 @@ function (b::Box)(uv...) b.min + uv .* (b.max - b.min) end -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Box{Dim}}) where {Dim} +function Random.rand(rng::Random.AbstractRNG, ::Type{Box{Dim}}) where {Dim} min = rand(rng, Point{Dim}) max = min + rand(rng, Vec{Dim,Met{Float64}}) Box(min, max) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index ebcf57a13..407f299c5 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -64,4 +64,4 @@ function (c::Circle)(φ) c.plane(u, v) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Circle}) = Circle(rand(rng, Plane), rand(rng, Met{Float64})) +Random.rand(rng::Random.AbstractRNG, ::Type{Circle}) = Circle(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 06df40c42..e42b7a30b 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -29,4 +29,4 @@ height(c::Cone) = norm(center(base(c)) - apex(c)) halfangle(c::Cone) = atan(radius(base(c)), height(c)) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cone}) = Cone(rand(rng, Disk), rand(rng, Point{3})) +Random.rand(rng::Random.AbstractRNG, ::Type{Cone}) = Cone(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 0a2258b64..2bb4a37d0 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -40,5 +40,4 @@ function (c::ConeSurface)(φ, h) s(T(φ)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ConeSurface}) = - ConeSurface(rand(rng, Disk), rand(rng, Point{3})) +Random.rand(rng::Random.AbstractRNG, ::Type{ConeSurface}) = ConeSurface(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index a75237be3..3e5d35f1a 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -100,5 +100,5 @@ function (c::Cylinder)(ρ, φ, z) s(T(z)) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Cylinder}) = +Random.rand(rng::Random.AbstractRNG, ::Type{Cylinder}) = Cylinder(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 80dd451ec..fe3310e41 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -123,7 +123,7 @@ function (c::CylinderSurface)(φ, z) o + Q' * to(p) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{CylinderSurface}) = +Random.rand(rng::Random.AbstractRNG, ::Type{CylinderSurface}) = CylinderSurface(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) function hasintersectingplanes(c::CylinderSurface) diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index 0b0b0e2f7..18e9c88a0 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -45,4 +45,4 @@ function (d::Disk)(ρ, φ) d.plane(u, v) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Disk}) = Disk(rand(rng, Plane), rand(rng, Met{Float64})) +Random.rand(rng::Random.AbstractRNG, ::Type{Disk}) = Disk(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 5ea98a722..002e1460c 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -49,7 +49,7 @@ function (e::Ellipsoid)(θ, φ) c + R * Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ellipsoid}) = Ellipsoid( +Random.rand(rng::Random.AbstractRNG, ::Type{Ellipsoid}) = Ellipsoid( (rand(rng, Met{Float64}), rand(rng, Met{Float64}), rand(rng, Met{Float64})), rand(rng, Point{3}), rand(rng, QuatRotation) diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index 2fe181597..c7e0ea8e6 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -38,7 +38,7 @@ height(f::Frustum) = height(boundary(f)) axis(f::Frustum) = axis(boundary(f)) -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Frustum}) +function Random.rand(rng::Random.AbstractRNG, ::Type{Frustum}) bottom = rand(rng, Disk) ax = normal(plane(bottom)) topplane = Plane(center(bottom) + rand() * ax, ax) diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 8df12f246..df1fe55d2 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -66,7 +66,7 @@ function (f::FrustumSurface)(φ, z) center(bottom(f)) + Q' * p end -function Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{FrustumSurface}) +function Random.rand(rng::Random.AbstractRNG, ::Type{FrustumSurface}) bottom = rand(rng, Disk) ax = normal(plane(bottom)) topplane = Plane(center(bottom) + rand() * ax, ax) diff --git a/src/primitives/line.jl b/src/primitives/line.jl index b148e1f75..ed18d82df 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -24,5 +24,4 @@ lentype(::Type{<:Line{Dim,P}}) where {Dim,P} = lentype(P) (l::Line)(t) = l.a + t * (l.b - l.a) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Line{Dim}}) where {Dim} = - Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) +Random.rand(rng::Random.AbstractRNG, ::Type{Line{Dim}}) where {Dim} = Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index b5a0abcc2..7d8c8b5b7 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -112,5 +112,5 @@ function (p::ParaboloidSurface)(ρ, θ) c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{ParaboloidSurface}) = +Random.rand(rng::Random.AbstractRNG, ::Type{ParaboloidSurface}) = ParaboloidSurface(rand(rng, Point{3}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index bb80f64f9..17b00258c 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -52,5 +52,4 @@ Base.isapprox(p₁::Plane, p₂::Plane) = (p::Plane)(u, v) = p.p + u * p.u + v * p.v -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Plane}) = - Plane(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}})) +Random.rand(rng::Random.AbstractRNG, ::Type{Plane}) = Plane(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}})) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index bfc71f14b..e6afa8147 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -119,8 +119,7 @@ See . ∠(A::P, B::P, C::P) where {P<:Point{2}} = ∠(A - B, C - B) ∠(A::P, B::P, C::P) where {P<:Point{3}} = ∠(A - B, C - B) -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Point{Dim}}) where {Dim} = - Point(rand(rng, Cartesian{NoDatum,Dim})) +Random.rand(rng::Random.AbstractRNG, ::Type{Point{Dim}}) where {Dim} = Point(rand(rng, Cartesian{NoDatum,Dim})) # ----------- # IO METHODS diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 3802074d1..1a95cd0a4 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -29,5 +29,5 @@ function (r::Ray)(t) r.p + t * r.v end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Ray{Dim}}) where {Dim} = +Random.rand(rng::Random.AbstractRNG, ::Type{Ray{Dim}}) where {Dim} = Ray(rand(rng, Point{Dim}), rand(rng, Vec{Dim,Met{Float64}})) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 5d6c4b60c..9800d7095 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -98,5 +98,5 @@ function (s::Sphere{3})(θ, φ) c + Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Sphere{Dim}}) where {Dim} = +Random.rand(rng::Random.AbstractRNG, ::Type{Sphere{Dim}}) where {Dim} = Sphere(rand(rng, Point{Dim}), rand(rng, Met{Float64})) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 794b1d7ce..338a5562f 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -75,5 +75,5 @@ function (t::Torus)(θ, φ) c + Q * Vec(x, y, z) end -Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Torus}) = +Random.rand(rng::Random.AbstractRNG, ::Type{Torus}) = Torus(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) From 894fe529d04a9b31ba5809713c24cdb346f25a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 29 May 2024 16:32:22 -0300 Subject: [PATCH 058/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c72c699ca..284b0e293 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.1" +version = "0.43.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 94aa611b09e87cb2ae6cefd3454177b248b7d848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 29 May 2024 16:46:51 -0300 Subject: [PATCH 059/423] Update rand(Point) usage --- test/discretization.jl | 2 +- test/domains.jl | 2 +- test/hulls.jl | 4 ++-- test/merging.jl | 4 ++-- test/mesh.jl | 4 ++-- test/neighborsearch.jl | 6 +++--- test/pointification.jl | 2 +- test/polytopes.jl | 4 ++-- test/runtests.jl | 5 ----- test/sets.jl | 6 +++--- test/subdomains.jl | 2 +- test/transforms.jl | 4 ++-- 12 files changed, 20 insertions(+), 25 deletions(-) diff --git a/test/discretization.jl b/test/discretization.jl index ec5c48dae..cc65c1963 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -403,7 +403,7 @@ grid = CartesianGrid(10) @test discretize(grid) == grid - mesh = SimpleMesh(randpoint2(3), connect.([(1, 2, 3)])) + mesh = SimpleMesh(rand(Point{2}, 3), connect.([(1, 2, 3)])) @test discretize(mesh) == mesh end diff --git a/test/domains.jl b/test/domains.jl index 5d56259f6..f65fb45d9 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -23,7 +23,7 @@ # concatenation dom1 = DummyDomain(point(0, 0)) dom2 = DummyDomain(point(3, 3)) - dom3 = PointSet(randpoint2(3)) + dom3 = PointSet(rand(Point{2}, 3)) @test vcat(dom1, dom2) == GeometrySet([collect(dom1); collect(dom2)]) @test vcat(dom2, dom3) == GeometrySet([collect(dom2); collect(dom3)]) @test vcat(dom3, dom1) == GeometrySet([collect(dom3); collect(dom1)]) diff --git a/test/hulls.jl b/test/hulls.jl index f9c34be91..25a2b4a31 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -2,12 +2,12 @@ @testset "Basic" begin for method in [GrahamScan(), JarvisMarch()] # basic test - pts = randpoint2(100) + pts = rand(Point{2}, 100) chul = hull(pts, method) @test all(pts .∈ Ref(chul)) # duplicated points - pts = [randpoint2(100); randpoint2(100)] + pts = [rand(Point{2}, 100); rand(Point{2}, 100)] chul = hull(pts, method) @test all(pts .∈ Ref(chul)) diff --git a/test/merging.jl b/test/merging.jl index 92642692b..cf41bef71 100644 --- a/test/merging.jl +++ b/test/merging.jl @@ -14,8 +14,8 @@ @test m isa Multi @test eltype(parent(m)) <: Primitive - m1 = SimpleMesh(randpoint3(3), [connect((1, 2, 3))]) - m2 = SimpleMesh(randpoint3(4), [connect((1, 2, 3, 4))]) + m1 = SimpleMesh(rand(Point{3}, 3), [connect((1, 2, 3))]) + m2 = SimpleMesh(rand(Point{3}, 4), [connect((1, 2, 3, 4))]) m = merge(m1, m2) @test m isa Mesh @test eltype(m) <: Ngon diff --git a/test/mesh.jl b/test/mesh.jl index 2d3981151..95536333c 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -617,13 +617,13 @@ @test_throws BoundsError grid[3:11, :] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 - points = randpoint2(5) + points = rand(Point{2}, 5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test nvertices(mesh) == length(vertices(mesh)) == 5 # single vertex access - points = randpoint2(5) + points = rand(Point{2}, 5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test vertex(mesh, 1) == points[1] diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index c01a8fc11..a3c39770b 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -29,7 +29,7 @@ @test Set(n) == Set([32041, 32400, 32401, 32760]) # construct from vector of geometries - s = BallSearch(randpoint2(100), MetricBall(T(1))) + s = BallSearch(rand(Point{2}, 100), MetricBall(T(1))) @test s isa BallSearch end @@ -58,7 +58,7 @@ @test Set(n[1:nn]) == Set([100, 99, 90]) # construct from vector of geometries - s = KNearestSearch(randpoint2(100), 3) + s = KNearestSearch(rand(Point{2}, 100), 3) @test s isa KNearestSearch end @@ -107,7 +107,7 @@ @test length(d) == 4 # construct from vector of geometries - s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) + s = KBallSearch(rand(Point{2}, 100), 10, MetricBall(T(1))) @test s isa KBallSearch end end diff --git a/test/pointification.jl b/test/pointification.jl index 6406da54e..0b5bd0f1e 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -37,7 +37,7 @@ points = pointify(gset) @test points == [pointify(tri); pointify(quad)] - pts = randpoint2(100) + pts = rand(Point{2}, 100) pset = PointSet(pts) @test pointify(pset) == pts diff --git a/test/polytopes.jl b/test/polytopes.jl index 471f2e80b..e00410aff 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -604,8 +604,8 @@ end # invalid inner - outer = Ring(randpoint2(10)) - p1, p2 = randpoint2(2) + outer = Ring(rand(Point{2}, 10)) + p1, p2 = rand(Point{2}, 2) inner = Ring(p1, p1, p2) poly = PolyArea([outer, inner]) upoly = unique(poly) diff --git a/test/runtests.jl b/test/runtests.jl index 62e721bdc..ce0ee1e66 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -82,11 +82,6 @@ function cartgrid(dims::Dims{Dim}) where {Dim} CartesianGrid(dims, origin, spacing, offset) end -randpoint1(n) = randpoint(1, n) -randpoint2(n) = randpoint(2, n) -randpoint3(n) = randpoint(3, n) -randpoint(Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] - # dummy definitions include("dummy.jl") diff --git a/test/sets.jl b/test/sets.jl index e70ce488f..3f9e5c357 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -32,19 +32,19 @@ end @testset "PointSet" begin - pset = PointSet(randpoint1(100)) + pset = PointSet(rand(Point{1}, 100)) @test embeddim(pset) == 1 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{1} - pset = PointSet(randpoint2(100)) + pset = PointSet(rand(Point{2}, 100)) @test embeddim(pset) == 2 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{2} - pset = PointSet(randpoint3(100)) + pset = PointSet(rand(Point{3}, 100)) @test embeddim(pset) == 3 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 diff --git a/test/subdomains.jl b/test/subdomains.jl index ae33c6423..56cdcbbf0 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -1,5 +1,5 @@ @testset "SubDomains" begin - pset = PointSet(randpoint3(100)) + pset = PointSet(rand(Point{3}, 100)) inds = rand(1:100, 3) v = view(pset, inds) @test nelements(v) == 3 diff --git a/test/transforms.jl b/test/transforms.jl index d66873066..a47ac2320 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -915,7 +915,7 @@ # --------- f = StdCoords() - d = view(PointSet(randpoint2(100)), 1:50) + d = view(PointSet(rand(Point{2}, 100)), 1:50) r, c = TB.apply(f, d) @test all(sides(boundingbox(r)) .≤ oneunit(ℳ)) @test TB.revert(f, r, c) ≈ d @@ -973,7 +973,7 @@ @testset "Repair{7}" begin # mesh with inconsistent orientation - points = randpoint3(6) + points = rand(Point{3}, 6) connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) mesh = SimpleMesh(points, connec) rmesh = mesh |> Repair{7}() From 5541d62faf5c0b4f78eb74ebf721736305d19570 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 29 May 2024 16:57:36 -0300 Subject: [PATCH 060/423] Fix 'Point' documentation (#854) --- docs/src/geometries/primitives.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 0d88ba495..977672dda 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -25,9 +25,9 @@ rand(Point{3}, 100) |> viz ```@docs to(::Point) --(::Point, ::Point) -+(::Point, ::Vec) --(::Point, ::Vec) +-(::Point{Dim}, ::Point{Dim}) where {Dim} ++(::Point{Dim}, ::Vec{Dim}) where {Dim} +-(::Point{Dim}, ::Vec{Dim}) where {Dim} ``` ### Ray From a72bfc823e776cba12516dcad6ee650fdbb15ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 29 May 2024 17:00:00 -0300 Subject: [PATCH 061/423] Revert "Update rand(Point) usage" This reverts commit 1efdc5d5b3df64f063f58183a67ca16ae618363a. --- test/discretization.jl | 2 +- test/domains.jl | 2 +- test/hulls.jl | 4 ++-- test/merging.jl | 4 ++-- test/mesh.jl | 4 ++-- test/neighborsearch.jl | 6 +++--- test/pointification.jl | 2 +- test/polytopes.jl | 4 ++-- test/runtests.jl | 5 +++++ test/sets.jl | 6 +++--- test/subdomains.jl | 2 +- test/transforms.jl | 4 ++-- 12 files changed, 25 insertions(+), 20 deletions(-) diff --git a/test/discretization.jl b/test/discretization.jl index cc65c1963..ec5c48dae 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -403,7 +403,7 @@ grid = CartesianGrid(10) @test discretize(grid) == grid - mesh = SimpleMesh(rand(Point{2}, 3), connect.([(1, 2, 3)])) + mesh = SimpleMesh(randpoint2(3), connect.([(1, 2, 3)])) @test discretize(mesh) == mesh end diff --git a/test/domains.jl b/test/domains.jl index f65fb45d9..5d56259f6 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -23,7 +23,7 @@ # concatenation dom1 = DummyDomain(point(0, 0)) dom2 = DummyDomain(point(3, 3)) - dom3 = PointSet(rand(Point{2}, 3)) + dom3 = PointSet(randpoint2(3)) @test vcat(dom1, dom2) == GeometrySet([collect(dom1); collect(dom2)]) @test vcat(dom2, dom3) == GeometrySet([collect(dom2); collect(dom3)]) @test vcat(dom3, dom1) == GeometrySet([collect(dom3); collect(dom1)]) diff --git a/test/hulls.jl b/test/hulls.jl index 25a2b4a31..f9c34be91 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -2,12 +2,12 @@ @testset "Basic" begin for method in [GrahamScan(), JarvisMarch()] # basic test - pts = rand(Point{2}, 100) + pts = randpoint2(100) chul = hull(pts, method) @test all(pts .∈ Ref(chul)) # duplicated points - pts = [rand(Point{2}, 100); rand(Point{2}, 100)] + pts = [randpoint2(100); randpoint2(100)] chul = hull(pts, method) @test all(pts .∈ Ref(chul)) diff --git a/test/merging.jl b/test/merging.jl index cf41bef71..92642692b 100644 --- a/test/merging.jl +++ b/test/merging.jl @@ -14,8 +14,8 @@ @test m isa Multi @test eltype(parent(m)) <: Primitive - m1 = SimpleMesh(rand(Point{3}, 3), [connect((1, 2, 3))]) - m2 = SimpleMesh(rand(Point{3}, 4), [connect((1, 2, 3, 4))]) + m1 = SimpleMesh(randpoint3(3), [connect((1, 2, 3))]) + m2 = SimpleMesh(randpoint3(4), [connect((1, 2, 3, 4))]) m = merge(m1, m2) @test m isa Mesh @test eltype(m) <: Ngon diff --git a/test/mesh.jl b/test/mesh.jl index 95536333c..2d3981151 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -617,13 +617,13 @@ @test_throws BoundsError grid[3:11, :] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 - points = rand(Point{2}, 5) + points = randpoint2(5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test nvertices(mesh) == length(vertices(mesh)) == 5 # single vertex access - points = rand(Point{2}, 5) + points = randpoint2(5) connec = [connect((1, 2, 3))] mesh = SimpleMesh(points, connec) @test vertex(mesh, 1) == points[1] diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index a3c39770b..c01a8fc11 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -29,7 +29,7 @@ @test Set(n) == Set([32041, 32400, 32401, 32760]) # construct from vector of geometries - s = BallSearch(rand(Point{2}, 100), MetricBall(T(1))) + s = BallSearch(randpoint2(100), MetricBall(T(1))) @test s isa BallSearch end @@ -58,7 +58,7 @@ @test Set(n[1:nn]) == Set([100, 99, 90]) # construct from vector of geometries - s = KNearestSearch(rand(Point{2}, 100), 3) + s = KNearestSearch(randpoint2(100), 3) @test s isa KNearestSearch end @@ -107,7 +107,7 @@ @test length(d) == 4 # construct from vector of geometries - s = KBallSearch(rand(Point{2}, 100), 10, MetricBall(T(1))) + s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) @test s isa KBallSearch end end diff --git a/test/pointification.jl b/test/pointification.jl index 0b5bd0f1e..6406da54e 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -37,7 +37,7 @@ points = pointify(gset) @test points == [pointify(tri); pointify(quad)] - pts = rand(Point{2}, 100) + pts = randpoint2(100) pset = PointSet(pts) @test pointify(pset) == pts diff --git a/test/polytopes.jl b/test/polytopes.jl index e00410aff..471f2e80b 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -604,8 +604,8 @@ end # invalid inner - outer = Ring(rand(Point{2}, 10)) - p1, p2 = rand(Point{2}, 2) + outer = Ring(randpoint2(10)) + p1, p2 = randpoint2(2) inner = Ring(p1, p1, p2) poly = PolyArea([outer, inner]) upoly = unique(poly) diff --git a/test/runtests.jl b/test/runtests.jl index ce0ee1e66..62e721bdc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -82,6 +82,11 @@ function cartgrid(dims::Dims{Dim}) where {Dim} CartesianGrid(dims, origin, spacing, offset) end +randpoint1(n) = randpoint(1, n) +randpoint2(n) = randpoint(2, n) +randpoint3(n) = randpoint(3, n) +randpoint(Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] + # dummy definitions include("dummy.jl") diff --git a/test/sets.jl b/test/sets.jl index 3f9e5c357..e70ce488f 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -32,19 +32,19 @@ end @testset "PointSet" begin - pset = PointSet(rand(Point{1}, 100)) + pset = PointSet(randpoint1(100)) @test embeddim(pset) == 1 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{1} - pset = PointSet(rand(Point{2}, 100)) + pset = PointSet(randpoint2(100)) @test embeddim(pset) == 2 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{2} - pset = PointSet(rand(Point{3}, 100)) + pset = PointSet(randpoint3(100)) @test embeddim(pset) == 3 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 diff --git a/test/subdomains.jl b/test/subdomains.jl index 56cdcbbf0..ae33c6423 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -1,5 +1,5 @@ @testset "SubDomains" begin - pset = PointSet(rand(Point{3}, 100)) + pset = PointSet(randpoint3(100)) inds = rand(1:100, 3) v = view(pset, inds) @test nelements(v) == 3 diff --git a/test/transforms.jl b/test/transforms.jl index a47ac2320..d66873066 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -915,7 +915,7 @@ # --------- f = StdCoords() - d = view(PointSet(rand(Point{2}, 100)), 1:50) + d = view(PointSet(randpoint2(100)), 1:50) r, c = TB.apply(f, d) @test all(sides(boundingbox(r)) .≤ oneunit(ℳ)) @test TB.revert(f, r, c) ≈ d @@ -973,7 +973,7 @@ @testset "Repair{7}" begin # mesh with inconsistent orientation - points = rand(Point{3}, 6) + points = randpoint3(6) connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) mesh = SimpleMesh(points, connec) rmesh = mesh |> Repair{7}() From 500a4f894b7a1c8ccbd1fcd61fe968aa8c1e36f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 29 May 2024 17:03:41 -0300 Subject: [PATCH 062/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 284b0e293..329fc84ee 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.2" +version = "0.43.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 92682972b914ee53011b5da8ef06908ae491b1c4 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 30 May 2024 11:44:54 -0300 Subject: [PATCH 063/423] Add a `convert` method for `Point` (#855) * Add a 'convert' method for 'Point' * Apply suggestions * Fix code * Fix typo --- src/primitives/point.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index e6afa8147..89e53f477 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -41,6 +41,10 @@ end # convenience constructor Point(coords...) = Point(Cartesian(coords...)) +# conversions +Base.convert(::Type{Point{Dim,CRSₜ}}, p::Point{Dim,CRSₛ}) where {Dim,CRSₜ,CRSₛ} = Point(convert(CRSₜ, p.coords)) +Base.convert(::Type{Point{Dim,CRS}}, p::Point{Dim,CRS}) where {Dim,CRS} = p + paramdim(::Type{<:Point}) = 0 lentype(::Type{<:Point{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) From 4fde1cec3d343f7a73590cd5ccb89e914fc858b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 30 May 2024 11:47:55 -0300 Subject: [PATCH 064/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 329fc84ee..8120a32b3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.3" +version = "0.43.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f30c4c76926021177d4557741a9e2901231b5939 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 30 May 2024 14:11:08 -0300 Subject: [PATCH 065/423] Add tests for point conversion (#856) --- test/primitives.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/primitives.jl b/test/primitives.jl index 7e77a0013..b02348c95 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -86,6 +86,15 @@ @test unit(Meshes.lentype(p)) == u"m" @test Unitful.numtype(Meshes.lentype(p)) === Float32 + # conversions + P = typeof(point(1, 1)) + p1 = Point(1.0, 1.0) + p2 = convert(P, p1) + @test p2 isa P + p1 = Point(1.0f0, 1.0f0) + p2 = convert(P, p1) + @test p2 isa P + # `to` function p1 = point(1, 1) p2 = point(1, 1, 1) From b2c08e11bccc2a16c95e011b5cdd44c2151445c5 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 30 May 2024 16:50:36 -0300 Subject: [PATCH 066/423] Refactor `MetricBall` struct (#857) * Refactor 'MetricBall' struct * Update implementation of 'isotropic' function --- src/neighborhoods/metricball.jl | 25 +++++++++++-------------- test/neighborhoods.jl | 12 ++++++------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/neighborhoods/metricball.jl b/src/neighborhoods/metricball.jl index 1ff1e2579..2ad8c5262 100644 --- a/src/neighborhoods/metricball.jl +++ b/src/neighborhoods/metricball.jl @@ -32,24 +32,22 @@ julia> mahalanobis = MetricBall((3.0, 2.0, 1.0)) ``` """ struct MetricBall{Dim,ℒ<:Len,R,M} <: Neighborhood - radii::SVector{Dim,ℒ} + radii::NTuple{Dim,ℒ} rotation::R # state fields metric::M - MetricBall(radii::SVector{Dim,ℒ}, rotation::R, metric::M) where {Dim,ℒ<:Len,R,M} = + MetricBall(radii::NTuple{Dim,ℒ}, rotation::R, metric::M) where {Dim,ℒ<:Len,R,M} = new{Dim,float(ℒ),R,M}(radii, rotation, metric) end -MetricBall(radii::SVector, rotation, metric) = MetricBall(addunit(radii, u"m"), rotation, metric) - -function MetricBall(radii::SVector{Dim,ℒ}, rotation=nothing) where {Dim,ℒ<:Len} +function MetricBall(radii::NTuple{Dim,ℒ}, rotation=nothing) where {Dim,ℒ<:Len} # scaling matrix - Λ = Diagonal((oneunit(ℒ) ./ radii) .^ 2) + Λ = Diagonal(SVector((oneunit(ℒ) ./ radii) .^ 2)) # rotation matrix - R = isnothing(rotation) ? default_rotation(Val(Dim), numtype(ℒ)) : rotation + R = isnothing(rotation) ? default_rotation(Val(Dim), float(numtype(ℒ))) : rotation # anisotropy matrix M = Symmetric(R * Λ * R') @@ -60,14 +58,13 @@ function MetricBall(radii::SVector{Dim,ℒ}, rotation=nothing) where {Dim,ℒ<:L MetricBall(radii, R, metric) end -MetricBall(radii::SVector, rotation=nothing) = MetricBall(addunit(radii, u"m"), rotation) +MetricBall(radii::NTuple{Dim,Len}, rotation=nothing) where {Dim} = MetricBall(promote(radii...), rotation) -MetricBall(radii::Tuple, rotation=nothing) = MetricBall(SVector(radii), rotation) +MetricBall(radii::Tuple, rotation=nothing) = MetricBall(addunit.(radii, u"m"), rotation) -# avoid silent calls to inner constructor -MetricBall(radii::AbstractVector{T}, rotation=nothing) where {T} = MetricBall(SVector{length(radii),T}(radii), rotation) +MetricBall(radius::Len, metric=Euclidean()) = MetricBall((radius,), nothing, metric) -MetricBall(radius::Number, metric=Euclidean()) = MetricBall(SVector(radius), nothing, metric) +MetricBall(radius::Number, metric=Euclidean()) = MetricBall(addunit(radius, u"m"), metric) default_rotation(::Val{2}, T) = one(Angle2d{T}) default_rotation(::Val{3}, T) = one(QuatRotation{T}) @@ -111,7 +108,7 @@ end Tells whether or not the metric `ball` is isotropic, i.e. if all its radii are equal. """ -isisotropic(ball::MetricBall) = length(unique(ball.radii)) == 1 +isisotropic(ball::MetricBall) = allequal(ball.radii) function *(α::Real, ball::MetricBall) if ball.metric isa Mahalanobis @@ -123,7 +120,7 @@ end function Base.show(io::IO, ball::MetricBall) n = length(ball.radii) - r = n > 1 ? Tuple(ball.radii) : first(ball.radii) + r = n > 1 ? ball.radii : first(ball.radii) m = nameof(typeof(ball.metric)) print(io, "MetricBall($r, $m)") end diff --git a/test/neighborhoods.jl b/test/neighborhoods.jl index aacf9459a..2a4e51647 100644 --- a/test/neighborhoods.jl +++ b/test/neighborhoods.jl @@ -6,7 +6,7 @@ m = metric(b) @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r @test evaluate(m, T[0] * u"m", T[1] * u"m") > r - @test radii(b) == T[1 / 2] * u"m" + @test radii(b) == (T(1 / 2) * u"m",) b = MetricBall(T(1)) r = radius(b) @@ -56,22 +56,22 @@ @test evaluate(m, T[-1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(2) * u"m" # make sure the correct constructor is called - m = metric(MetricBall(T[1.0, 0.5, 0.2], RotXYX(T(0), T(0), T(0)))) + m = metric(MetricBall(T.((1.0, 0.5, 0.2)), RotXYX(T(0), T(0), T(0)))) @test m isa Mahalanobis # make sure the angle is clockwise - m = metric(MetricBall(T[20.0, 5.0], Angle2d(T(π / 2)))) + m = metric(MetricBall(T.((20.0, 5.0)), Angle2d(T(π / 2)))) @test m isa Mahalanobis @test evaluate(m, T[1.0, 0.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.2) * u"m" @test evaluate(m, T[0.0, 1.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.05) * u"m" # basic multiplication @test 2MetricBall(T(1)) == MetricBall(T(2)) - @test 2MetricBall(T[1, 2, 3]) == MetricBall(T[2, 4, 6]) + @test 2MetricBall(T.((1, 2, 3))) == MetricBall(T.((2, 4, 6))) # access to rotation @test rotation(MetricBall(T(1))) == I - @test rotation(MetricBall(T[1, 2, 3])) == I - @test rotation(MetricBall(T[1, 2], Angle2d(T(π / 2)))) == Angle2d(T(π / 2)) + @test rotation(MetricBall(T.((1, 2, 3)))) == I + @test rotation(MetricBall(T.((1, 2)), Angle2d(T(π / 2)))) == Angle2d(T(π / 2)) end end From 9cbf9f8c845decfa30761a6c4cc20c13646e98dc Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 31 May 2024 15:11:16 -0300 Subject: [PATCH 067/423] Refactor the implementation of the `evaluate` function (#858) * Refactor the implementation of the 'evaluate' function * Add tests * Apply suggestions --- src/Meshes.jl | 2 +- src/distances.jl | 62 +++++++++++++++++++++++++++++++++++++---------- test/distances.jl | 20 ++++++++++++--- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index f992ce9d4..abd4be940 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -15,7 +15,7 @@ using Random using Bessels: gamma using Unitful: AbstractQuantity, numtype using StatsBase: AbstractWeights, Weights, quantile -using Distances: PreMetric, Euclidean, Mahalanobis, evaluate +using Distances: PreMetric, Euclidean, Haversine, Mahalanobis, SphericalAngle, evaluate, result_type using Rotations: Rotation, QuatRotation, Angle2d, rotation_between using NearestNeighbors: KDTree, BallTree, knn, inrange using Transducers: Filter, Map, TakeWhile, tcollect, ⨟ diff --git a/src/distances.jl b/src/distances.jl index 668115975..b3e14b521 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -6,11 +6,11 @@ evaluate(d::PreMetric, g::Geometry, p::Point) = evaluate(d, p, g) """ - evaluate(Euclidean(), point, line) + evaluate(distance::Euclidean, point, line) -Evaluate the Euclidean distance between `point` and `line`. +Evaluate the Euclidean `distance` between `point` and `line`. """ -function evaluate(::Euclidean, p::Point, l::Line) +function evaluate(::Euclidean, p::Point{Dim}, l::Line{Dim}) where {Dim} a, b = l(0), l(1) u = p - a v = b - a @@ -19,24 +19,60 @@ function evaluate(::Euclidean, p::Point, l::Line) end """ - evaluate(Euclidean(), line1, line2) -Evaluate the minimum Euclidean distance between `line1` and `line2`. + evaluate(distance::Euclidean, line₁, line₂) + +Evaluate the minimum Euclidean `distance` between `line₁` and `line₂`. """ -function evaluate(::Euclidean, line1::Line{Dim}, line2::Line{Dim}) where {Dim} - λ₁, λ₂, r, rₐ = intersectparameters(line1(0), line1(1), line2(0), line2(1)) +function evaluate(d::Euclidean, l₁::Line{Dim}, l₂::Line{Dim}) where {Dim} + λ₁, λ₂, r, rₐ = intersectparameters(l₁(0), l₁(1), l₂(0), l₂(1)) if (r == rₐ == 2) || (r == rₐ == 1) # lines intersect or are colinear - return zero(lentype(line1)) + return zero(result_type(d, lentype(l₁), lentype(l₂))) elseif (r == 1) && (rₐ == 2) # lines are parallel - return evaluate(Euclidean(), line1(0), line2) + return evaluate(d, l₁(0), l₂) else # get distance between closest points on each line - return evaluate(Euclidean(), line1(λ₁), line2(λ₂)) + return evaluate(d, l₁(λ₁), l₂(λ₂)) end end """ - evaluate(::PreMetric, point1, point2) + evaluate(distance::PreMetric, point₁, point₂) -Evaluate pre-metric between coordinates of `point1` and `point2`. +Evaluate pre-metric `distance` between coordinates of `point₁` and `point₂`. """ -evaluate(d::PreMetric, p1::Point, p2::Point) = evaluate(d, to(p1), to(p2)) +function evaluate(d::PreMetric, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} + u₁ = unit(Meshes.lentype(p₁)) + u₂ = unit(Meshes.lentype(p₂)) + u = Unitful.promote_unit(u₁, u₂) + v₁ = ustrip.(u, to(p₁)) + v₂ = ustrip.(u, to(p₂)) + evaluate(d, v₁, v₂) * u +end + +# -------------- +# SPECIAL CASES +# -------------- + +function evaluate(d::Haversine, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:LatLon}) where {Dim} + uᵣ = unit(d.radius) + u = uᵣ === NoUnits ? u"m" : uᵣ + latlon₁ = coords(p₁) + latlon₂ = coords(p₂) + v₁ = SVector(latlon₁.lon, latlon₁.lat) + v₂ = SVector(latlon₂.lon, latlon₂.lat) + evaluate(d, v₁, v₂) * u +end + +evaluate(d::Haversine, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} = + evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) + +function evaluate(d::SphericalAngle, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:LatLon}) where {Dim} + latlon₁ = coords(p₁) + latlon₂ = coords(p₂) + v₁ = SVector(deg2rad(latlon₁.lon), deg2rad(latlon₁.lat)) + v₂ = SVector(deg2rad(latlon₂.lon), deg2rad(latlon₂.lat)) + evaluate(d, v₁, v₂) * u"rad" +end + +evaluate(d::SphericalAngle, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} = + evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) diff --git a/test/distances.jl b/test/distances.jl index eeda7b015..024bec1b4 100644 --- a/test/distances.jl +++ b/test/distances.jl @@ -4,9 +4,6 @@ @test evaluate(Euclidean(), p, l) == T(1) * u"m" @test evaluate(Euclidean(), l, p) == T(1) * u"m" - p1, p2 = point(1, 0), point(0, 1) - @test evaluate(Chebyshev(), p1, p2) == T(1) * u"m" - p = point(68, 259) l = Line(point(68, 260), point(69, 261)) @test evaluate(Euclidean(), p, l) ≤ T(0.8) * u"m" @@ -20,4 +17,21 @@ @test evaluate(Euclidean(), line1, line3) ≈ T(1) * u"m" @test evaluate(Euclidean(), line1, line4) ≈ T(0) * u"m" @test evaluate(Euclidean(), line1, line5) ≈ T(0) * u"m" + + p1, p2 = point(1, 0), point(0, 1) + @test evaluate(Chebyshev(), p1, p2) == T(1) * u"m" + @test evaluate(Euclidean(), p1, p2) == T(√2) * u"m" + + latlon1 = LatLon(T(0), T(0)) + latlon2 = LatLon(T(1), T(0)) + cart1 = convert(Cartesian, latlon1) + cart2 = convert(Cartesian, latlon2) + p1 = Point(latlon1) + p2 = Point(latlon2) + p3 = Point(cart1) + p4 = Point(cart2) + @test evaluate(Haversine(), p1, p2) ≈ T(111194.92664455874) * u"m" + @test evaluate(Haversine(), p3, p4) ≈ T(111194.92664455874) * u"m" + @test evaluate(SphericalAngle(), p1, p2) ≈ deg2rad(T(1) * u"°") + @test evaluate(SphericalAngle(), p3, p4) ≈ deg2rad(T(1) * u"°") end From a33cd1c777706172f792678b2b4cc4849c66f268 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 31 May 2024 16:38:46 -0300 Subject: [PATCH 068/423] Remove unnecessary code in viz (#859) --- ext/geometryset.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 22d102d5f..46c0a69e2 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -107,8 +107,6 @@ function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) else Makie.poly!(plot, polys, color=colors) end - - showfacets2D!(plot, geoms) end function vizgset3D!(plot, geoms, colorant) From 2f86b8f11d9e2daf0fe6e30beee0668212bf60fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 31 May 2024 16:39:04 -0300 Subject: [PATCH 069/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8120a32b3..1dc20fa34 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.4" +version = "0.43.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From a16654fc77caae02e8c5b857bcbdcedec6305243 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:50:48 -0300 Subject: [PATCH 070/423] Fix the implementation of the 'Haversine' distance (#860) --- src/distances.jl | 3 ++- test/distances.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/distances.jl b/src/distances.jl index b3e14b521..fd7cd6798 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -55,7 +55,8 @@ end function evaluate(d::Haversine, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:LatLon}) where {Dim} uᵣ = unit(d.radius) - u = uᵣ === NoUnits ? u"m" : uᵣ + # add default unit if necessary + u = uᵣ === NoUnits ? u"m" : NoUnits latlon₁ = coords(p₁) latlon₂ = coords(p₂) v₁ = SVector(latlon₁.lon, latlon₁.lat) diff --git a/test/distances.jl b/test/distances.jl index 024bec1b4..54056fbae 100644 --- a/test/distances.jl +++ b/test/distances.jl @@ -32,6 +32,10 @@ p4 = Point(cart2) @test evaluate(Haversine(), p1, p2) ≈ T(111194.92664455874) * u"m" @test evaluate(Haversine(), p3, p4) ≈ T(111194.92664455874) * u"m" + @test evaluate(Haversine(6371000u"m"), p1, p2) ≈ T(111194.92664455874) * u"m" + @test evaluate(Haversine(6371000u"m"), p3, p4) ≈ T(111194.92664455874) * u"m" + @test evaluate(Haversine(6371u"km"), p1, p2) ≈ T(111.19492664455874) * u"km" + @test evaluate(Haversine(6371u"km"), p3, p4) ≈ T(111.19492664455874) * u"km" @test evaluate(SphericalAngle(), p1, p2) ≈ deg2rad(T(1) * u"°") @test evaluate(SphericalAngle(), p3, p4) ≈ deg2rad(T(1) * u"°") end From 4762d7287047d056422d64938c49baf2bb70f024 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:58:13 -0300 Subject: [PATCH 071/423] Add support for using custom `Datum` in `RectilinearGrid` and `StructuredGrid` (#861) * Add support for using custom 'Datum' in 'RectilinearGrid' and 'StructuredGrid' * Apply suggestions --- src/boundingboxes.jl | 6 ++---- src/coarsening/regular.jl | 8 ++++---- src/domains.jl | 2 ++ src/mesh/rectilineargrid.jl | 35 +++++++++++++++++++++-------------- src/mesh/structuredgrid.jl | 36 ++++++++++++++++++++++-------------- src/refinement/regular.jl | 12 ++++++------ src/transforms/translate.jl | 8 ++++---- test/mesh.jl | 8 ++++++++ 8 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index fa47639c2..40e437d76 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -73,11 +73,9 @@ boundingbox(g::CartesianGrid) = Box(extrema(g)...) boundingbox(g::RectilinearGrid) = Box(extrema(g)...) -boundingbox(g::TransformedGrid{Dim,<:CartesianGrid{Dim}}) where {Dim} = - boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:CartesianGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox -boundingbox(g::TransformedGrid{Dim,<:RectilinearGrid{Dim}}) where {Dim} = - boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:RectilinearGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox boundingbox(m::Mesh) = _pboxes(vertices(m)) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 190c189a1..0fa0c29e3 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -25,20 +25,20 @@ function coarsen(grid::CartesianGrid{Dim}, method::RegularCoarsening) where {Dim CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end -function coarsen(grid::RectilinearGrid{Dim}, method::RegularCoarsening) where {Dim} +function coarsen(grid::RectilinearGrid{Datum,Dim}, method::RegularCoarsening) where {Datum,Dim} factors = fitdims(method.factors, Dim) dims = size(grid) .+ .!isperiodic(grid) rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) xyzₛ = xyz(grid) xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], Dim) - RectilinearGrid(xyzₜ) + RectilinearGrid{Datum}(xyzₜ) end -function coarsen(grid::StructuredGrid{Dim}, method::RegularCoarsening) where {Dim} +function coarsen(grid::StructuredGrid{Datum,Dim}, method::RegularCoarsening) where {Datum,Dim} factors = fitdims(method.factors, Dim) dims = size(grid) .+ .!isperiodic(grid) rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) XYZₛ = XYZ(grid) XYZₜ = ntuple(i -> XYZₛ[i][rngs...], Dim) - StructuredGrid(XYZₜ) + StructuredGrid{Datum}(XYZₜ) end diff --git a/src/domains.jl b/src/domains.jl index daee76c63..1c2b13e3f 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -151,6 +151,8 @@ Base.convert(::Type{GeometrySet}, d::Domain) = GeometrySet(collect(d)) Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m)) +# TODO: extract Datum from `g` Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid(XYZ(g)) +# TODO: extract Datum from `g` Base.convert(::Type{RectilinearGrid}, g::CartesianGrid) = RectilinearGrid(xyz(g)) diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index b1d21c884..25de96241 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -4,8 +4,10 @@ """ RectilinearGrid(x, y, z, ...) + RectilinearGrid{Datum}(x, y, z, ...) -A rectilinear grid with vertices at sorted coordinates `x`, `y`, `z`, ... +A rectilinear grid with vertices at sorted coordinates `x`, `y`, `z`, ..., +and a given `Datum` (default to `NoDatum`). ## Examples @@ -18,31 +20,36 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Dim,V<:AbstractVector{<:Len}} <: Grid{Dim} +struct RectilinearGrid{Datum,Dim,V<:AbstractVector{<:Len}} <: Grid{Dim} xyz::NTuple{Dim,V} topology::GridTopology{Dim} - RectilinearGrid{Dim,V}(xyz, topology) where {Dim,V<:AbstractVector{<:Len}} = new(xyz, topology) + RectilinearGrid{Datum,Dim,V}(xyz, topology) where {Datum,Dim,V<:AbstractVector{<:Len}} = new(xyz, topology) end -function RectilinearGrid(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Dim,V<:AbstractVector{<:Len}} +function RectilinearGrid{Datum}( + xyz::NTuple{Dim,V}, + topology::GridTopology{Dim} +) where {Datum,Dim,V<:AbstractVector{<:Len}} coords = float.(xyz) - RectilinearGrid{Dim,eltype(coords)}(coords, topology) + RectilinearGrid{Datum,Dim,eltype(coords)}(coords, topology) end -RectilinearGrid(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Dim,V<:AbstractVector} = - RectilinearGrid(addunit.(xyz, u"m"), topology) +RectilinearGrid{Datum}(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Datum,Dim,V<:AbstractVector} = + RectilinearGrid{Datum}(addunit.(xyz, u"m"), topology) -function RectilinearGrid(xyz::Tuple) +function RectilinearGrid{Datum}(xyz::Tuple) where {Datum} coords = promote(collect.(xyz)...) topology = GridTopology(length.(coords) .- 1) - RectilinearGrid(coords, topology) + RectilinearGrid{Datum}(coords, topology) end -RectilinearGrid(xyz...) = RectilinearGrid(xyz) +RectilinearGrid{Datum}(xyz...) where {Datum} = RectilinearGrid{Datum}(xyz) + +RectilinearGrid(args...) = RectilinearGrid{NoDatum}(args...) -lentype(::Type{<:RectilinearGrid{Dim,V}}) where {Dim,V} = eltype(V) +lentype(::Type{<:RectilinearGrid{Datum,Dim,V}}) where {Datum,Dim,V} = eltype(V) -vertex(g::RectilinearGrid{Dim}, ijk::Dims{Dim}) where {Dim} = Point(getindex.(g.xyz, ijk)) +vertex(g::RectilinearGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk))) xyz(g::RectilinearGrid) = g.xyz @@ -55,13 +62,13 @@ function centroid(g::RectilinearGrid, ind::Int) Point(coords((to(p1) + to(p2)) / 2)) end -function Base.getindex(g::RectilinearGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} +function Base.getindex(g::RectilinearGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} @boundscheck _checkbounds(g, I) dims = size(I) start = Tuple(first(I)) stop = Tuple(last(I)) .+ 1 xyz = ntuple(i -> g.xyz[i][start[i]:stop[i]], Dim) - RectilinearGrid(xyz, GridTopology(dims)) + RectilinearGrid{Datum}(xyz, GridTopology(dims)) end function Base.summary(io::IO, g::RectilinearGrid) diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index cf496f78f..0cfe5cb97 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -4,8 +4,10 @@ """ StructuredGrid(X, Y, Z, ...) + StructuredGrid{Datum}(X, Y, Z, ...) -A structured grid with vertices at sorted coordinates `X`, `Y`, `Z`, ... +A structured grid with vertices at sorted coordinates `X`, `Y`, `Z`, ..., +and a given `Datum` (default to `NoDatum`). ## Examples @@ -18,40 +20,46 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Dim,A<:AbstractArray{<:Len}} <: Grid{Dim} +struct StructuredGrid{Datum,Dim,A<:AbstractArray{<:Len}} <: Grid{Dim} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} - StructuredGrid{Dim,A}(XYZ, topology) where {Dim,A<:AbstractArray{<:Len}} = new(XYZ, topology) + StructuredGrid{Datum,Dim,A}(XYZ, topology) where {Datum,Dim,A<:AbstractArray{<:Len}} = new(XYZ, topology) end -function StructuredGrid(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Dim,A<:AbstractArray{<:Len}} +function StructuredGrid{Datum}( + XYZ::NTuple{Dim,A}, + topology::GridTopology{Dim} +) where {Datum,Dim,A<:AbstractArray{<:Len}} coords = float.(XYZ) - StructuredGrid{Dim,eltype(coords)}(coords, topology) + StructuredGrid{Datum,Dim,eltype(coords)}(coords, topology) end -StructuredGrid(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Dim,A<:AbstractArray} = - StructuredGrid(addunit.(XYZ, u"m"), topology) +StructuredGrid{Datum}(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Datum,Dim,A<:AbstractArray} = + StructuredGrid{Datum}(addunit.(XYZ, u"m"), topology) -function StructuredGrid(XYZ::Tuple) +function StructuredGrid{Datum}(XYZ::Tuple) where {Datum} coords = promote(XYZ...) topology = GridTopology(size(first(coords)) .- 1) - StructuredGrid(coords, topology) + StructuredGrid{Datum}(coords, topology) end -StructuredGrid(XYZ...) = StructuredGrid(XYZ) +StructuredGrid{Datum}(XYZ...) where {Datum} = StructuredGrid{Datum}(XYZ) + +StructuredGrid(args...) = StructuredGrid{NoDatum}(args...) -lentype(::Type{<:StructuredGrid{Dim,A}}) where {Dim,A} = eltype(A) +lentype(::Type{<:StructuredGrid{Datum,Dim,A}}) where {Datum,Dim,A} = eltype(A) -vertex(g::StructuredGrid{Dim}, ijk::Dims{Dim}) where {Dim} = Point(ntuple(d -> g.XYZ[d][ijk...], Dim)) +vertex(g::StructuredGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = + Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], Dim))) XYZ(g::StructuredGrid) = g.XYZ -function Base.getindex(g::StructuredGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} +function Base.getindex(g::StructuredGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} @boundscheck _checkbounds(g, I) dims = size(I) cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) XYZ = ntuple(i -> g.XYZ[i][cinds], Dim) - StructuredGrid(XYZ, GridTopology(dims)) + StructuredGrid{Datum}(XYZ, GridTopology(dims)) end function Base.summary(io::IO, g::StructuredGrid) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 89ce1acf8..0c2f9e1e8 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -25,17 +25,17 @@ function refine(grid::CartesianGrid{Dim}, method::RegularRefinement) where {Dim} CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end -function refine(grid::RectilinearGrid{Dim}, method::RegularRefinement) where {Dim} +function refine(grid::RectilinearGrid{Datum,Dim}, method::RegularRefinement) where {Datum,Dim} factors = fitdims(method.factors, Dim) xyzₛ = xyz(grid) xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), Dim) - RectilinearGrid(xyzₜ) + RectilinearGrid{Datum}(xyzₜ) end -function refine(grid::StructuredGrid{Dim}, method::RegularRefinement) where {Dim} +function refine(grid::StructuredGrid{Datum,Dim}, method::RegularRefinement) where {Datum,Dim} factors = fitdims(method.factors, Dim) XYZ′ = _XYZ(grid, factors) - StructuredGrid(XYZ′) + StructuredGrid{Datum}(XYZ′) end function _refinedims(x, f) @@ -46,7 +46,7 @@ function _refinedims(x, f) x′ end -function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) +function _XYZ(grid::StructuredGrid{Datum,2}, factors::Dims{2}) where {Datum} T = numtype(lentype(grid)) fᵢ, fⱼ = factors sᵢ, sⱼ = size(grid) @@ -72,7 +72,7 @@ function _XYZ(grid::StructuredGrid{2}, factors::Dims{2}) (X, Y) end -function _XYZ(grid::StructuredGrid{3}, factors::Dims{3}) +function _XYZ(grid::StructuredGrid{Datum,3}, factors::Dims{3}) where {Datum} T = numtype(lentype(grid)) fᵢ, fⱼ, fₖ = factors sᵢ, sⱼ, sₖ = size(grid) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index 334d5ff00..1027875bd 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -37,12 +37,12 @@ applycoord(t::Translate, p::Point) = p + Vec(t.offsets) # SPECIALIZATIONS # ---------------- -apply(t::Translate{Dim}, g::RectilinearGrid{Dim}) where {Dim} = - RectilinearGrid(ntuple(i -> xyz(g)[i] .+ t.offsets[i], Dim)), nothing +apply(t::Translate{Dim}, g::RectilinearGrid{Datum,Dim}) where {Datum,Dim} = + RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], Dim)), nothing revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g)) -apply(t::Translate{Dim}, g::StructuredGrid{Dim}) where {Dim} = - StructuredGrid(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], Dim)), nothing +apply(t::Translate{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} = + StructuredGrid{Datum}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], Dim)), nothing revert(t::Translate, g::StructuredGrid, c) = first(apply(inverse(t), g)) diff --git a/test/mesh.jl b/test/mesh.jl index 2d3981151..575abc288 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -272,6 +272,10 @@ @test vertex(grid, 1) == point(0, 0) @test vertex(grid, 121) == point(10, 10) + # datum + grid = RectilinearGrid{WGS84Latest}(x, y) + @test datum(Meshes.coords(vertex(grid, 1))) === WGS84Latest + # conversion cg = cartgrid(10, 10) rg = convert(RectilinearGrid, cg) @@ -386,6 +390,10 @@ @test_throws BoundsError grid[2:6, :] @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") + # datum + grid = StructuredGrid{WGS84Latest}(X, Y) + @test datum(Meshes.coords(vertex(grid, 1))) === WGS84Latest + # conversion cg = cartgrid(10, 10) sg = convert(StructuredGrid, cg) From 5a42c7aa249ae5b0bd583a572c1a6957680b8d1e Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:45:28 -0300 Subject: [PATCH 072/423] Add `CRS` to `Geometry` type (#862) * Add 'CRS' to 'Geometry' type * Fix code * Apply suggestions * Update docstrings --- src/geometries.jl | 7 ++++--- src/multigeoms.jl | 16 +++++++--------- src/polytopes.jl | 21 ++++++++++----------- src/polytopes/ngon.jl | 14 ++++++-------- src/polytopes/polyarea.jl | 8 +++----- src/polytopes/ring.jl | 6 +++--- src/polytopes/rope.jl | 2 +- src/primitives.jl | 4 ++-- src/primitives/ball.jl | 10 ++++------ src/primitives/bezier.jl | 4 +--- src/primitives/box.jl | 12 +++++------- src/primitives/circle.jl | 8 +++----- src/primitives/cone.jl | 8 +++----- src/primitives/conesurface.jl | 8 +++----- src/primitives/cylinder.jl | 8 +++----- src/primitives/cylindersurface.jl | 9 ++++----- src/primitives/disk.jl | 8 +++----- src/primitives/ellipsoid.jl | 15 ++++++--------- src/primitives/frustum.jl | 8 +++----- src/primitives/frustumsurface.jl | 8 +++----- src/primitives/line.jl | 8 +++----- src/primitives/paraboloidsurface.jl | 12 +++++------- src/primitives/plane.jl | 10 ++++------ src/primitives/point.jl | 4 +--- src/primitives/ray.jl | 8 +++----- src/primitives/sphere.jl | 10 ++++------ src/primitives/torus.jl | 19 ++++++++++--------- 27 files changed, 107 insertions(+), 148 deletions(-) diff --git a/src/geometries.jl b/src/geometries.jl index 07fcd437d..3673dd7e1 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Geometry{Dim} + Geometry{Dim,CRS} -A geometry embedded in a `Dim`-dimensional space. +A geometry embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. """ -abstract type Geometry{Dim} end +abstract type Geometry{Dim,CRS} end Broadcast.broadcastable(g::Geometry) = Ref(g) @@ -34,6 +34,7 @@ paramdim(g::Geometry) = paramdim(typeof(g)) Return the length type of the `geometry`. """ +lentype(::Type{<:Geometry{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) lentype(g::Geometry) = lentype(typeof(g)) """ diff --git a/src/multigeoms.jl b/src/multigeoms.jl index ce5f6a1a0..b55ef0ec1 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -15,7 +15,7 @@ multiple polygons as a single entity (e.g. country with islands). - Type aliases are [`MultiPoint`](@ref), [`MultiSegment`](@ref), [`MultiRope`](@ref), [`MultiRing`](@ref), [`MultiPolygon`](@ref). """ -struct Multi{Dim,G<:Geometry{Dim}} <: Geometry{Dim} +struct Multi{Dim,C<:CRS,G<:Geometry{Dim,C}} <: Geometry{Dim,C} geoms::Vector{G} end @@ -23,17 +23,15 @@ end Multi(geoms) = Multi(collect(geoms)) # type aliases for convenience -const MultiPoint{Dim} = Multi{Dim,<:Point{Dim}} -const MultiSegment{Dim} = Multi{Dim,<:Segment{Dim}} -const MultiRope{Dim} = Multi{Dim,<:Rope{Dim}} -const MultiRing{Dim} = Multi{Dim,<:Ring{Dim}} -const MultiPolygon{Dim} = Multi{Dim,<:Polygon{Dim}} -const MultiPolyhedron{Dim} = Multi{Dim,<:Polyhedron{Dim}} +const MultiPoint{Dim,CRS} = Multi{Dim,CRS,<:Point{Dim,CRS}} +const MultiSegment{Dim,CRS} = Multi{Dim,CRS,<:Segment{Dim,CRS}} +const MultiRope{Dim,CRS} = Multi{Dim,CRS,<:Rope{Dim,CRS}} +const MultiRing{Dim,CRS} = Multi{Dim,CRS,<:Ring{Dim,CRS}} +const MultiPolygon{Dim,CRS} = Multi{Dim,CRS,<:Polygon{Dim,CRS}} +const MultiPolyhedron{Dim,CRS} = Multi{Dim,CRS,<:Polyhedron{Dim,CRS}} paramdim(m::Multi) = maximum(paramdim, m.geoms) -lentype(::Type{<:Multi{Dim,G}}) where {Dim,G} = lentype(G) - vertex(m::Multi, ind) = vertices(m)[ind] vertices(m::Multi) = [vertex for geom in m.geoms for vertex in vertices(geom)] diff --git a/src/polytopes.jl b/src/polytopes.jl index efd9612f5..f4d02d721 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -3,13 +3,14 @@ # ------------------------------------------------------------------ """ - Polytope{K,Dim,P} + Polytope{K,Dim,CRS} We say that a geometry is a K-polytope when it is a collection of "flat" sides that constitute a `K`-dimensional subspace. They are called chain, polygon and polyhedron respectively for 1D (`K=1`), 2D (`K=2`) and 3D (`K=3`) subspaces, -embedded in a `Dim`-dimensional space with point type `P`. The parameter `K` is also known as the -rank or parametric dimension of the polytope: . +embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. +The parameter `K` is also known as the rank or parametric dimension +of the polytope: . The term polytope expresses a particular combinatorial structure. A polyhedron, for example, can be decomposed into faces. Each face can then be decomposed into @@ -25,13 +26,13 @@ have (K-1)-polytopes in common. See . - Type aliases are `Chain`, `Polygon`, `Polyhedron`. """ -abstract type Polytope{K,Dim,P<:Point} <: Geometry{Dim} end +abstract type Polytope{K,Dim,CRS} <: Geometry{Dim,CRS} end # heper macro to define polytopes macro polytope(type, K, N) expr = quote - $Base.@__doc__ struct $type{Dim,P<:Point{Dim}} <: Polytope{$K,Dim,P} - vertices::NTuple{$N,P} + $Base.@__doc__ struct $type{Dim,C<:CRS} <: Polytope{$K,Dim,C} + vertices::NTuple{$N,Point{Dim,C}} end $type(vertices::Vararg{Tuple,$N}) = $type(Point.(vertices)) @@ -45,7 +46,7 @@ end # ------------------- """ - Chain{Dim,P} + Chain{Dim,CRS} A chain is a 1-polytope, i.e. a polytope with parametric dimension 1. See . @@ -152,7 +153,7 @@ include("polytopes/ring.jl") # --------------------- """ - Polygon{Dim,P} + Polygon{Dim,CRS} A polygon is a 2-polytope, i.e. a polytope with parametric dimension 2. @@ -176,7 +177,7 @@ include("polytopes/polyarea.jl") # ------------------------ """ - Polyhedron{Dim,P} + Polyhedron{Dim,CRS} A polyhedron is a 3-polytope, i.e. a polytope with parametric dimension 3. @@ -200,8 +201,6 @@ Return the parametric dimension or rank of the polytope. """ paramdim(::Type{<:Polytope{K}}) where {K} = K -lentype(::Type{<:Polytope{K,Dim,P}}) where {K,Dim,P} = lentype(P) - """ vertex(polytope, ind) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 3c75b01eb..ec6db3fb9 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -20,9 +20,9 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. - Type aliases are `Triangle`, `Quadrangle`, `Pentagon`, `Hexagon`, `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ -struct Ngon{N,Dim,P<:Point{Dim}} <: Polygon{Dim,P} - vertices::NTuple{N,P} - function Ngon{N,Dim,P}(vertices) where {N,Dim,P<:Point{Dim}} +struct Ngon{N,Dim,C<:CRS} <: Polygon{Dim,C} + vertices::NTuple{N,Point{Dim,C}} + function Ngon{N,Dim,C}(vertices) where {N,Dim,C<:CRS} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) end @@ -30,11 +30,11 @@ struct Ngon{N,Dim,P<:Point{Dim}} <: Polygon{Dim,P} end end -Ngon{N}(vertices::NTuple{N,P}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) -Ngon{N}(vertices::Vararg{P,N}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) +Ngon{N}(vertices::NTuple{N,Point{Dim,C}}) where {N,Dim,C<:CRS} = Ngon{N,Dim,C}(vertices) +Ngon{N}(vertices::Vararg{P,N}) where {N,P<:Point} = Ngon{N}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,P}) where {N,Dim,P<:Point{Dim}} = Ngon{N,Dim,P}(vertices) +Ngon(vertices::NTuple{N,Point{Dim,C}}) where {N,Dim,C<:CRS} = Ngon{N,Dim,C}(vertices) Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) @@ -48,8 +48,6 @@ const Octagon = Ngon{8} const Nonagon = Ngon{9} const Decagon = Ngon{10} -lentype(::Type{<:Ngon{N,Dim,P}}) where {N,Dim,P} = lentype(P) - Base.unique!(ngon::Ngon) = ngon nvertices(::Type{<:Ngon{N}}) where {N} = N diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index d8a301e28..4b2f24b93 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -24,10 +24,10 @@ in the real world, including issues with: * `degeneracy` - Sometimes data is shared with degenerate rings (e.g. only 2 vertices). """ -struct PolyArea{Dim,P<:Point{Dim},R<:Ring{Dim,P}} <: Polygon{Dim,P} +struct PolyArea{Dim,C<:CRS,R<:Ring{Dim,C}} <: Polygon{Dim,C} rings::Vector{R} - function PolyArea{Dim,P,R}(rings; fix=true) where {Dim,P<:Point{Dim},R<:Ring{Dim,P}} + function PolyArea{Dim,C,R}(rings; fix=true) where {Dim,C<:CRS,R<:Ring{Dim,C}} if isempty(rings) throw(ArgumentError("cannot create PolyArea without rings")) end @@ -57,7 +57,7 @@ struct PolyArea{Dim,P<:Point{Dim},R<:Ring{Dim,P}} <: Polygon{Dim,P} end end -PolyArea(rings::AbstractVector{R}; fix=true) where {Dim,P<:Point{Dim},R<:Ring{Dim,P}} = PolyArea{Dim,P,R}(rings; fix) +PolyArea(rings::AbstractVector{R}; fix=true) where {Dim,C<:CRS,R<:Ring{Dim,C}} = PolyArea{Dim,C,R}(rings; fix) PolyArea(vertices::AbstractVector{<:AbstractVector}; fix=true) = PolyArea([Ring(v) for v in vertices]; fix) @@ -67,8 +67,6 @@ PolyArea(outer::AbstractVector; fix=true) = PolyArea(Ring(outer); fix) PolyArea(outer...; fix=true) = PolyArea(collect(outer); fix) -lentype(::Type{<:PolyArea{Dim,R}}) where {Dim,R} = lentype(R) - ==(p₁::PolyArea, p₂::PolyArea) = p₁.rings == p₂.rings function Base.isapprox(p₁::PolyArea, p₂::PolyArea; kwargs...) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index b21ca101e..48b66fd31 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -9,10 +9,10 @@ A closed polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Rope`](@ref). """ -struct Ring{Dim,P<:Point{Dim},V<:CircularVector{P}} <: Chain{Dim,P} +struct Ring{Dim,C<:CRS,V<:CircularVector{Point{Dim,C}}} <: Chain{Dim,C} vertices::V - function Ring{Dim,P,V}(vertices) where {Dim,P<:Point{Dim},V<:CircularVector{P}} + function Ring{Dim,C,V}(vertices) where {Dim,C<:CRS,V<:CircularVector{Point{Dim,C}}} if first(vertices) == last(vertices) && length(vertices) ≥ 2 throw(ArgumentError(""" First and last vertices of `Ring` constructor must be different @@ -24,7 +24,7 @@ struct Ring{Dim,P<:Point{Dim},V<:CircularVector{P}} <: Chain{Dim,P} end end -Ring(vertices::CircularVector{P}) where {Dim,P<:Point{Dim}} = Ring{Dim,P,typeof(vertices)}(vertices) +Ring(vertices::CircularVector{Point{Dim,C}}) where {Dim,C<:CRS} = Ring{Dim,C,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index 45752203c..e54acf1e6 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -9,7 +9,7 @@ An open polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Ring`](@ref). """ -struct Rope{Dim,P<:Point{Dim},V<:AbstractVector{P}} <: Chain{Dim,P} +struct Rope{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}}} <: Chain{Dim,C} vertices::V end diff --git a/src/primitives.jl b/src/primitives.jl index a0658071c..2956641ec 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -3,14 +3,14 @@ # ------------------------------------------------------------------ """ - Primitive{Dim} + Primitive{Dim,CRS} We say that a geometry is a primitive when it can be expressed as a single entity with no parts (a.k.a. atomic). For example, a sphere is a primitive described in terms of a mathematical expression involving a metric and a radius. See . """ -abstract type Primitive{Dim} <: Geometry{Dim} end +abstract type Primitive{Dim,CRS} <: Geometry{Dim,CRS} end function Base.show(io::IO, geom::Primitive) name = prettyname(geom) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index b3951b499..e89029677 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -9,13 +9,13 @@ A ball with `center` and `radius`. See also [`Sphere`](@ref). """ -struct Ball{Dim,P<:Point{Dim},ℒ<:Len} <: Primitive{Dim} - center::P +struct Ball{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} + center::Point{Dim,C} radius::ℒ - Ball{Dim,P,ℒ}(center, radius) where {Dim,P<:Point{Dim},ℒ<:Len} = new(center, radius) + Ball{Dim,C,ℒ}(center, radius) where {Dim,C<:CRS,ℒ<:Len} = new(center, radius) end -Ball(center::P, radius::ℒ) where {Dim,P<:Point{Dim},ℒ<:Len} = Ball{Dim,P,float(ℒ)}(center, radius) +Ball(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = Ball{Dim,C,float(ℒ)}(center, radius) Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) @@ -27,8 +27,6 @@ Ball(center::Tuple) = Ball(Point(center)) paramdim(::Type{<:Ball{Dim}}) where {Dim} = Dim -lentype(::Type{<:Ball{Dim,P}}) where {Dim,P} = lentype(P) - center(b::Ball) = b.center radius(b::Ball) = b.radius diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 08c4f96f5..0094d2930 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -20,7 +20,7 @@ large number of points but less precise, can be used via BezierCurve(Point2[(0.,0.),(1.,-1.)]) ``` """ -struct BezierCurve{Dim,V<:AbstractVector{<:Point{Dim}}} <: Primitive{Dim} +struct BezierCurve{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}}} <: Primitive{Dim,C} controls::V end @@ -29,8 +29,6 @@ BezierCurve(points...) = BezierCurve(collect(points)) paramdim(::Type{<:BezierCurve}) = 1 -lentype(::Type{<:BezierCurve{Dim,V}}) where {Dim,V} = lentype(eltype(V)) - controls(b::BezierCurve) = b.controls ncontrols(b::BezierCurve) = length(b.controls) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 19065b387..911f40b6e 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -15,24 +15,22 @@ Box(Point(0, 0, 0), Point(1, 1, 1)) Box((0, 0), (1, 1)) ``` """ -struct Box{Dim,P<:Point{Dim}} <: Primitive{Dim} - min::P - max::P +struct Box{Dim,C<:CRS} <: Primitive{Dim,C} + min::Point{Dim,C} + max::Point{Dim,C} - function Box{Dim,P}(min, max) where {Dim,P<:Point{Dim}} + function Box{Dim,C}(min, max) where {Dim,C<:CRS} assertion(min ⪯ max, "`min` must be less than or equal to `max`") new(min, max) end end -Box(min::P, max::P) where {Dim,P<:Point{Dim}} = Box{Dim,P}(min, max) +Box(min::Point{Dim,C}, max::Point{Dim,C}) where {Dim,C<:CRS} = Box{Dim,C}(min, max) Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) paramdim(::Type{<:Box{Dim}}) where {Dim} = Dim -lentype(::Type{<:Box{Dim,P}}) where {Dim,P} = lentype(P) - Base.minimum(b::Box) = b.min Base.maximum(b::Box) = b.max diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 407f299c5..1bf253af7 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -10,13 +10,13 @@ given `plane` with given `radius`. See also [`Disk`](@ref). """ -struct Circle{P<:Plane,ℒ<:Len} <: Primitive{3} +struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} plane::P radius::ℒ - Circle{P,ℒ}(plane, radius) where {P<:Plane,ℒ<:Len} = new(plane, radius) + Circle{C,P,ℒ}(plane, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(plane, radius) end -Circle(plane::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Circle{P,float(ℒ)}(plane, radius) +Circle(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Circle{C,P,float(ℒ)}(plane, radius) Circle(plane::Plane, radius) = Circle(plane, addunit(radius, u"m")) @@ -43,8 +43,6 @@ Circle(p1::Tuple, p2::Tuple, p3::Tuple) = Circle(Point(p1), Point(p2), Point(p3) paramdim(::Type{<:Circle}) = 1 -lentype(::Type{<:Circle{P}}) where {P} = lentype(P) - plane(c::Circle) = c.plane center(c::Circle) = c.plane(0, 0) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index e42b7a30b..41006b847 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -10,17 +10,15 @@ See . See also [`ConeSurface`](@ref). """ -struct Cone{D<:Disk,P<:Point{3}} <: Primitive{3} +struct Cone{C<:CRS,D<:Disk{C}} <: Primitive{3,C} base::D - apex::P + apex::Point{3,C} end -Cone(base::Disk, apex::Tuple) = Cone(base, Point(apex)) +Cone(base::Disk{C}, apex::Tuple) where {C<:Cartesian} = Cone(base, Point(C(apex))) paramdim(::Type{<:Cone}) = 3 -lentype(::Type{<:Cone{D}}) where {D} = lentype(D) - base(c::Cone) = c.base apex(c::Cone) = c.apex diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 2bb4a37d0..7c0a5ac44 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -10,17 +10,15 @@ See . See also [`Cone`](@ref). """ -struct ConeSurface{D<:Disk,P<:Point{3}} <: Primitive{3} +struct ConeSurface{C<:CRS,D<:Disk{C}} <: Primitive{3,C} base::D - apex::P + apex::Point{3,C} end -ConeSurface(base::Disk, apex::Tuple) = ConeSurface(base, Point(apex)) +ConeSurface(base::Disk{C}, apex::Tuple) where {C<:Cartesian} = ConeSurface(base, Point(C(apex))) paramdim(::Type{<:ConeSurface}) = 2 -lentype(::Type{<:ConeSurface{D}}) where {D} = lentype(D) - base(c::ConeSurface) = c.base apex(c::ConeSurface) = c.apex diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index 3e5d35f1a..b5ca32657 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -24,14 +24,14 @@ Finally, construct a right vertical circular cylinder with given `radius`. See . """ -struct Cylinder{P<:Plane,ℒ<:Len} <: Primitive{3} +struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} bot::P top::P radius::ℒ - Cylinder{P,ℒ}(bot, top, radius) where {P<:Plane,ℒ<:Len} = new(bot, top, radius) + Cylinder{C,P,ℒ}(bot, top, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(bot, top, radius) end -Cylinder(bot::P, top::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Cylinder{P,float(ℒ)}(bot, top, radius) +Cylinder(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Cylinder{C,P,float(ℒ)}(bot, top, radius) Cylinder(bot::P, top::P, radius) where {P<:Plane} = Cylinder(bot, top, addunit(radius, u"m")) @@ -56,8 +56,6 @@ end paramdim(::Type{<:Cylinder}) = 3 -lentype(::Type{<:Cylinder{P}}) where {P} = lentype(P) - radius(c::Cylinder) = c.radius bottom(c::Cylinder) = c.bot diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index fe3310e41..7797e6c55 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -24,14 +24,15 @@ Finally, construct a right vertical circular cylinder surface with given `radius See . """ -struct CylinderSurface{P<:Plane,ℒ<:Len} <: Primitive{3} +struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} bot::P top::P radius::ℒ - CylinderSurface{P,ℒ}(bot, top, radius) where {P<:Plane,ℒ<:Len} = new(bot, top, radius) + CylinderSurface{C,P,ℒ}(bot, top, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(bot, top, radius) end -CylinderSurface(bot::P, top::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = CylinderSurface{P,float(ℒ)}(bot, top, radius) +CylinderSurface(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = + CylinderSurface{C,P,float(ℒ)}(bot, top, radius) CylinderSurface(bot::P, top::P, radius) where {P<:Plane} = CylinderSurface(bot, top, addunit(radius, u"m")) @@ -56,8 +57,6 @@ end paramdim(::Type{<:CylinderSurface}) = 2 -lentype(::Type{<:CylinderSurface{P}}) where {P} = lentype(P) - radius(c::CylinderSurface) = c.radius bottom(c::CylinderSurface) = c.bot diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index 18e9c88a0..b3d5c670d 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -10,20 +10,18 @@ given `plane` with given `radius`. See also [`Circle`](@ref). """ -struct Disk{P<:Plane,ℒ<:Len} <: Primitive{3} +struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} plane::P radius::ℒ - Disk{P,ℒ}(plane, radius) where {P<:Plane,ℒ<:Len} = new(plane, radius) + Disk{C,P,ℒ}(plane, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(plane, radius) end -Disk(plane::P, radius::ℒ) where {P<:Plane,ℒ<:Len} = Disk{P,float(ℒ)}(plane, radius) +Disk(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Disk{C,P,float(ℒ)}(plane, radius) Disk(plane::Plane, radius) = Disk(plane, addunit(radius, u"m")) paramdim(::Type{<:Disk}) = 2 -lentype(::Type{<:Disk{P}}) where {P} = lentype(P) - plane(d::Disk) = d.plane center(d::Disk) = d.plane(0, 0) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 002e1460c..749a3fbf8 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -7,26 +7,23 @@ A 3D ellipsoid with given `radii`, `center` and `rotation`. """ -struct Ellipsoid{ℒ<:Len,P<:Point{3},R} <: Primitive{3} +struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{3,C} radii::NTuple{3,ℒ} - center::P + center::Point{3,C} rotation::R - Ellipsoid{ℒ,P,R}(radii, center, rotation) where {ℒ<:Len,P<:Point{3},R} = new(radii, center, rotation) + Ellipsoid{ℒ,C,R}(radii, center, rotation) where {ℒ<:Len,C<:CRS,R} = new(radii, center, rotation) end -Ellipsoid(radii::NTuple{3,ℒ}, center::P, rotation::R) where {ℒ<:Len,P<:Point{3},R} = - Ellipsoid{float(ℒ),P,R}(radii, center, rotation) +Ellipsoid(radii::NTuple{3,ℒ}, center::Point{3,C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = + Ellipsoid{float(ℒ),C,R}(radii, center, rotation) -Ellipsoid(radii::NTuple{3}, center::P, rotation::R) where {P<:Point{3},R} = - Ellipsoid(addunit.(radii, u"m"), center, rotation) +Ellipsoid(radii::NTuple{3}, center::Point{3}, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) Ellipsoid(radii::NTuple{3,T}, center=(zero(T), zero(T), zero(T)), rotation=I) where {T} = Ellipsoid(radii, Point(center), rotation) paramdim(::Type{<:Ellipsoid}) = 2 -lentype(::Type{<:Ellipsoid{ℒ,P}}) where {ℒ,P} = lentype(P) - radii(e::Ellipsoid) = e.radii center(e::Ellipsoid) = e.center diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index c7e0ea8e6..94475c249 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -10,11 +10,11 @@ See . See also [`FrustumSurface`](@ref). """ -struct Frustum{D<:Disk} <: Primitive{3} +struct Frustum{C<:CRS,D<:Disk{C}} <: Primitive{3,C} bot::D top::D - function Frustum{D}(bot, top) where {D} + function Frustum{C,D}(bot, top) where {C<:CRS,D<:Disk{C}} bn = normal(plane(bot)) tn = normal(plane(top)) a = bn ⋅ tn @@ -24,12 +24,10 @@ struct Frustum{D<:Disk} <: Primitive{3} end end -Frustum(bot::D, top::D) where {D<:Disk} = Frustum{D}(bot, top) +Frustum(bot::D, top::D) where {C<:CRS,D<:Disk{C}} = Frustum{C,D}(bot, top) paramdim(::Type{<:Frustum}) = 3 -lentype(::Type{<:Frustum{D}}) where {D} = lentype(D) - bottom(f::Frustum) = f.bot top(f::Frustum) = f.top diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index df1fe55d2..820ea224e 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -10,11 +10,11 @@ See . See also [`Frustum`](@ref). """ -struct FrustumSurface{D<:Disk} <: Primitive{3} +struct FrustumSurface{C<:CRS,D<:Disk{C}} <: Primitive{3,C} bot::D top::D - function FrustumSurface{D}(bot, top) where {D} + function FrustumSurface{C,D}(bot, top) where {C<:CRS,D<:Disk{C}} bn = normal(plane(bot)) tn = normal(plane(top)) a = bn ⋅ tn @@ -24,12 +24,10 @@ struct FrustumSurface{D<:Disk} <: Primitive{3} end end -FrustumSurface(bot::D, top::D) where {D<:Disk} = FrustumSurface{D}(bot, top) +FrustumSurface(bot::D, top::D) where {C<:CRS,D<:Disk{C}} = FrustumSurface{C,D}(bot, top) paramdim(::Type{<:FrustumSurface}) = 2 -lentype(::Type{<:FrustumSurface{D}}) where {D} = lentype(D) - bottom(f::FrustumSurface) = f.bot top(f::FrustumSurface) = f.top diff --git a/src/primitives/line.jl b/src/primitives/line.jl index ed18d82df..8bee80698 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -9,17 +9,15 @@ A line passing through points `a` and `b`. See also [`Segment`](@ref). """ -struct Line{Dim,P<:Point{Dim}} <: Primitive{Dim} - a::P - b::P +struct Line{Dim,C<:CRS} <: Primitive{Dim,C} + a::Point{Dim,C} + b::Point{Dim,C} end Line(a::Tuple, b::Tuple) = Line(Point(a), Point(b)) paramdim(::Type{<:Line}) = 1 -lentype(::Type{<:Line{Dim,P}}) where {Dim,P} = lentype(P) - ==(l₁::Line, l₂::Line) = l₁.a ∈ l₂ && l₁.b ∈ l₂ && l₂.a ∈ l₁ && l₂.b ∈ l₁ (l::Line)(t) = l.a + t * (l.b - l.a) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 7d8c8b5b7..1b0d6eb32 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -32,15 +32,15 @@ Same as above, but here the apex is at `Apex(0, 0, 0)`. See also . """ -struct ParaboloidSurface{P<:Point{3},ℒ<:Len} <: Primitive{3} - apex::P +struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{3,C} + apex::Point{3,C} radius::ℒ focallength::ℒ - ParaboloidSurface{P,ℒ}(apex, radius, focallength) where {P<:Point{3},ℒ<:Len} = new(apex, radius, focallength) + ParaboloidSurface{C,ℒ}(apex, radius, focallength) where {C<:CRS,ℒ<:Len} = new(apex, radius, focallength) end -ParaboloidSurface(apex::P, radius::ℒ, focallength::ℒ) where {P<:Point{3},ℒ<:Len} = - ParaboloidSurface{P,float(ℒ)}(apex, radius, focallength) +ParaboloidSurface(apex::Point{3,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = + ParaboloidSurface{C,float(ℒ)}(apex, radius, focallength) ParaboloidSurface(apex::Point{3}, radius::Len, focallength::Len) = ParaboloidSurface(apex, promote(radius, focallength)...) @@ -62,8 +62,6 @@ ParaboloidSurface() = ParaboloidSurface(Point(0, 0, 0)) paramdim(::Type{<:ParaboloidSurface}) = 2 -lentype(::Type{<:ParaboloidSurface{P}}) where {P} = lentype(P) - """ focallength(p::ParaboloidSurface) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 17b00258c..7203e85b5 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -13,10 +13,10 @@ defined by non-parallel vectors `u` and `v`. Alternatively specify point `p` and a given normal vector `n` to the plane. """ -struct Plane{P<:Point{3},V<:Vec{3}} <: Primitive{3} - p::P - u::V - v::V +struct Plane{C<:CRS,ℒ<:Len} <: Primitive{3,C} + p::Point{3,C} + u::Vec{3,ℒ} + v::Vec{3,ℒ} end function Plane(p::Point{3}, n::Vec{3}) @@ -38,8 +38,6 @@ end paramdim(::Type{<:Plane}) = 2 -lentype(::Type{<:Plane{P}}) where {P} = lentype(P) - normal(p::Plane) = unormalize(ucross(p.u, p.v)) ==(p₁::Plane, p₂::Plane) = diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 89e53f477..d11fee71b 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -33,7 +33,7 @@ Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design algorithms assume a continuous space. The conversion to float avoids `InexactError` and other unexpected results. """ -struct Point{Dim,C<:CRS} <: Primitive{Dim} +struct Point{Dim,C<:CRS} <: Primitive{Dim,C} coords::C Point(coords::C) where {C<:CRS} = new{CoordRefSystems.ndims(coords),C}(coords) end @@ -47,8 +47,6 @@ Base.convert(::Type{Point{Dim,CRS}}, p::Point{Dim,CRS}) where {Dim,CRS} = p paramdim(::Type{<:Point}) = 0 -lentype(::Type{<:Point{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) - center(p::Point) = p ==(A::Point, B::Point) = A.coords == B.coords diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 1a95cd0a4..c5e1938ab 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -9,17 +9,15 @@ A ray originating at point `p`, pointed in direction `v`. It can be called as `r(t)` with `t > 0` to cast it at `p + t * v`. """ -struct Ray{Dim,P<:Point{Dim},V<:Vec{Dim}} <: Primitive{Dim} - p::P - v::V +struct Ray{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} + p::Point{Dim,C} + v::Vec{Dim,ℒ} end Ray(p::Tuple, v::Tuple) = Ray(Point(p), Vec(v)) paramdim(::Type{<:Ray}) = 1 -lentype(::Type{<:Ray{Dim,P}}) where {Dim,P} = lentype(P) - ==(r₁::Ray, r₂::Ray) = (r₁.p ≈ r₂.p) && (r₁.p + r₁.v) ∈ r₂ && (r₂.p + r₂.v) ∈ r₁ function (r::Ray)(t) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 9800d7095..b59e999a2 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -9,13 +9,13 @@ A sphere with `center` and `radius`. See also [`Ball`](@ref). """ -struct Sphere{Dim,P<:Point{Dim},ℒ<:Len} <: Primitive{Dim} - center::P +struct Sphere{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} + center::Point{Dim,C} radius::ℒ - Sphere{Dim,P,ℒ}(center, radius) where {Dim,P<:Point{Dim},ℒ<:Len} = new(center, radius) + Sphere{Dim,C,ℒ}(center, radius) where {Dim,C<:CRS,ℒ<:Len} = new(center, radius) end -Sphere(center::P, radius::ℒ) where {Dim,P<:Point{Dim},ℒ<:Len} = Sphere{Dim,P,float(ℒ)}(center, radius) +Sphere(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = Sphere{Dim,C,float(ℒ)}(center, radius) Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) @@ -64,8 +64,6 @@ Sphere(p1::Tuple, p2::Tuple, p3::Tuple, p4::Tuple) = Sphere(Point(p1), Point(p2) paramdim(::Type{<:Sphere{Dim}}) where {Dim} = Dim - 1 -lentype(::Type{<:Sphere{Dim,P}}) where {Dim,P} = lentype(P) - center(s::Sphere) = s.center radius(s::Sphere) = s.radius diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 338a5562f..76a271a4f 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -9,18 +9,21 @@ A torus centered at `center` with axis of revolution directed by `normal` and with radii `major` and `minor`. """ -struct Torus{P<:Point{3},V<:Vec{3},ℒ<:Len} <: Primitive{3} - center::P - normal::V +struct Torus{C<:CRS,ℒ<:Len} <: Primitive{3,C} + center::Point{3,C} + normal::Vec{3,ℒ} major::ℒ minor::ℒ - Torus{P,V,ℒ}(center, normal, major, minor) where {P<:Point{3},V<:Vec{3},ℒ<:Len} = new(center, normal, major, minor) + Torus{C,ℒ}(center, normal, major, minor) where {C<:CRS,ℒ<:Len} = new(center, normal, major, minor) end -Torus(center::P, normal::V, major::ℒ, minor::ℒ) where {P<:Point{3},V<:Vec{3},ℒ<:Len} = - Torus{P,V,float(ℒ)}(center, normal, major, minor) +Torus(center::Point{3,C}, normal::Vec{3,ℒ}, major::ℒ, minor::ℒ) where {C<:CRS,ℒ<:Len} = + Torus{C,float(ℒ)}(center, normal, major, minor) -Torus(center::Point{3}, normal::Vec{3}, major::Len, minor::Len) = Torus(center, normal, promote(major, minor)...) +function Torus(center::Point{3}, normal::Vec{3}, major::Len, minor::Len) + ℒ = promote_type(eltype(normal), typeof(major), typeof(minor)) + Torus(center, Vec{3,ℒ}(normal), ℒ(major), ℒ(minor)) +end Torus(center::Point{3}, normal::Vec{3}, major, minor) = Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) @@ -45,8 +48,6 @@ Torus(p1::Tuple, p2::Tuple, p3::Tuple, minor) = Torus(Point(p1), Point(p2), Poin paramdim(::Type{<:Torus}) = 2 -lentype(::Type{<:Torus{P}}) where {P} = lentype(P) - center(t::Torus) = t.center normal(t::Torus) = t.normal From 26ba6729d46346945d92f44e3b010cd63764a923 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:15:19 -0300 Subject: [PATCH 073/423] Add `CRS` to `Domain` type (#863) * Add 'CRS' to 'Domain' type * Fix code * Fix code --- src/boundingboxes.jl | 4 ++-- src/domains.jl | 5 +++-- src/mesh.jl | 19 ++++++++++--------- src/mesh/cartesiangrid.jl | 12 +++++------- src/mesh/rectilineargrid.jl | 16 ++++++---------- src/mesh/simplemesh.jl | 6 ++---- src/mesh/structuredgrid.jl | 16 ++++++---------- src/mesh/transformedmesh.jl | 6 ++---- src/sets.jl | 8 +++----- src/subdomains.jl | 4 +--- src/trajecs.jl | 12 +++++------- test/dummy.jl | 6 ++---- 12 files changed, 47 insertions(+), 67 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 40e437d76..ececda03a 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -73,9 +73,9 @@ boundingbox(g::CartesianGrid) = Box(extrema(g)...) boundingbox(g::RectilinearGrid) = Box(extrema(g)...) -boundingbox(g::TransformedGrid{<:Any,<:CartesianGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:Any,<:CartesianGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox -boundingbox(g::TransformedGrid{<:Any,<:RectilinearGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:Any,<:RectilinearGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox boundingbox(m::Mesh) = _pboxes(vertices(m)) diff --git a/src/domains.jl b/src/domains.jl index 1c2b13e3f..4776dafb0 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Domain{Dim} + Domain{Dim,CRS} A domain is an indexable collection of geometries (e.g. mesh). """ -abstract type Domain{Dim} end +abstract type Domain{Dim,CRS} end """ element(domain, ind) @@ -76,6 +76,7 @@ paramdim(d::Domain) = paramdim(first(d)) Return the length type of the `domain`. """ +lentype(::Type{<:Domain{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) lentype(d::Domain) = lentype(typeof(d)) """ diff --git a/src/mesh.jl b/src/mesh.jl index 1685b748a..4711fe26c 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -3,11 +3,12 @@ # ------------------------------------------------------------------ """ - Mesh{Dim,TP} + Mesh{Dim,CRS,TP} -A mesh embedded in a `Dim`-dimensional space with topology of type `TP`. +A mesh embedded in a `Dim`-dimensional space with given +coordinate reference system `CRS` and topology of type `TP`. """ -abstract type Mesh{Dim,TP<:Topology} <: Domain{Dim} end +abstract type Mesh{Dim,CRS,TP<:Topology} <: Domain{Dim,CRS} end """ vertex(mesh, ind) @@ -138,18 +139,18 @@ function Base.show(io::IO, ::MIME"text/plain", m::Mesh) end """ - Grid{Dim} + Grid{Dim,CRS} -A grid embedded in a `Dim`-dimensional space. +A grid embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. """ -const Grid{Dim} = Mesh{Dim,GridTopology{Dim}} +const Grid{Dim,CRS} = Mesh{Dim,CRS,GridTopology{Dim}} """ - SubGrid{Dim} + SubGrid{Dim,CRS} -A view of a grid in a `Dim`-dimensinoal space. +A view of a grid in a `Dim`-dimensinoal space with given coordinate reference system `CRS`. """ -const SubGrid{Dim} = SubDomain{Dim,<:Grid{Dim}} +const SubGrid{Dim,CRS} = SubDomain{Dim,CRS,<:Grid{Dim,CRS}} """ vertex(grid, ijk) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index fe38c8e71..1efda3959 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -50,13 +50,13 @@ Create a 1D grid from -1 to 1 with 100 segments: julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` """ -struct CartesianGrid{Dim,P<:Point{Dim},ℒ<:Len} <: Grid{Dim} - origin::P +struct CartesianGrid{Dim,C<:CRS,ℒ<:Len} <: Grid{Dim,C} + origin::Point{Dim,C} spacing::NTuple{Dim,ℒ} offset::Dims{Dim} topology::GridTopology{Dim} - function CartesianGrid{Dim,P,ℒ}(origin, spacing, offset, topology) where {Dim,P<:Point{Dim},ℒ<:Len} + function CartesianGrid{Dim,C,ℒ}(origin, spacing, offset, topology) where {Dim,C<:CRS,ℒ<:Len} if !all(>(zero(ℒ)), spacing) throw(ArgumentError("spacing must be positive")) end @@ -65,11 +65,11 @@ struct CartesianGrid{Dim,P<:Point{Dim},ℒ<:Len} <: Grid{Dim} end CartesianGrid( - origin::P, + origin::Point{Dim,C}, spacing::NTuple{Dim,ℒ}, offset::Dims{Dim}, topology::GridTopology{Dim} -) where {Dim,P<:Point{Dim},ℒ<:Len} = CartesianGrid{Dim,P,float(ℒ)}(origin, spacing, offset, topology) +) where {Dim,C<:CRS,ℒ<:Len} = CartesianGrid{Dim,C,float(ℒ)}(origin, spacing, offset, topology) CartesianGrid(origin::Point{Dim}, spacing::NTuple{Dim}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) @@ -125,8 +125,6 @@ end CartesianGrid(dims::Int...) = CartesianGrid(dims) -lentype(::Type{<:CartesianGrid{Dim,P}}) where {Dim,P} = lentype(P) - vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = g.origin + Vec((ijk .- g.offset) .* g.spacing) spacing(g::CartesianGrid) = g.spacing diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 25de96241..c02920266 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -20,21 +20,19 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Datum,Dim,V<:AbstractVector{<:Len}} <: Grid{Dim} +struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} xyz::NTuple{Dim,V} topology::GridTopology{Dim} - RectilinearGrid{Datum,Dim,V}(xyz, topology) where {Datum,Dim,V<:AbstractVector{<:Len}} = new(xyz, topology) + RectilinearGrid{Datum,Dim,ℒ,V}(xyz, topology) where {Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} = new(xyz, topology) end -function RectilinearGrid{Datum}( - xyz::NTuple{Dim,V}, - topology::GridTopology{Dim} -) where {Datum,Dim,V<:AbstractVector{<:Len}} +function RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} coords = float.(xyz) - RectilinearGrid{Datum,Dim,eltype(coords)}(coords, topology) + V = eltype(coords) + RectilinearGrid{Datum,Dim,eltype(V),V}(coords, topology) end -RectilinearGrid{Datum}(xyz::NTuple{Dim,V}, topology::GridTopology{Dim}) where {Datum,Dim,V<:AbstractVector} = +RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector}, topology::GridTopology{Dim}) where {Datum,Dim} = RectilinearGrid{Datum}(addunit.(xyz, u"m"), topology) function RectilinearGrid{Datum}(xyz::Tuple) where {Datum} @@ -47,8 +45,6 @@ RectilinearGrid{Datum}(xyz...) where {Datum} = RectilinearGrid{Datum}(xyz) RectilinearGrid(args...) = RectilinearGrid{NoDatum}(args...) -lentype(::Type{<:RectilinearGrid{Datum,Dim,V}}) where {Datum,Dim,V} = eltype(V) - vertex(g::RectilinearGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk))) xyz(g::RectilinearGrid) = g.xyz diff --git a/src/mesh/simplemesh.jl b/src/mesh/simplemesh.jl index 136285b63..94a2a859b 100644 --- a/src/mesh/simplemesh.jl +++ b/src/mesh/simplemesh.jl @@ -31,7 +31,7 @@ See also [`Topology`](@ref), [`GridTopology`](@ref), of the mesh to a [`HalfEdgeTopology`](@ref) instead of a [`SimpleTopology`](@ref). """ -struct SimpleMesh{Dim,V<:AbstractVector{<:Point{Dim}},TP<:Topology} <: Mesh{Dim,TP} +struct SimpleMesh{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}},TP<:Topology} <: Mesh{Dim,C,TP} vertices::V topology::TP end @@ -43,15 +43,13 @@ function SimpleMesh(vertices, connec::AbstractVector{<:Connectivity}; relations= SimpleMesh(vertices, topology) end -lentype(::Type{<:SimpleMesh{Dim,V}}) where {Dim,V} = lentype(eltype(V)) - vertex(m::SimpleMesh, ind::Int) = m.vertices[ind] vertices(m::SimpleMesh) = m.vertices nvertices(m::SimpleMesh) = length(m.vertices) -function Base.getindex(m::SimpleMesh{Dim,V,GridTopology{Dim}}, I::CartesianIndices{Dim}) where {Dim,V} +function Base.getindex(m::SimpleMesh{Dim,<:Any,<:Any,GridTopology{Dim}}, I::CartesianIndices{Dim}) where {Dim} @boundscheck _checkbounds(m, I) dims = size(I) odims = size(m) diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index 0cfe5cb97..136b77e04 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -20,21 +20,19 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Datum,Dim,A<:AbstractArray{<:Len}} <: Grid{Dim} +struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} - StructuredGrid{Datum,Dim,A}(XYZ, topology) where {Datum,Dim,A<:AbstractArray{<:Len}} = new(XYZ, topology) + StructuredGrid{Datum,Dim,ℒ,A}(XYZ, topology) where {Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} = new(XYZ, topology) end -function StructuredGrid{Datum}( - XYZ::NTuple{Dim,A}, - topology::GridTopology{Dim} -) where {Datum,Dim,A<:AbstractArray{<:Len}} +function StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} coords = float.(XYZ) - StructuredGrid{Datum,Dim,eltype(coords)}(coords, topology) + A = eltype(coords) + StructuredGrid{Datum,Dim,eltype(A),A}(coords, topology) end -StructuredGrid{Datum}(XYZ::NTuple{Dim,A}, topology::GridTopology{Dim}) where {Datum,Dim,A<:AbstractArray} = +StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray}, topology::GridTopology{Dim}) where {Datum,Dim} = StructuredGrid{Datum}(addunit.(XYZ, u"m"), topology) function StructuredGrid{Datum}(XYZ::Tuple) where {Datum} @@ -47,8 +45,6 @@ StructuredGrid{Datum}(XYZ...) where {Datum} = StructuredGrid{Datum}(XYZ) StructuredGrid(args...) = StructuredGrid{NoDatum}(args...) -lentype(::Type{<:StructuredGrid{Datum,Dim,A}}) where {Datum,Dim,A} = eltype(A) - vertex(g::StructuredGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], Dim))) diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index a96bf95ec..1c7d735f4 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,7 +7,7 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{Dim,TP<:Topology,M<:Mesh{Dim,TP},TR<:Transform} <: Mesh{Dim,TP} +struct TransformedMesh{Dim,C<:CRS,TP<:Topology,M<:Mesh{Dim,C,TP},TR<:Transform} <: Mesh{Dim,C,TP} mesh::M transform::TR end @@ -15,8 +15,6 @@ end # specialize constructor to avoid deep structures TransformedMesh(m::TransformedMesh, t::Transform) = TransformedMesh(m.mesh, m.transform → t) -lentype(::Type{<:TransformedMesh{Dim,TP,M}}) where {Dim,TP,M} = lentype(M) - Base.parent(m::TransformedMesh) = m.mesh transform(m::TransformedMesh) = m.transform @@ -26,7 +24,7 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{Dim,G<:Grid{Dim},TR} = TransformedMesh{Dim,GridTopology{Dim},G,TR} +const TransformedGrid{Dim,CRS,G<:Grid{Dim,CRS},TR} = TransformedMesh{Dim,CRS,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) diff --git a/src/sets.jl b/src/sets.jl index 4e6cae331..5541ab029 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -15,15 +15,13 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` """ -struct GeometrySet{Dim,G<:Geometry{Dim}} <: Domain{Dim} +struct GeometrySet{Dim,C<:CRS,G<:Geometry{Dim,C}} <: Domain{Dim,C} geoms::Vector{G} end # constructor with iterator of geometries GeometrySet(geoms) = GeometrySet(map(identity, geoms)) -lentype(::Type{<:GeometrySet{Dim,G}}) where {Dim,G} = lentype(G) - element(d::GeometrySet, ind::Int) = d.geoms[ind] nelements(d::GeometrySet) = length(d.geoms) @@ -39,7 +37,7 @@ Base.vcat(d1::Domain, d2::GeometrySet) = GeometrySet(vcat(collect(d1), d2.geoms) # SPECIAL CASE: POINT SET # ------------------------ -const PointSet{Dim,P<:Point{Dim}} = GeometrySet{Dim,P} +const PointSet{Dim,CRS} = GeometrySet{Dim,CRS,Point{Dim,CRS}} """ PointSet(points) @@ -60,7 +58,7 @@ julia> PointSet([1,2,3], [4,5,6]) julia> PointSet([1 4; 2 5; 3 6]) ``` """ -PointSet(points::AbstractVector{P}) where {Dim,P<:Point{Dim}} = PointSet{Dim,P}(points) +PointSet(points::AbstractVector{Point{Dim,C}}) where {Dim,C<:CRS} = PointSet{Dim,C}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) diff --git a/src/subdomains.jl b/src/subdomains.jl index 1337ede85..0c0c5ae8c 100644 --- a/src/subdomains.jl +++ b/src/subdomains.jl @@ -11,7 +11,7 @@ A partial view of a `domain` containing only the elements at `indices`. """ -struct SubDomain{Dim,D<:Domain{Dim},I<:AbstractVector{Int}} <: Domain{Dim} +struct SubDomain{Dim,C<:CRS,D<:Domain{Dim,C},I<:AbstractVector{Int}} <: Domain{Dim,C} domain::D inds::I end @@ -23,8 +23,6 @@ SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[ # DOMAIN INTERFACE # ----------------- -lentype(::Type{<:SubDomain{Dim,D}}) where {Dim,D} = lentype(D) - element(d::SubDomain, ind::Int) = element(d.domain, d.inds[ind]) nelements(d::SubDomain) = length(d.inds) diff --git a/src/trajecs.jl b/src/trajecs.jl index 9b21638c1..f3724d22d 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -7,14 +7,14 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. """ -struct CylindricalTrajectory{P<:Point{3},ℒ<:Len} <: Domain{3} - centroids::Vector{P} +struct CylindricalTrajectory{C<:CRS,ℒ<:Len} <: Domain{3,C} + centroids::Vector{Point{3,C}} radius::ℒ - CylindricalTrajectory{P,ℒ}(centroids, radius) where {P<:Point{3},ℒ<:Len} = new(centroids, radius) + CylindricalTrajectory{C,ℒ}(centroids, radius) where {C<:CRS,ℒ<:Len} = new(centroids, radius) end -CylindricalTrajectory(centroids::Vector{P}, radius::ℒ) where {P<:Point{3},ℒ<:Len} = - CylindricalTrajectory{P,float(ℒ)}(centroids, radius) +CylindricalTrajectory(centroids::Vector{Point{3,C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = + CylindricalTrajectory{C,float(ℒ)}(centroids, radius) CylindricalTrajectory(centroids, radius::Len) = CylindricalTrajectory(collect(centroids), radius) @@ -24,8 +24,6 @@ CylindricalTrajectory(centroids::Vector{P}) where {P<:Point{3}} = CylindricalTra CylindricalTrajectory(centroids) = CylindricalTrajectory(collect(centroids)) -lentype(::Type{<:CylindricalTrajectory{P}}) where {P} = lentype(P) - topology(t::CylindricalTrajectory) = GridTopology(length(t.centroids)) function element(t::CylindricalTrajectory, ind::Int) diff --git a/test/dummy.jl b/test/dummy.jl index 01b217851..b4478451e 100644 --- a/test/dummy.jl +++ b/test/dummy.jl @@ -1,10 +1,8 @@ # dummy type implementing the Domain trait -struct DummyDomain{Dim,P<:Point{Dim}} <: Domain{Dim} - origin::P +struct DummyDomain{Dim,C<:CRS} <: Domain{Dim,C} + origin::Point{Dim,C} end -Meshes.lentype(::Type{<:DummyDomain{Dim,P}}) where {Dim,P} = Meshes.lentype(P) - function Meshes.element(domain::DummyDomain{Dim}, ind::Int) where {Dim} ℒ = Meshes.lentype(domain) T = Unitful.numtype(ℒ) From 5e367f6024c9cbfaddb618fc6fee7011bc2edc11 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:46:11 -0300 Subject: [PATCH 074/423] Fix 'SubCartesianGrid' alias (#865) --- ext/subcartesiangrid.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index 233db603b..71979a0b2 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -const SubCartesianGrid{Dim} = SubDomain{Dim,<:CartesianGrid{Dim}} +const SubCartesianGrid{Dim,CRS} = SubDomain{Dim,CRS,<:CartesianGrid{Dim,CRS}} function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) subgrid = plot[:object] From d091a72bb5504883c82b93a44d87e86f53f77b7a Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:49:50 -0300 Subject: [PATCH 075/423] Add `crs` function (#866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'crstype' function * 'crstype' -> 'crs' * Add more tests * Apply suggestions from code review --------- Co-authored-by: Júlio Hoffimann --- src/domains.jl | 14 ++++++++++---- src/geometries.jl | 8 ++++++++ test/domains.jl | 1 + test/mesh.jl | 17 +++++++++++++++-- test/multigeoms.jl | 2 ++ test/polytopes.jl | 18 ++++++++++++++++++ test/primitives.jl | 41 ++++++++++++++++++++++++++++++----------- test/sets.jl | 5 +++++ test/subdomains.jl | 6 ++++++ test/trajecs.jl | 6 ++++++ 10 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/domains.jl b/src/domains.jl index 4776dafb0..74cc91736 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -71,6 +71,14 @@ parametric dimensions of its elements. """ paramdim(d::Domain) = paramdim(first(d)) +""" + crs(domain) + +Return the coordinate reference system (CRS) of the `domain`. +""" +crs(::Type{<:Domain{Dim,CRS}}) where {Dim,CRS} = CRS +crs(d::Domain) = crs(typeof(d)) + """ lentype(domain) @@ -152,8 +160,6 @@ Base.convert(::Type{GeometrySet}, d::Domain) = GeometrySet(collect(d)) Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m)) -# TODO: extract Datum from `g` -Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid(XYZ(g)) +Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{datum(crs(g))}(XYZ(g)) -# TODO: extract Datum from `g` -Base.convert(::Type{RectilinearGrid}, g::CartesianGrid) = RectilinearGrid(xyz(g)) +Base.convert(::Type{RectilinearGrid}, g::CartesianGrid) = RectilinearGrid{datum(crs(g))}(xyz(g)) diff --git a/src/geometries.jl b/src/geometries.jl index 3673dd7e1..4c9cad244 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -29,6 +29,14 @@ See also [`isparametrized`](@ref). """ paramdim(g::Geometry) = paramdim(typeof(g)) +""" + crs(geometry) + +Return the coordinate reference system (CRS) of the `geometry`. +""" +crs(::Type{<:Geometry{Dim,CRS}}) where {Dim,CRS} = CRS +crs(g::Geometry) = crs(typeof(g)) + """ lentype(geometry) diff --git a/test/domains.jl b/test/domains.jl index 5d56259f6..ca3215fff 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -2,6 +2,7 @@ # basic properties dom = DummyDomain(point(0, 0)) @test embeddim(dom) == 2 + @test Meshes.crs(dom) <: Cartesian{NoDatum} @test Meshes.lentype(dom) == ℳ @test !isparametrized(dom) diff --git a/test/mesh.jl b/test/mesh.jl index 575abc288..a9761e7de 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -2,6 +2,7 @@ @testset "CartesianGrid" begin grid = cartgrid(100) @test embeddim(grid) == 1 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (100,) @test minimum(grid) == point(0) @@ -18,6 +19,7 @@ grid = cartgrid(200, 100) @test embeddim(grid) == 2 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) @test minimum(grid) == point(0, 0) @@ -34,6 +36,7 @@ grid = CartesianGrid((200, 100, 50), T.((0, 0, 0)), T.((1, 1, 1))) @test embeddim(grid) == 3 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100, 50) @test minimum(grid) == point(0, 0, 0) @@ -50,6 +53,7 @@ grid = CartesianGrid(T.((0, 0, 0)), T.((1, 1, 1)), T.((0.1, 0.1, 0.1))) @test embeddim(grid) == 3 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10, 10) @test minimum(grid) == point(0, 0, 0) @@ -58,6 +62,7 @@ grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) @test embeddim(grid) == 2 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) @test minimum(grid) == point(-1.0, -1.0) @@ -68,6 +73,7 @@ grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) @test embeddim(grid) == 3 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (20, 10, 5) @test minimum(grid) == point(0, 0, 0) @@ -91,6 +97,7 @@ # constructor with offset grid = CartesianGrid((10, 10), T.((1.0, 1.0)), T.((1.0, 1.0)), (2, 2)) @test embeddim(grid) == 2 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10) @test minimum(grid) == point(0.0, 0.0) @@ -235,6 +242,7 @@ y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) @test embeddim(grid) == 2 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) @test minimum(grid) == point(0, 0) @@ -274,7 +282,7 @@ # datum grid = RectilinearGrid{WGS84Latest}(x, y) - @test datum(Meshes.coords(vertex(grid, 1))) === WGS84Latest + @test datum(Meshes.crs(grid)) === WGS84Latest # conversion cg = cartgrid(10, 10) @@ -359,6 +367,7 @@ Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) grid = StructuredGrid(X, Y) @test embeddim(grid) == 2 + @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) @test minimum(grid) == point(0, 0) @@ -392,7 +401,7 @@ # datum grid = StructuredGrid{WGS84Latest}(X, Y) - @test datum(Meshes.coords(vertex(grid, 1))) === WGS84Latest + @test datum(Meshes.crs(grid)) === WGS84Latest # conversion cg = cartgrid(10, 10) @@ -499,6 +508,8 @@ (point(1.0, 1.0), point(0.0, 1.0), point(0.5, 0.5)), (point(0.0, 1.0), point(0.0, 0.0), point(0.5, 0.5)) ]) + @test Meshes.crs(mesh) <: Cartesian{NoDatum} + @test Meshes.lentype(mesh) == ℳ @test vertices(mesh) == points @test collect(faces(mesh, 2)) == triangles @test collect(elements(mesh)) == triangles @@ -682,6 +693,8 @@ mesh = convert(SimpleMesh, grid) trans = Identity() tmesh = TransformedMesh(mesh, trans) + @test Meshes.crs(tmesh) <: Cartesian{NoDatum} + @test Meshes.lentype(tmesh) == ℳ @test parent(tmesh) === mesh @test Meshes.transform(tmesh) === trans @test TransformedMesh(grid, trans) == grid diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 44ca25f58..28546d077 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -7,6 +7,8 @@ @test multi == multi @test multi ≈ multi @test paramdim(multi) == 2 + @test Meshes.crs(multi) <: Cartesian{NoDatum} + @test Meshes.lentype(multi) == ℳ @test vertex(multi, 1) == vertex(poly, 1) @test vertices(multi) == [vertices(poly); vertices(poly)] @test nvertices(multi) == nvertices(poly) + nvertices(poly) diff --git a/test/polytopes.jl b/test/polytopes.jl index 471f2e80b..4a2b41bd7 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -4,6 +4,8 @@ @test nvertices(Segment) == 2 s = Segment(point(1.0), point(2.0)) + @test Meshes.crs(s) <: Cartesian{NoDatum} + @test Meshes.lentype(s) == ℳ @test vertex(s, 1) == point(1.0) @test vertex(s, 2) == point(2.0) @test all(point(x) ∈ s for x in 1:0.01:2) @@ -94,9 +96,13 @@ @test c1 == c2 == c3 c = Rope(point.([(1, 1), (2, 2)])) + @test Meshes.crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ @test vertex(c, 1) == point(1, 1) @test vertex(c, 2) == point(2, 2) c = Ring(point.([(1, 1), (2, 2)])) + @test Meshes.crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ @test vertex(c, 0) == point(2, 2) @test vertex(c, 1) == point(1, 1) @test vertex(c, 2) == point(2, 2) @@ -310,6 +316,8 @@ # Triangle in 2D space t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test vertex(t, 1) == point(0, 0) @test vertex(t, 2) == point(1, 0) @test vertex(t, 3) == point(0, 1) @@ -408,6 +416,8 @@ # Quadrangle in 2D space q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test Meshes.crs(q) <: Cartesian{NoDatum} + @test Meshes.lentype(q) == ℳ @test vertex(q, 1) == point(0, 0) @test vertex(q, 2) == point(1, 0) @test vertex(q, 3) == point(1, 1) @@ -477,6 +487,8 @@ poly = PolyArea([outer, hole1, hole2]) @test poly == poly @test poly ≈ poly + @test Meshes.crs(poly) <: Cartesian{NoDatum} + @test Meshes.lentype(poly) == ℳ # outer chain with 2 vertices is fixed by default poly = PolyArea(point.([(0, 0), (1, 0)])) @@ -680,6 +692,8 @@ @test nvertices(Tetrahedron) == 4 t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test vertex(t, 1) == point(0, 0, 0) @test vertex(t, 2) == point(1, 0, 0) @test vertex(t, 3) == point(0, 1, 0) @@ -736,6 +750,8 @@ point(1, 1, 1), point(0, 1, 1) ) + @test Meshes.crs(h) <: Cartesian{NoDatum} + @test Meshes.lentype(h) == ℳ @test vertex(h, 1) == point(0, 0, 0) @test vertex(h, 8) == point(0, 1, 1) @test h(T(0), T(0), T(0)) == point(0, 0, 0) @@ -844,6 +860,8 @@ @test nvertices(Pyramid) == 5 p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + @test Meshes.crs(p) <: Cartesian{NoDatum} + @test Meshes.lentype(p) == ℳ @test volume(p) ≈ T(1 / 3) * u"m^3" m = boundary(p) @test m isa Mesh diff --git a/test/primitives.jl b/test/primitives.jl index b02348c95..5856a68fc 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -3,16 +3,20 @@ @test embeddim(Point(1)) == 1 @test embeddim(Point(1, 2)) == 2 @test embeddim(Point(1, 2, 3)) == 3 + @test Meshes.crs(point(1, 1)) <: Cartesian{NoDatum} + @test Meshes.crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} + @test Meshes.crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} @test Meshes.lentype(Point(1.0, 1.0)) == Meshes.Met{Float64} @test Meshes.lentype(Point(1.0f0, 1.0f0)) == Meshes.Met{Float32} - @test Meshes.lentype(Point((T(1), T(1)))) == ℳ @test Meshes.lentype(Point(T(1), T(1))) == ℳ @test to(point(1)) == vector(1) @test to(point(1, 2)) == vector(1, 2) @test to(point(1, 2, 3)) == vector(1, 2, 3) + @test to(Point(Polar(T(√2), T(π / 4)))) ≈ vector(1, 1) + @test to(Point(Cylindrical(T(√2), T(π / 4), T(1)))) ≈ vector(1, 1, 1) @test point(1) - point(1) == vector(0) @test point(1, 2) - point(1, 1) == vector(0, 1) @@ -95,16 +99,6 @@ p2 = convert(P, p1) @test p2 isa P - # `to` function - p1 = point(1, 1) - p2 = point(1, 1, 1) - p3 = Point(Polar(T(√2), T(π / 4))) - p4 = Point(Cylindrical(T(√2), T(π / 4), T(1))) - @test to(p1) == vector(1, 1) - @test to(p2) == vector(1, 1, 1) - @test to(p3) ≈ vector(1, 1) - @test to(p4) ≈ vector(1, 1, 1) - # generalized inequality @test point(1, 1) ⪯ point(1, 1) @test !(point(1, 1) ≺ point(1, 1)) @@ -184,6 +178,8 @@ @testset "Ray" begin r = Ray(point(0, 0), vector(1, 1)) @test paramdim(r) == 1 + @test Meshes.crs(r) <: Cartesian{NoDatum} + @test Meshes.lentype(r) == ℳ @test measure(r) == typemax(ℳ) @test length(r) == typemax(ℳ) @test boundary(r) == point(0, 0) @@ -245,6 +241,8 @@ @testset "Line" begin l = Line(point(0, 0), point(1, 1)) @test paramdim(l) == 1 + @test Meshes.crs(l) <: Cartesian{NoDatum} + @test Meshes.lentype(l) == ℳ @test measure(l) == typemax(ℳ) @test length(l) == typemax(ℳ) @test isnothing(boundary(l)) @@ -280,6 +278,8 @@ @test p(T(1), T(0)) == point(1, 0, 0) @test paramdim(p) == 2 @test embeddim(p) == 3 + @test Meshes.crs(p) <: Cartesian{NoDatum} + @test Meshes.lentype(p) == ℳ @test measure(p) == typemax(ℳ)^2 @test area(p) == typemax(ℳ)^2 @test p(T(0), T(0)) == point(0, 0, 0) @@ -343,6 +343,8 @@ b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) @test embeddim(b) == 2 @test paramdim(b) == 1 + @test Meshes.crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) for method in [DeCasteljau(), Horner()] @@ -389,6 +391,7 @@ b = Box(point(0), point(1)) @test embeddim(b) == 1 @test paramdim(b) == 1 + @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == point(0) @test maximum(b) == point(1) @@ -397,6 +400,7 @@ b = Box(point(0, 0), point(1, 1)) @test embeddim(b) == 2 @test paramdim(b) == 2 + @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == point(0, 0) @test maximum(b) == point(1, 1) @@ -405,6 +409,7 @@ b = Box(point(0, 0, 0), point(1, 1, 1)) @test embeddim(b) == 3 @test paramdim(b) == 3 + @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == point(0, 0, 0) @test maximum(b) == point(1, 1, 1) @@ -520,6 +525,7 @@ b = Ball(point(1, 2, 3), T(5)) @test embeddim(b) == 3 @test paramdim(b) == 3 + @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test Meshes.center(b) == point(1, 2, 3) @test radius(b) == T(5) * u"m" @@ -599,6 +605,7 @@ s = Sphere(point(0, 0, 0), T(1)) @test embeddim(s) == 3 @test paramdim(s) == 2 + @test Meshes.crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test Meshes.center(s) == point(0, 0, 0) @test radius(s) == T(1) * u"m" @@ -703,6 +710,7 @@ e = Ellipsoid((T(3), T(2), T(1))) @test embeddim(e) == 3 @test paramdim(e) == 2 + @test Meshes.crs(e) <: Cartesian{NoDatum} @test Meshes.lentype(e) == ℳ @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") @test center(e) == point(0, 0, 0) @@ -732,6 +740,7 @@ d = Disk(p, T(2)) @test embeddim(d) == 3 @test paramdim(d) == 2 + @test Meshes.crs(d) <: Cartesian{NoDatum} @test Meshes.lentype(d) == ℳ @test plane(d) == p @test Meshes.center(d) == point(0, 0, 0) @@ -769,6 +778,7 @@ c = Circle(p, T(2)) @test embeddim(c) == 3 @test paramdim(c) == 1 + @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test plane(c) == p @test Meshes.center(c) == point(0, 0, 0) @@ -823,6 +833,7 @@ c = Cylinder(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) @test embeddim(c) == 3 @test paramdim(c) == 3 + @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(5) * u"m" @test bottom(c) == Plane(point(1, 2, 3), vector(0, 0, 1)) @@ -899,6 +910,7 @@ c = CylinderSurface(T(2)) @test embeddim(c) == 3 @test paramdim(c) == 2 + @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(2) * u"m" @test bottom(c) == Plane(point(0, 0, 0), vector(0, 0, 1)) @@ -962,6 +974,7 @@ p = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) @test embeddim(p) == 3 @test paramdim(p) == 2 + @test Meshes.crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test focallength(p) == T(2) * u"m" @test radius(p) == T(1) * u"m" @@ -1033,6 +1046,7 @@ c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 + @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) @@ -1094,6 +1108,7 @@ s = ConeSurface(d, a) @test embeddim(s) == 3 @test paramdim(s) == 2 + @test Meshes.crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) @@ -1127,6 +1142,7 @@ dt = Disk(pt, T(2)) f = Frustum(db, dt) @test embeddim(f) == 3 + @test Meshes.crs(f) <: Cartesian{NoDatum} @test Meshes.lentype(f) == ℳ @test boundary(f) == FrustumSurface(db, dt) @@ -1170,6 +1186,7 @@ f = FrustumSurface(db, dt) @test embeddim(f) == 3 @test paramdim(f) == 2 + @test Meshes.crs(f) <: Cartesian{NoDatum} @test Meshes.lentype(f) == ℳ @test isnothing(boundary(f)) @@ -1184,6 +1201,8 @@ @test point(1, 1, -1) ∈ t @test point(1, 1, 1) ∉ t @test paramdim(t) == 2 + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test Meshes.center(t) == point(1, 1, 1) @test normal(t) == vector(1, 0, 0) @test radii(t) == (T(2) * u"m", T(1) * u"m") diff --git a/test/sets.jl b/test/sets.jl index e70ce488f..5254aae97 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -4,6 +4,8 @@ t = Triangle(point(0, 0), point(1, 0), point(0, 1)) p = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) gset = GeometrySet([s, t, p]) + @test Meshes.crs(gset) <: Cartesian{NoDatum} + @test Meshes.lentype(gset) == ℳ @test [centroid(gset, i) for i in 1:3] == point.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) s = Segment(point(0, 0), point(1, 1)) @@ -34,18 +36,21 @@ @testset "PointSet" begin pset = PointSet(randpoint1(100)) @test embeddim(pset) == 1 + @test Meshes.crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{1} pset = PointSet(randpoint2(100)) @test embeddim(pset) == 2 + @test Meshes.crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{2} pset = PointSet(randpoint3(100)) @test embeddim(pset) == 3 + @test Meshes.crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{3} diff --git a/test/subdomains.jl b/test/subdomains.jl index ae33c6423..f3c05353e 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -3,6 +3,8 @@ inds = rand(1:100, 3) v = view(pset, inds) @test nelements(v) == 3 + @test Meshes.crs(v) <: Cartesian{NoDatum} + @test Meshes.lentype(v) == ℳ for i in 1:3 p = pset[inds[i]] @test v[i] == p @@ -13,6 +15,8 @@ inds = rand(1:100, 3) v = view(grid, inds) @test nelements(v) == 3 + @test Meshes.crs(v) <: Cartesian{NoDatum} + @test Meshes.lentype(v) == ℳ for i in 1:3 e = grid[inds[i]] @test v[i] == e @@ -25,6 +29,8 @@ inds = rand(1:4, 3) v = view(mesh, inds) @test nelements(v) == 3 + @test Meshes.crs(v) <: Cartesian{NoDatum} + @test Meshes.lentype(v) == ℳ for i in 1:3 e = mesh[inds[i]] @test v[i] == e diff --git a/test/trajecs.jl b/test/trajecs.jl index 73143ef6a..e03896777 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -3,6 +3,8 @@ s = Segment(point(0, 0, 0), point(0, 0, 1)) c = [s(t) for t in range(T(0), stop=T(1), length=10)] t = CylindricalTrajectory(c) + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 10 @test radius(t) == T(1) * u"m" @@ -11,6 +13,8 @@ b = BezierCurve([point(0, 0, 0), point(3, 3, 0), point(3, 0, 7)]) c = [b(t) for t in range(T(0), stop=T(1), length=20)] t = CylindricalTrajectory(c, T(2)) + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 20 @test radius(t) == T(2) * u"m" @@ -18,6 +22,8 @@ # trajectory with single cylinder t = CylindricalTrajectory([point(0, 0, 0)], T(1)) + @test Meshes.crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 1 @test radius(t) == T(1) * u"m" From 9d63241cb31cd4b3b348f62a2c356ec14bda0043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 5 Jun 2024 09:49:24 -0300 Subject: [PATCH 076/423] Refactor matrices.jl --- src/matrices.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/matrices.jl b/src/matrices.jl index 8a609e9af..646fa5b78 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -35,9 +35,8 @@ function laplacematrix(mesh; weights=:cotangent) if weights == :uniform uniformlaplacian!(L, 𝒩) elseif weights == :cotangent - v = vertices(ℳ) assertion(eltype(ℳ) <: Triangle, "cotangent weights only defined for triangle meshes") - cotangentlaplacian!(L, 𝒩, v) + cotangentlaplacian!(L, 𝒩, vertices(ℳ)) else throw(ArgumentError("invalid discretization weights")) end From 45a3288a10a61f0035d14ce5070debac2c2cca7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 5 Jun 2024 14:03:51 -0300 Subject: [PATCH 077/423] Improvements to matrices.jl --- src/matrices.jl | 52 +++++++++++++++++++++---------------- src/topologies.jl | 8 ++++-- src/transforms/smoothing.jl | 2 +- test/matrices.jl | 14 ++++++++-- test/topologies.jl | 14 ++++++++++ 5 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/matrices.jl b/src/matrices.jl index 646fa5b78..49593793b 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -3,12 +3,12 @@ # ------------------------------------------------------------------ """ - laplacematrix(mesh; weights=:cotangent) + laplacematrix(mesh; kind=nothing) The Laplace-Beltrami (a.k.a. Laplacian) matrix of the `mesh`. -Optionally specify the discretization `weights`. +Optionally, specify the `kind` of discretization. -## Weights +## Available discretizations * `:uniform` - `Lᵢⱼ = 1 / |𝒩(i)|, ∀j ∈ 𝒩(i)` * `:cotangent` - `Lᵢⱼ = cot(αᵢⱼ) + cot(βᵢⱼ), ∀j ∈ 𝒩(i)` @@ -20,30 +20,38 @@ Optionally specify the discretization `weights`. * Pinkall, U. & Polthier, K. 1993. [Computing discrete minimal surfaces and their conjugates] (https://projecteuclid.org/journals/experimental-mathematics/volume-2/issue-1/Computing-discrete-minimal-surfaces-and-their-conjugates/em/1062620735.full). """ -function laplacematrix(mesh; weights=:cotangent) - # convert to half-edge topology - ℳ = topoconvert(HalfEdgeTopology, mesh) +function laplacematrix(mesh; kind=nothing) + # select default discretization + 𝒦 = isnothing(kind) ? laplacekind(mesh) : kind + + # sanity checks + 𝒦 == :cotangent && assertion(eltype(mesh) <: Triangle, "cotangent weights only defined for triangle meshes") + + # adjust topology if necessary + 𝒯 = laplacetopo(topology(mesh)) # retrieve adjacency relation - 𝒩 = Adjacency{0}(topology(ℳ)) + 𝒩 = Adjacency{0}(𝒯) # initialize matrix - n = nvertices(ℳ) + n = nvertices(mesh) L = spzeros(n, n) - # fill matrix with weights - if weights == :uniform + # fill matrix + if 𝒦 == :uniform uniformlaplacian!(L, 𝒩) - elseif weights == :cotangent - assertion(eltype(ℳ) <: Triangle, "cotangent weights only defined for triangle meshes") - cotangentlaplacian!(L, 𝒩, vertices(ℳ)) - else - throw(ArgumentError("invalid discretization weights")) + elseif 𝒦 == :cotangent + cotangentlaplacian!(L, 𝒩, vertices(mesh)) end L end +laplacekind(mesh) = eltype(mesh) <: Triangle ? :cotangent : :uniform + +laplacetopo(topo) = topo +laplacetopo(topo::SimpleTopology) = convert(HalfEdgeTopology, topo) + function uniformlaplacian!(L, 𝒩) n = size(L, 1) for i in 1:n @@ -106,18 +114,18 @@ function measurematrix(mesh) end """ - adjacencymatrix(mesh) + adjacencymatrix(mesh; rank=paramdim(mesh)) -Return the adjacency matrix of the elements of the `mesh` -using the adjacency relation of the underlying topology. +The adjacency matrix of the `mesh` using the adjacency +relation of given `rank` for the underlying topology. """ -function adjacencymatrix(mesh) +function adjacencymatrix(mesh; rank=paramdim(mesh)) + # retrieve adjacency relation t = topology(mesh) - D = paramdim(mesh) - 𝒜 = Adjacency{D}(t) + 𝒜 = Adjacency{rank}(t) # initialize matrix - n = nelements(mesh) + n = nfaces(mesh, rank) A = spzeros(Int, n, n) # fill in matrix diff --git a/src/topologies.jl b/src/topologies.jl index 5449b768e..0661d8f71 100644 --- a/src/topologies.jl +++ b/src/topologies.jl @@ -55,7 +55,9 @@ segments = faces(topology, 1) """ function faces(t::Topology, rank) D = paramdim(t) - if rank == D + if rank == 0 + vertices(t) + elseif rank == D elements(t) elseif rank == D - 1 facets(t) @@ -71,7 +73,9 @@ Return the number of `rank`-faces of the `topology`. """ function nfaces(t::Topology, rank) D = paramdim(t) - if rank == D + if rank == 0 + nvertices(t) + elseif rank == D nelements(t) elseif rank == D - 1 nfacets(t) diff --git a/src/transforms/smoothing.jl b/src/transforms/smoothing.jl index 23d2a2db3..253b61d2b 100644 --- a/src/transforms/smoothing.jl +++ b/src/transforms/smoothing.jl @@ -40,7 +40,7 @@ function revert(transform::LambdaMuSmoothing, mesh::Mesh, cache) _smooth(mesh, L, n, λ, μ, revert=true) end -_laplacian(mesh) = laplacematrix(mesh, weights=:uniform) +_laplacian(mesh) = laplacematrix(mesh, kind=:uniform) function _smooth(mesh, L, n, λ, μ; revert=false) # retrieve vertices diff --git a/test/matrices.jl b/test/matrices.jl index 8a581a207..5d9118cef 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -3,7 +3,7 @@ points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) - L = laplacematrix(mesh, weights=:uniform) + L = laplacematrix(mesh, kind=:uniform) @test L == [ -1 1/3 1/3 0 1/3 1/3 -1 0 1/3 1/3 @@ -16,7 +16,7 @@ points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) connec = connect.([(1, 2, 3, 4)], Quadrangle) mesh = SimpleMesh(points, connec) - @test_throws AssertionError laplacematrix(mesh, weights=:cotangent) + @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) # full Laplace-Beltrami operator sphere = Sphere(point(0, 0, 0), T(1)) @@ -37,6 +37,8 @@ @test minimum(d) == 2 @test maximum(d) == 4 @test length(findall(==(2), d)) == 4 + A = adjacencymatrix(grid, rank=0) + @test size(A) == (101*101, 101*101) # adjacency of SimpleMesh points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) @@ -44,4 +46,12 @@ mesh = SimpleMesh(points, connec, relations=true) A = adjacencymatrix(mesh) @test A == [0 1; 1 0] + A = adjacencymatrix(mesh, rank=0) + @test A == [ + 0 1 1 0 0 + 1 0 1 1 0 + 1 1 0 0 1 + 0 1 0 0 1 + 0 0 1 1 0 + ] end diff --git a/test/topologies.jl b/test/topologies.jl index 590145748..ab8efdb45 100644 --- a/test/topologies.jl +++ b/test/topologies.jl @@ -21,10 +21,12 @@ @test nfacets(t) == 4 @test nvertices(t) == 4 @test nfaces(t, 1) == 3 + @test nfaces(t, 0) == 4 @test element(t, 1) == connect((1, 2)) @test element(t, 2) == connect((2, 3)) @test element(t, 3) == connect((3, 4)) @test faces(t, 1) == elements(t) + @test faces(t, 0) == vertices(t) @test vertices(t) == 1:4 @test vertex(t, 1) == 1 @test vertex(t, 4) == 4 @@ -87,9 +89,11 @@ @test nvertices(t) == 20 @test nfaces(t, 2) == 12 @test nfaces(t, 1) == 31 + @test nfaces(t, 0) == 20 @test element(t, 1) == connect((1, 2, 6, 5)) @test element(t, 5) == connect((6, 7, 11, 10)) @test faces(t, 2) == elements(t) + @test faces(t, 0) == vertices(t) @test vertices(t) == 1:20 @test vertex(t, 1) == 1 @test vertex(t, 20) == 20 @@ -234,9 +238,11 @@ @test nvertices(t) == 60 @test nfaces(t, 3) == 24 @test nfaces(t, 2) == 3 * 24 + 3 * 4 + 4 * 2 + 3 * 2 + @test nfaces(t, 0) == 60 @test element(t, 1) == connect((1, 2, 6, 5, 21, 22, 26, 25), Hexahedron) @test element(t, 5) == connect((6, 7, 11, 10, 26, 27, 31, 30), Hexahedron) @test faces(t, 3) == elements(t) + @test faces(t, 0) == vertices(t) @test vertices(t) == 1:60 @test vertex(t, 1) == 1 @test vertex(t, 60) == 60 @@ -422,6 +428,7 @@ @test nvertices(t) == 4 @test nfaces(t, 2) == 2 @test nfaces(t, 1) == 5 + @test nfaces(t, 0) == 4 test_halfedge(elems, t) # 2 triangles + 2 quadrangles @@ -433,6 +440,7 @@ @test nvertices(t) == 6 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 9 + @test nfaces(t, 0) == 6 test_halfedge(elems, t) # 1 triangle + 3 quadrangles + 1 triangle hole @@ -444,6 +452,7 @@ @test nvertices(t) == 7 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 11 + @test nfaces(t, 0) == 7 @test vertices(t) == 1:7 @test vertex(t, 1) == 1 @test vertex(t, 7) == 7 @@ -458,6 +467,7 @@ @test nvertices(t) == 7 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 11 + @test nfaces(t, 0) == 7 test_halfedge(elems, t) # correct construction from inconsistent orientation @@ -490,6 +500,7 @@ @test vertex(t, 4) == 4 @test nfaces(t, 2) == 2 @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 4 # 2 triangles + 2 quadrangles elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) @@ -503,6 +514,7 @@ @test nvertices(t) == 6 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 6 # 1 triangle + 3 quadrangles + 1 triangle hole elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) @@ -516,6 +528,7 @@ @test nvertices(t) == 7 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 7 # convert from other topologies g = GridTopology(2, 2) @@ -525,5 +538,6 @@ @test nvertices(t) == 9 @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 12 + @test nfaces(t, 0) == 9 end end From 4f98199cc4e0b1b2ab94d270fae0bcf642dcc0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 5 Jun 2024 14:20:24 -0300 Subject: [PATCH 078/423] Add matrices to docs --- docs/make.jl | 3 +- docs/src/algorithms/matrices.md | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 docs/src/algorithms/matrices.md diff --git a/docs/make.jl b/docs/make.jl index 86830fe18..83512406c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -59,7 +59,8 @@ makedocs( "algorithms/orientation.md", "algorithms/neighborsearch.md", "algorithms/boundingbox.md", - "algorithms/hulls.md" + "algorithms/hulls.md", + "algorithms/matrices.md" ], "Transforms" => "transforms.md", "Visualization" => "visualization.md", diff --git a/docs/src/algorithms/matrices.md b/docs/src/algorithms/matrices.md new file mode 100644 index 000000000..ae33f500e --- /dev/null +++ b/docs/src/algorithms/matrices.md @@ -0,0 +1,62 @@ +# Matrices + +```@example matrices +using Meshes # hide +import CairoMakie as Mke # hide +``` + +## Laplacian + +```@docs +laplacematrix +``` + +```@example matrices +grid = CartesianGrid(10, 10) + +laplacematrix(grid, kind = :uniform) +``` + +```@example matrices +points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] +connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) +mesh = SimpleMesh(points, connec) + +laplacematrix(mesh, kind = :cotangent) +``` + +## Measure + +```@docs +measurematrix +``` + +```@example matrices +grid = CartesianGrid(10, 10) + +measurematrix(grid) +``` + +```@example matrices +points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] +connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) +mesh = SimpleMesh(points, connec) + +measurematrix(mesh) +``` + +## Adjacency + +```@docs +adjacencymatrix +``` + +```@example matrices +grid = CartesianGrid(10, 10) + +adjacencymatrix(grid) +``` + +```@example matrices +adjacencymatrix(grid, rank = 0) +``` From aed5b0dc26b2213c144125577c8bcc7149879ead Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:22:32 -0300 Subject: [PATCH 079/423] :robot: Format .jl files (#868) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- test/matrices.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/matrices.jl b/test/matrices.jl index 5d9118cef..c27a424a3 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -38,7 +38,7 @@ @test maximum(d) == 4 @test length(findall(==(2), d)) == 4 A = adjacencymatrix(grid, rank=0) - @test size(A) == (101*101, 101*101) + @test size(A) == (101 * 101, 101 * 101) # adjacency of SimpleMesh points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) From b17efda0d0b7d0880c16f9af0b646dc120bcdbc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 5 Jun 2024 16:54:32 -0300 Subject: [PATCH 080/423] Improvements to matrices.jl --- docs/src/algorithms/matrices.md | 2 +- src/matrices.jl | 27 ++++--- test/matrices.jl | 130 +++++++++++++++++++------------- 3 files changed, 96 insertions(+), 63 deletions(-) diff --git a/docs/src/algorithms/matrices.md b/docs/src/algorithms/matrices.md index ae33f500e..aaeaa794e 100644 --- a/docs/src/algorithms/matrices.md +++ b/docs/src/algorithms/matrices.md @@ -5,7 +5,7 @@ using Meshes # hide import CairoMakie as Mke # hide ``` -## Laplacian +## Laplace ```@docs laplacematrix diff --git a/src/matrices.jl b/src/matrices.jl index 49593793b..dc4f4f4c2 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -2,6 +2,13 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ +# helper function to select default Laplacian discretization +laplacekind(mesh) = eltype(mesh) <: Triangle ? :cotangent : :uniform + +# helper function to convert topology if necessary +laplacetopo(topo::SimpleTopology) = convert(HalfEdgeTopology, topo) +laplacetopo(topo) = topo + """ laplacematrix(mesh; kind=nothing) @@ -47,11 +54,6 @@ function laplacematrix(mesh; kind=nothing) L end -laplacekind(mesh) = eltype(mesh) <: Triangle ? :cotangent : :uniform - -laplacetopo(topo) = topo -laplacetopo(topo::SimpleTopology) = convert(HalfEdgeTopology, topo) - function uniformlaplacian!(L, 𝒩) n = size(L, 1) for i in 1:n @@ -91,20 +93,23 @@ as `Δ = M⁻¹L`. When solving systems of the form `Δu = f`, it is useful to write `Lu = Mf` and exploit the symmetry of `L`. """ function measurematrix(mesh) - # convert to half-edge topology - ℳ = topoconvert(HalfEdgeTopology, mesh) + # adjust topology if necessary + 𝒯 = laplacetopo(topology(mesh)) + + # parametric dimension + D = paramdim(mesh) # retrieve coboundary relation - ∂ = Coboundary{0,2}(topology(ℳ)) + ∂ = Coboundary{0,D}(𝒯) # pre-compute all measures - A = measure.(ℳ) + A = measure.(mesh) # initialize matrix - n = nvertices(ℳ) + n = nvertices(mesh) M = oneunit(eltype(A)) * I(n) - # fill matrix with measures + # fill matrix for i in 1:n Aᵢ = sum(A[∂(i)]) / 3 M[i, i] = 2Aᵢ diff --git a/test/matrices.jl b/test/matrices.jl index c27a424a3..c6e25838b 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -1,57 +1,85 @@ @testset "Matrices" begin - # uniform weights for simple mesh - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - L = laplacematrix(mesh, kind=:uniform) - @test L == [ - -1 1/3 1/3 0 1/3 - 1/3 -1 0 1/3 1/3 - 1/3 0 -1 1/3 1/3 - 0 1/3 1/3 -1 1/3 - 1/4 1/4 1/4 1/4 -1 - ] + @testset "Laplace" begin + # uniform weights for simple mesh + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + L = laplacematrix(mesh, kind=:uniform) + @test L == [ + -1 1/3 1/3 0 1/3 + 1/3 -1 0 1/3 1/3 + 1/3 0 -1 1/3 1/3 + 0 1/3 1/3 -1 1/3 + 1/4 1/4 1/4 1/4 -1 + ] - # cotangent weights only defined for triangle meshes - points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - connec = connect.([(1, 2, 3, 4)], Quadrangle) - mesh = SimpleMesh(points, connec) - @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) + # cotangent weights for simple mesh + L = laplacematrix(mesh, kind=:cotangent) + @test size(L) == (5, 5) - # full Laplace-Beltrami operator - sphere = Sphere(point(0, 0, 0), T(1)) - mesh = simplexify(sphere) - L = laplacematrix(mesh) - M = measurematrix(mesh) - @test issymmetric(L) - @test issparse(L) - @test isdiag(M) + # cotangent weights only defined for triangle meshes + points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + connec = connect.([(1, 2, 3, 4)], Quadrangle) + mesh = SimpleMesh(points, connec) + @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) - # adjacency of CartesianGrid - grid = cartgrid(100, 100) - A = adjacencymatrix(grid) - d = sum(A, dims=2) - @test size(A) == (10000, 10000) - @test issymmetric(A) - @test issparse(A) - @test minimum(d) == 2 - @test maximum(d) == 4 - @test length(findall(==(2), d)) == 4 - A = adjacencymatrix(grid, rank=0) - @test size(A) == (101 * 101, 101 * 101) + # uniform weights for Cartesian grid + grid = CartesianGrid(10, 10) + L = laplacematrix(grid, kind=:uniform) + @test size(L) == (11 * 11, 11 * 11) + grid = CartesianGrid(10, 10, 10) + L = laplacematrix(grid, kind=:uniform) + @test size(L) == (11 * 11 * 11, 11 * 11 * 11) + end - # adjacency of SimpleMesh - points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) - connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) - mesh = SimpleMesh(points, connec, relations=true) - A = adjacencymatrix(mesh) - @test A == [0 1; 1 0] - A = adjacencymatrix(mesh, rank=0) - @test A == [ - 0 1 1 0 0 - 1 0 1 1 0 - 1 1 0 0 1 - 0 1 0 0 1 - 0 0 1 1 0 - ] + @testset "Measure" begin + # measure matrix of simple mesh + points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + M = measurematrix(mesh) + @test size(M) == (5, 5) + @test isdiag(M) + end + + @testset "Adjacency" begin + # adjacency of CartesianGrid + grid = cartgrid(100, 100) + A = adjacencymatrix(grid) + d = sum(A, dims=2) + @test size(A) == (10000, 10000) + @test issymmetric(A) + @test issparse(A) + @test minimum(d) == 2 + @test maximum(d) == 4 + @test length(findall(==(2), d)) == 4 + A = adjacencymatrix(grid, rank=0) + @test size(A) == (101 * 101, 101 * 101) + + # adjacency of SimpleMesh + points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) + connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) + mesh = SimpleMesh(points, connec, relations=true) + A = adjacencymatrix(mesh) + @test A == [0 1; 1 0] + A = adjacencymatrix(mesh, rank=0) + @test A == [ + 0 1 1 0 0 + 1 0 1 1 0 + 1 1 0 0 1 + 0 1 0 0 1 + 0 0 1 1 0 + ] + end + + @testset "Miscellaneous" begin + # full Laplace-Beltrami operator + sphere = Sphere(point(0, 0, 0)) + mesh = simplexify(sphere) + L = laplacematrix(mesh) + M = measurematrix(mesh) + @test issymmetric(L) + @test issparse(L) + @test isdiag(M) + end end From e3e41fe920cd2672f6ed2c5df014bce44a3b2de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 5 Jun 2024 17:03:16 -0300 Subject: [PATCH 081/423] Improvements to matrices.jl --- src/matrices.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/matrices.jl b/src/matrices.jl index dc4f4f4c2..864681498 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -6,8 +6,8 @@ laplacekind(mesh) = eltype(mesh) <: Triangle ? :cotangent : :uniform # helper function to convert topology if necessary -laplacetopo(topo::SimpleTopology) = convert(HalfEdgeTopology, topo) -laplacetopo(topo) = topo +adjusttopo(topo::SimpleTopology) = convert(HalfEdgeTopology, topo) +adjusttopo(topo) = topo """ laplacematrix(mesh; kind=nothing) @@ -35,7 +35,7 @@ function laplacematrix(mesh; kind=nothing) 𝒦 == :cotangent && assertion(eltype(mesh) <: Triangle, "cotangent weights only defined for triangle meshes") # adjust topology if necessary - 𝒯 = laplacetopo(topology(mesh)) + 𝒯 = adjusttopo(topology(mesh)) # retrieve adjacency relation 𝒩 = Adjacency{0}(𝒯) @@ -94,7 +94,7 @@ is useful to write `Lu = Mf` and exploit the symmetry of `L`. """ function measurematrix(mesh) # adjust topology if necessary - 𝒯 = laplacetopo(topology(mesh)) + 𝒯 = adjusttopo(topology(mesh)) # parametric dimension D = paramdim(mesh) @@ -125,9 +125,11 @@ The adjacency matrix of the `mesh` using the adjacency relation of given `rank` for the underlying topology. """ function adjacencymatrix(mesh; rank=paramdim(mesh)) + # adjust topology if necessary + 𝒯 = adjusttopo(topology(mesh)) + # retrieve adjacency relation - t = topology(mesh) - 𝒜 = Adjacency{rank}(t) + 𝒜 = Adjacency{rank}(𝒯) # initialize matrix n = nfaces(mesh, rank) From daa2349f2c8486355c783470bb521b363249994b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:47:14 -0300 Subject: [PATCH 082/423] Propagate the `Datum` in the code (#869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Propagate the 'Datum' in the code * Add docstring * Apply suggestions * Apply suggestions * Add tests * Rearrange the code * Apply suggestions from code review * Apply suggestions * Format code * Apply suggestions --------- Co-authored-by: Júlio Hoffimann --- src/Meshes.jl | 7 +---- src/boundingboxes.jl | 4 +-- src/coords.jl | 17 ----------- src/domains.jl | 2 +- src/intersections/boxes.jl | 4 +-- src/intersections/planes.jl | 2 +- src/mesh/rectilineargrid.jl | 2 +- src/multigeoms.jl | 2 +- src/partitioning/bisectfraction.jl | 2 +- src/polytopes.jl | 2 +- src/polytopes/hexahedron.jl | 21 +++++++------ src/polytopes/ngon.jl | 4 +-- src/polytopes/segment.jl | 2 +- src/polytopes/tetrahedron.jl | 2 +- src/predicates/intersects.jl | 2 +- src/primitives/bezier.jl | 2 +- src/primitives/box.jl | 2 +- src/primitives/circle.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/point.jl | 11 +++++-- src/refinement/catmullclark.jl | 6 ++-- src/refinement/quad.jl | 4 +-- src/refinement/tri.jl | 2 +- src/sets.jl | 2 +- src/transforms.jl | 2 +- src/transforms/affine.jl | 2 +- src/utils.jl | 7 +++++ test/boundingboxes.jl | 9 ++++++ test/domains.jl | 5 ++++ test/intersections.jl | 16 ++++++++++ test/mesh.jl | 5 ++-- test/multigeoms.jl | 10 +++++++ test/partitioning.jl | 6 ++++ test/polytopes.jl | 47 ++++++++++++++++++++++++++++++ test/primitives.jl | 31 ++++++++++++++++++++ test/refinement.jl | 22 ++++++++++++++ test/sets.jl | 6 ++++ test/transforms.jl | 12 ++++++++ 38 files changed, 222 insertions(+), 66 deletions(-) delete mode 100644 src/coords.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index abd4be940..b0cb7f549 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -54,9 +54,6 @@ include("vectors.jl") # geometries include("geometries.jl") -# coordinates -include("coords.jl") - # topological objects include("connectivities.jl") include("topologies.jl") @@ -160,6 +157,7 @@ export degree, Horner, DeCasteljau, + coords, to, radius, radii, @@ -217,9 +215,6 @@ export MultiPolygon, MultiPolyhedron, - # coordinates - coords, - # connectivities Connectivity, paramdim, diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index ececda03a..b6bf9f26a 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -36,7 +36,7 @@ function boundingbox(r::Ray) v = r(1) - r(0) l = lower.(to(p), v) u = upper.(to(p), v) - Box(Point(coords(l)), Point(coords(u))) + Box(withdatum(r, l), withdatum(r, u)) end function boundingbox(s::Sphere{Dim}) where {Dim} @@ -96,5 +96,5 @@ function _pboxes(points) @. xmin = min(x, xmin) @. xmax = max(x, xmax) end - Box(Point(coords(xmin)), Point(coords(xmax))) + Box(withdatum(p, xmin), withdatum(p, xmax)) end diff --git a/src/coords.jl b/src/coords.jl deleted file mode 100644 index 749008ca5..000000000 --- a/src/coords.jl +++ /dev/null @@ -1,17 +0,0 @@ -# ------------------------------------------------------------------ -# Licensed under the MIT License. See LICENSE in the project root. -# ------------------------------------------------------------------ - -""" - coords(point) - -Return the coordinates of the `point`. -""" -coords(A::Point) = A.coords - -""" - coords(vec) - -Return the coordinates of the `vec`. -""" -coords(vec::StaticVector) = Cartesian(Tuple(vec)) diff --git a/src/domains.jl b/src/domains.jl index 74cc91736..5b57d3a61 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -107,7 +107,7 @@ function centroid(d::Domain) x = vector.(1:n) w = volume.(1:n) all(iszero, w) && (w = ones(eltype(w), n)) - Point(coords(sum(w .* x) / sum(w))) + withdatum(d, sum(w .* x) / sum(w)) end """ diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 4a1b5afc1..17b9b218a 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -14,8 +14,8 @@ function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} m2, M2 = to.(extrema(box₂)) # relevant vertices - u = Point(coords(max.(m1, m2))) - v = Point(coords(min.(M1, M2))) + u = withdatum(box₁, max.(m1, m2)) + v = withdatum(box₁, min.(M1, M2)) # auxiliary variables δ = v - u diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index 691513cd5..a63f65cf8 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -22,7 +22,7 @@ function intersection(f, plane1::Plane, plane2::Plane) c2 = (h2 - h1 * n1n2) / (1 - n1n2^2) p1 = (c1 * n1) + (c2 * n2) p2 = p1 + d - return @IT Intersecting Line(Point(coords(p1 * u)), Point(coords(p2 * u))) f + return @IT Intersecting Line(withdatum(plane1, p1 * u), withdatum(plane1, p2 * u)) f end end diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index c02920266..9f65a9ed7 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -55,7 +55,7 @@ function centroid(g::RectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) - Point(coords((to(p1) + to(p2)) / 2)) + withdatum(g, (to(p1) + to(p2)) / 2) end function Base.getindex(g::RectilinearGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} diff --git a/src/multigeoms.jl b/src/multigeoms.jl index b55ef0ec1..6d3117a22 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -47,7 +47,7 @@ end function centroid(m::Multi) cs = to.(centroid.(m.geoms)) - Point(coords(sum(cs) / length(cs))) + withdatum(m, sum(cs) / length(cs)) end rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index 785b5012a..7f90df63c 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -41,7 +41,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP while iter < maxiter m = (a + b) / 2 - bisectpoint = BisectPointPartition(n, Point(coords(m))) + bisectpoint = BisectPointPartition(n, withdatum(domain, m)) subsets, metadata = partitioninds(rng, domain, bisectpoint) g = length(subsets[1]) / nelements(domain) diff --git a/src/polytopes.jl b/src/polytopes.jl index f4d02d721..2778bc91a 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -227,7 +227,7 @@ nvertices(p::Polytope) = nvertices(typeof(p)) Return the centroid of the `polytope`. """ -centroid(p::Polytope) = Point(coords(sum(to, vertices(p)) / length(vertices(p)))) +centroid(p::Polytope) = withdatum(p, sum(to, vertices(p)) / length(vertices(p))) """ unique(polytope) diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index bd811d34b..8a269dd4c 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -19,17 +19,16 @@ function (h::Hexahedron)(u, v, w) throw(DomainError((u, v, w), "h(u, v, w) is not defined for u, v, w outside [0, 1]³.")) end A1, A2, A4, A3, A5, A6, A8, A7 = to.(h.vertices) - Point( - coords( - (1 - u) * (1 - v) * (1 - w) * A1 + - u * (1 - v) * (1 - w) * A2 + - (1 - u) * v * (1 - w) * A3 + - u * v * (1 - w) * A4 + - (1 - u) * (1 - v) * w * A5 + - u * (1 - v) * w * A6 + - (1 - u) * v * w * A7 + - u * v * w * A8 - ) + withdatum( + h, + (1 - u) * (1 - v) * (1 - w) * A1 + + u * (1 - v) * (1 - w) * A2 + + (1 - u) * v * (1 - w) * A3 + + u * v * (1 - w) * A4 + + (1 - u) * (1 - v) * w * A5 + + u * (1 - v) * w * A6 + + (1 - u) * v * w * A7 + + u * v * w * A8 ) end diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index ec6db3fb9..15889bf63 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -89,7 +89,7 @@ function (t::Triangle)(u, v) throw(DomainError((u, v), "invalid barycentric coordinates for triangle.")) end v₁, v₂, v₃ = to.(t.vertices) - Point(coords(v₁ * w + v₂ * u + v₃ * v)) + withdatum(t, v₁ * w + v₂ * u + v₃ * v) end # ------------ @@ -102,5 +102,5 @@ function (q::Quadrangle)(u, v) throw(DomainError((u, v), "q(u, v) is not defined for u, v outside [0, 1]².")) end c₀₀, c₀₁, c₁₁, c₁₀ = to.(q.vertices) - Point(coords(c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v)) + withdatum(q, c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v) end diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index bc1b61b0a..8da928fa8 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -23,7 +23,7 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] function center(s::Segment) a, b = extrema(s) - Point(coords((to(a) + to(b)) / 2)) + withdatum(s, (to(a) + to(b)) / 2) end Base.isapprox(s₁::Segment, s₂::Segment; kwargs...) = diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index fd7baf79d..9c5164766 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -20,7 +20,7 @@ function (t::Tetrahedron)(u, v, w) throw(DomainError((u, v, w), "invalid barycentric coordinates for tetrahedron.")) end v₁, v₂, v₃, v₄ = to.(t.vertices) - Point(coords(v₁ * z + v₂ * u + v₃ * v + v₄ * w)) + withdatum(t, v₁ * z + v₂ * u + v₃ * v + v₄ * w) end Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron{Dim}}) where {Dim} = diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 7096f76af..5a8090b59 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -245,7 +245,7 @@ intersects(m::Multi, c::Chain) = intersects(c, m) # ------------------ # support point in Minkowski difference -minkowskipoint(g₁::Geometry, g₂::Geometry, d) = Point(coords(supportfun(g₁, d) - supportfun(g₂, -d))) +minkowskipoint(g₁::Geometry, g₂::Geometry, d) = withdatum(g₁, supportfun(g₁, d) - supportfun(g₂, -d)) # origin of coordinate system minkowskiorigin(Dim, ℒ) = Point(ntuple(i -> zero(ℒ), Dim)) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 0094d2930..03f9c388c 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -103,7 +103,7 @@ function (curve::BezierCurve)(t, ::Horner) end b₀ = bᵢ₋₁ - Point(coords(b₀)) + withdatum(curve, b₀) end Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve{Dim}}) where {Dim} = BezierCurve(rand(rng, Point{Dim}, 5)) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 911f40b6e..5288ae264 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -37,7 +37,7 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -center(b::Box) = Point(coords((to(b.max) + to(b.min)) / 2)) +center(b::Box) = withdatum(b, (to(b.max) + to(b.min)) / 2) diagonal(b::Box) = norm(b.max - b.min) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 1bf253af7..09ede78bd 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -34,7 +34,7 @@ function Circle(p1::Point{3}, p2::Point{3}, p3::Point{3}) F = to(p1) ⋅ n⃗ M = transpose([n⃗ v12 v13]) u = [F, m12 ⋅ v12, m13 ⋅ v13] - O = Point(coords(uinv(M) * u)) + O = withdatum(p1, uinv(M) * u) r = norm(p1 - O) Circle(Plane(O, n⃗), r) end diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 7797e6c55..35a00ddc4 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -66,7 +66,7 @@ top(c::CylinderSurface) = c.top function center(c::CylinderSurface) a = to(c.bot(0, 0)) b = to(c.top(0, 0)) - Point(coords((a .+ b) ./ 2)) + withdatum(c, (a .+ b) ./ 2) end axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index d11fee71b..65c8e022d 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -54,6 +54,13 @@ center(p::Point) = p Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) = isapprox(A.coords, B.coords; atol, kwargs...) +""" + coords(point) + +Return the coordinates of the `point`. +""" +coords(A::Point) = A.coords + """ to(point) @@ -76,7 +83,7 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) + v)) ++(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withdatum(A, to(A) + v) +(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A + v """ @@ -86,7 +93,7 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim}, v::Vec{Dim}) where {Dim} = Point(coords(to(A) - v)) +-(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withdatum(A, to(A) - v) -(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A - v """ diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index b078d3c0d..061b843f6 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -34,7 +34,7 @@ function refine(mesh, ::CatmullClark) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coords(cₒ)) + withdatum(mesh, cₒ) end # add midpoints of edges @@ -46,7 +46,7 @@ function refine(mesh, ::CatmullClark) ∑p = sum(to, ps) ∑q = sum(to, qs) M = length(ps) + length(qs) - Point(coords((∑p + ∑q) / M)) + withdatum(mesh, (∑p + ∑q) / M) end # move original vertices @@ -68,7 +68,7 @@ function refine(mesh, ::CatmullClark) sum(to, uv) / 2 end / n - Point(coords((F + 2R + (n - 3)P) / n)) + withdatum(mesh, (F + 2R + (n - 3)P) / n) end # new points in refined mesh diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index 06a0a8a52..03838cd7b 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -23,7 +23,7 @@ function refine(mesh, ::QuadRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coords(cₒ)) + withdatum(mesh, cₒ) end # add midpoints of edges @@ -31,7 +31,7 @@ function refine(mesh, ::QuadRefinement) fpts = map(1:nfacets(t)) do edge ps = view(points, ∂₁₀(edge)) cₒ = sum(to, ps) / length(ps) - Point(coords(cₒ)) + withdatum(mesh, cₒ) end # original vertices diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index cf18a1f27..dddff390f 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -26,7 +26,7 @@ function refine(mesh, ::TriRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - Point(coords(cₒ)) + withdatum(mesh, cₒ) end # original vertices diff --git a/src/sets.jl b/src/sets.jl index 5541ab029..3a28af9a5 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -71,4 +71,4 @@ PointSet(points) = PointSet(map(identity, points)) centroid(d::PointSet, ind::Int) = d[ind] -centroid(d::PointSet) = Point(coords(sum(to, d) / nelements(d))) +centroid(d::PointSet) = withdatum(d, sum(to, d) / nelements(d)) diff --git a/src/transforms.jl b/src/transforms.jl index 318ae79e1..fda5e8873 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -70,7 +70,7 @@ applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} = applycoord(::CoordinateTransform, x) = x # special treatment for Point -applycoord(t::CoordinateTransform, p::Point) = Point(coords(applycoord(t, to(p)))) +applycoord(t::CoordinateTransform, p::Point) = withdatum(p, applycoord(t, to(p))) # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 25a032b7b..657a19f43 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -55,7 +55,7 @@ end applycoord(t::Affine, v::Vec) = t.A * v -applycoord(t::Affine, p::Point) = Point(coords(t.A * to(p) + t.b)) +applycoord(t::Affine, p::Point) = withdatum(p, t.A * to(p) + t.b) # -------------- # SPECIAL CASES diff --git a/src/utils.jl b/src/utils.jl index 0ce336e1d..d2120b634 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -35,6 +35,13 @@ function collectat(iter, inds) end end +""" + withdatum(g, v) + +Point at the end of the vector `v` with the same datum of `g`. +""" +withdatum(g::GeometryOrDomain, v::StaticVector) = Point(Cartesian{datum(crs(g))}(Tuple(v))) + """ signarea(A, B, C) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 924b965b2..dd683c1a5 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -118,4 +118,13 @@ p = ParaboloidSurface(point(1, 2, 3), T(5), T(4)) @test boundingbox(p) ≈ Box(point(-4, -3, 3), point(6, 7, 73 / 16)) + + # datum propagation + c = Cartesian{WGS84Latest}(T(-1), T(1)) + r = Ray(Point(c), vector(1, -1)) + @test datum(Meshes.crs(boundingbox(r))) === WGS84Latest + c = Cartesian{WGS84Latest}(T(0), T(0)) + g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) + m = convert(SimpleMesh, g) + @test datum(Meshes.crs(boundingbox(m))) === WGS84Latest end diff --git a/test/domains.jl b/test/domains.jl index ca3215fff..4ecc1077d 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -30,6 +30,11 @@ @test vcat(dom3, dom1) == GeometrySet([collect(dom3); collect(dom1)]) @test vcat(dom1, dom2, dom3) == GeometrySet([collect(dom1); collect(dom2); collect(dom3)]) + # datum propagation + c = Cartesian{WGS84Latest}(T(1), T(1)) + dom = DummyDomain(Point(c)) + @test datum(Meshes.crs(centroid(dom))) === WGS84Latest + dom = DummyDomain(point(0, 0)) @test sprint(show, dom) == "3 DummyDomain" @test sprint(show, MIME"text/plain"(), dom) == """ diff --git a/test/intersections.jl b/test/intersections.jl index 2778c5225..bf7683e66 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -764,6 +764,13 @@ p2 = Plane(point(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) @test intersection(p1, p2) |> type == Intersecting @test p1 ∩ p2 == Line(point(1, 0, 0), point(1, 1, 0)) + + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + p1 = Plane(Point(c1), vector(0, 0, 1)) + p2 = Plane(Point(c2), vector(1 / sqrt(2), 0, 1 / sqrt(2))) + @test datum(Meshes.crs(p1 ∩ p2)) === WGS84Latest end @testset "Boxes" begin @@ -825,6 +832,15 @@ b2 = Box(point(0.5, 0.5), point(2, 2)) @inferred someornone(b1, b2) + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(1)) + c3 = Cartesian{WGS84Latest}(T(0.5), T(0.5)) + c4 = Cartesian{WGS84Latest}(T(2), T(2)) + b1 = Box(Point(c1), Point(c2)) + b2 = Box(Point(c3), Point(c4)) + @test datum(Meshes.crs(b1 ∩ b2)) === WGS84Latest + # Ray-Box intersection b = Box(point(0, 0, 0), point(1, 1, 1)) diff --git a/test/mesh.jl b/test/mesh.jl index a9761e7de..c2a36ebb6 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -280,9 +280,10 @@ @test vertex(grid, 1) == point(0, 0) @test vertex(grid, 121) == point(10, 10) - # datum + # constructor with datum & datum propagation grid = RectilinearGrid{WGS84Latest}(x, y) @test datum(Meshes.crs(grid)) === WGS84Latest + @test datum(Meshes.crs(centroid(grid))) === WGS84Latest # conversion cg = cartgrid(10, 10) @@ -399,7 +400,7 @@ @test_throws BoundsError grid[2:6, :] @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") - # datum + # constructor with datum grid = StructuredGrid{WGS84Latest}(X, Y) @test datum(Meshes.crs(grid)) === WGS84Latest diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 28546d077..9a26295ae 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -85,4 +85,14 @@ @test Multi([ring, ring]) isa MultiRing @test Multi([tri, tri]) isa MultiPolygon @test Multi([poly, poly]) isa MultiPolygon + + # datum propagation + tuples1 = [T.((0, 0)), T.((1, 0)), T.((1, 1)), T.((0, 1))] + tuples2 = [T.((1, 1)), T.((2, 1)), T.((2, 2)), T.((1, 2))] + points1 = Point.(Cartesian{WGS84Latest}.(tuples1)) + points2 = Point.(Cartesian{WGS84Latest}.(tuples2)) + poly1 = PolyArea(points1) + poly2 = PolyArea(points2) + multi = Multi([poly1, poly2]) + @test datum(Meshes.crs(centroid(multi))) === WGS84Latest end diff --git a/test/partitioning.jl b/test/partitioning.jl index 14dbb96b4..cac1d838a 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -207,6 +207,12 @@ rng = StableRNG(123) p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) @test p1 == p2 + + # datum propagation + c = Cartesian{WGS84Latest}(T(0), T(0)) + g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) + p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + @test datum(Meshes.crs(first(p))) === WGS84Latest end @testset "BallPartition" begin diff --git a/test/polytopes.jl b/test/polytopes.jl index 4a2b41bd7..92c26ba71 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -70,6 +70,12 @@ @test embeddim(s) == 3 @test Meshes.lentype(s) === Meshes.Met{Float64} + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(1)) + s = Segment(Point(c1), Point(c2)) + @test datum(Meshes.crs(s(T(0)))) === WGS84Latest + s = Segment(point(0, 0), point(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" if T === Float32 @@ -250,6 +256,12 @@ r2 = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test innerangles(r1) ≈ innerangles(r2) + # datum propagation + tuples = [T.((0, 0)), T.((1, 0)), T.((1, 1)), T.((0, 1))] + points = Point.(Cartesian{WGS84Latest}.(tuples)) + r = Ring(points) + @test datum(Meshes.crs(centroid(r))) === WGS84Latest + ri = Ring(point.([(1, 1), (2, 2), (3, 3)])) ro = Rope(point.([(1, 1), (2, 2), (3, 3)])) @test sprint(show, ri) == "Ring((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" @@ -391,6 +403,13 @@ t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0)) + c3 = Cartesian{WGS84Latest}(T(0), T(1)) + t = Triangle(Point(c1), Point(c2), Point(c3)) + @test datum(Meshes.crs(t(T(0), T(0)))) === WGS84Latest + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" if T === Float32 @@ -458,6 +477,14 @@ @test q(T(1), T(1)) == point(1, 1, 0) @test q(T(0), T(1)) == point(0, 1, 1) + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0)) + c3 = Cartesian{WGS84Latest}(T(1), T(1)) + c4 = Cartesian{WGS84Latest}(T(0), T(1)) + q = Quadrangle(Point(c1), Point(c2), Point(c3), Point(c4)) + @test datum(Meshes.crs(q(T(0), T(0)))) === WGS84Latest + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" if T === Float32 @@ -719,6 +746,14 @@ @test embeddim(t) == 3 @test Meshes.lentype(t) === Meshes.Met{Float64} + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) + c3 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) + c4 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) + @test datum(Meshes.crs(t(T(0), T(0), T(0)))) === WGS84Latest + t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @@ -821,6 +856,18 @@ @test embeddim(h) == 3 @test Meshes.lentype(h) === Meshes.Met{Float64} + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) + c3 = Cartesian{WGS84Latest}(T(1), T(1), T(0)) + c4 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) + c5 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + c6 = Cartesian{WGS84Latest}(T(1), T(0), T(1)) + c7 = Cartesian{WGS84Latest}(T(1), T(1), T(1)) + c8 = Cartesian{WGS84Latest}(T(0), T(1), T(1)) + h = Hexahedron(Point(c1), Point(c2), Point(c3), Point(c4), Point(c5), Point(c6), Point(c7), Point(c8)) + @test datum(Meshes.crs(h(T(0), T(0), T(0)))) === WGS84Latest + h = Hexahedron( point(0, 0, 0), point(1, 0, 0), diff --git a/test/primitives.jl b/test/primitives.jl index 5856a68fc..35014cddc 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -158,6 +158,11 @@ @test p ∉ q @test q ∉ p + # datum propagation + c = Cartesian{WGS84Latest}(T(1), T(1)) + @test datum(Meshes.crs(Point(c) + vector(1, 1))) === WGS84Latest + @test datum(Meshes.crs(Point(c) - vector(1, 1))) === WGS84Latest + p = point(0, 1) @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" if T === Float32 @@ -374,6 +379,13 @@ @test embeddim(b2) == 2 @test embeddim(b3) == 3 + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(0.5), T(1)) + c3 = Cartesian{WGS84Latest}(T(1), T(1)) + b = BezierCurve(Point(c1), Point(c2), Point(c3)) + @test datum(Meshes.crs(b(T(0), Horner()))) === WGS84Latest + b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" if T === Float32 @@ -506,6 +518,12 @@ point(0, 1, 1) ) + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(1)) + b = Box(Point(c1), Point(c2)) + @test datum(Meshes.crs(center(b))) === WGS84Latest + b = Box(point(0, 0), point(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" if T === Float32 @@ -812,6 +830,13 @@ @test c isa Circle @test embeddim(c) == 3 + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(4), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) + c3 = Cartesian{WGS84Latest}(T(0), T(0), T(4)) + c = Circle(Point(c1), Point(c2), Point(c3)) + @test datum(Meshes.crs(c)) === WGS84Latest + p = Plane(point(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @test sprint(show, c) == @@ -952,6 +977,12 @@ @test c isa CylinderSurface @test embeddim(c) == 3 + # datum propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + c = CylinderSurface(Point(c1), Point(c2), T(1)) + @test datum(Meshes.crs(center(c))) === WGS84Latest + c = CylinderSurface(T(1)) @test sprint(show, c) == "CylinderSurface(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" diff --git a/test/refinement.jl b/test/refinement.jl index 52a3f36b5..6d68909e9 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -11,6 +11,12 @@ viz(fig[1, 3], ref2, showsegments=true) @test_reference "data/trirefine-$T.png" fig end + + # datum propagation + c = Cartesian{WGS84Latest}(T(0), T(0)) + grid = CartesianGrid((3, 3), Point(c), (T(1), T(1))) + ref = refine(grid, TriRefinement()) + @test datum(Meshes.crs(ref)) === WGS84Latest end @testset "QuadRefinement" begin @@ -28,6 +34,14 @@ viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/quadrefine-$T.png" fig end + + # datum propagation + tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.25, 0.25)), T.((0.75, 0.25)), T.((0.5, 0.75))] + points = Point.(Cartesian{WGS84Latest}.(tuples)) + connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, QuadRefinement()) + @test datum(Meshes.crs(ref)) === WGS84Latest end @testset "RegularRefinement" begin @@ -99,6 +113,14 @@ viz(fig[1, 3], ref3, showsegments=true) @test_reference "data/catmullclark-3-$T.png" fig end + + # datum propagation + tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] + points = Point.(Cartesian{WGS84Latest}.(tuples)) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, CatmullClark()) + @test datum(Meshes.crs(ref)) === WGS84Latest end @testset "TriSubdivision" begin diff --git a/test/sets.jl b/test/sets.jl index 5254aae97..85d65cc47 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -86,6 +86,12 @@ pset2 = PointSet(p for p in points) @test pset1 == pset2 + # datum propagation + tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1))] + points = Point.(Cartesian{WGS84Latest}.(tuples)) + pset = PointSet(points) + @test datum(Meshes.crs(centroid(pset))) === WGS84Latest + pset = PointSet(point.([(1, 0), (0, 1)])) @test sprint(show, pset) == "2 PointSet" if T == Float32 diff --git a/test/transforms.jl b/test/transforms.jl index d66873066..c4aa6c8b4 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -221,6 +221,12 @@ r, c = TB.apply(f, v) @test r ≈ vector(0, 1) @test TB.revert(f, r, c) ≈ v + + # datum propagation + f = Rotate(Angle2d(T(π / 2))) + c = Cartesian{WGS84Latest}(T(1), T(0)) + p = Point(c) + @test datum(Meshes.crs(f(p))) === WGS84Latest end @testset "Translate" begin @@ -596,6 +602,12 @@ @test f.A isa SMatrix @test f.b isa SVector + # datum propagation + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + c = Cartesian{WGS84Latest}(T(1), T(0)) + p = Point(c) + @test datum(Meshes.crs(f(p))) === WGS84Latest + # error: A must be a square matrix @test_throws ArgumentError Affine(T[1 1; 2 2; 3 3], T[1, 2]) # error: A and b must have the same dimension From 56924c0a5cb4377bf816d309b6ba1ceb952e731c Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:34:46 -0300 Subject: [PATCH 083/423] Fix 'Cone'/'ConeSurface' constructor (#870) --- src/primitives/cone.jl | 5 ++++- src/primitives/conesurface.jl | 5 ++++- test/primitives.jl | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 41006b847..5aa805f5f 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -15,7 +15,10 @@ struct Cone{C<:CRS,D<:Disk{C}} <: Primitive{3,C} apex::Point{3,C} end -Cone(base::Disk{C}, apex::Tuple) where {C<:Cartesian} = Cone(base, Point(C(apex))) +function Cone(base::Disk{C}, apex::Tuple) where {C<:Cartesian} + coords = convert(C, Cartesian{datum(C)}(apex)) + Cone(base, Point(coords)) +end paramdim(::Type{<:Cone}) = 3 diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 7c0a5ac44..0e51a0e8f 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -15,7 +15,10 @@ struct ConeSurface{C<:CRS,D<:Disk{C}} <: Primitive{3,C} apex::Point{3,C} end -ConeSurface(base::Disk{C}, apex::Tuple) where {C<:Cartesian} = ConeSurface(base, Point(C(apex))) +function ConeSurface(base::Disk{C}, apex::Tuple) where {C<:Cartesian} + coords = convert(C, Cartesian{datum(C)}(apex)) + ConeSurface(base, Point(coords)) +end paramdim(::Type{<:ConeSurface}) = 2 diff --git a/test/primitives.jl b/test/primitives.jl index 35014cddc..f7ae267ce 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1081,6 +1081,15 @@ @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = T.((0, 0, 1)) + c = Cone(d, a) + @test embeddim(c) == 3 + @test paramdim(c) == 3 + @test Meshes.crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + c = rand(Cone) @test c isa Cone @test embeddim(c) == 3 @@ -1143,6 +1152,15 @@ @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = T.((0, 0, 1)) + c = ConeSurface(d, a) + @test embeddim(c) == 3 + @test paramdim(c) == 2 + @test Meshes.crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + c = rand(ConeSurface) @test c isa ConeSurface @test embeddim(c) == 3 From 97f06052de1e47512300fd3219e4d7129107e768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 6 Jun 2024 11:26:09 -0300 Subject: [PATCH 084/423] Add Coboundary{0,D} for GridTopology --- src/toporelations/coboundary.jl | 47 +++- test/toporelations.jl | 371 ++++++++++++++++++-------------- 2 files changed, 251 insertions(+), 167 deletions(-) diff --git a/src/toporelations/coboundary.jl b/src/toporelations/coboundary.jl index 9409f71fc..6e5014e21 100644 --- a/src/toporelations/coboundary.jl +++ b/src/toporelations/coboundary.jl @@ -21,18 +21,52 @@ function Coboundary{P,Q}(topology) where {P,Q} Coboundary{P,Q,D,T}(topology) end +# -------------- +# GRID TOPOLOGY +# -------------- + +# elements sharing vertex in grid +function (𝒞::Coboundary{0,D,D,T})(ind::Integer) where {D,T<:GridTopology} + topo = 𝒞.topology + dims = size(topo) + cycl = isperiodic(topo) + cind = corner2cart(topo, ind) + + # offsets along each dimension + offsets = CartesianIndices(ntuple(i -> -1:0, D)) + + ninds = NTuple{D,Int}[] + for offset in offsets + # apply offset to center index + sind = cind .+ Tuple(offset) + + # wrap indices in case of periodic dimension + wrap(i) = mod1(sind[i], dims[i]) + wind = ntuple(i -> cycl[i] ? wrap(i) : sind[i], D) + + # discard invalid indices + valid(i) = 1 ≤ wind[i] ≤ dims[i] + all(valid, 1:D) && push!(ninds, wind) + end + + # return linear index of element + [cart2elem(topo, ind...) for ind in ninds] +end + # ------------------- # HALF-EDGE TOPOLOGY # ------------------- -function (𝒞::Coboundary{0,1,2,T})(vert::Integer) where {T<:HalfEdgeTopology} +# segments sharing a vertex in 2D mesh +function (𝒞::Coboundary{0,1,2,T})(ind::Integer) where {T<:HalfEdgeTopology} t = 𝒞.topology 𝒜 = Adjacency{0}(t) - [edge4pair(t, (vert, other)) for other in 𝒜(vert)] + [edge4pair(t, (ind, other)) for other in 𝒜(ind)] end -function (𝒞::Coboundary{0,2,2,T})(vert::Integer) where {T<:HalfEdgeTopology} - e = half4vert(𝒞.topology, vert) +# elements sharing a vertex in 2D mesh +function (𝒞::Coboundary{0,2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} + e = half4vert(𝒞.topology, ind) # initialize result elements = [e.elem] @@ -60,7 +94,8 @@ function (𝒞::Coboundary{0,2,2,T})(vert::Integer) where {T<:HalfEdgeTopology} elements end -function (𝒞::Coboundary{1,2,2,T})(edge::Integer) where {T<:HalfEdgeTopology} - e = half4edge(𝒞.topology, edge) +# elements sharing a segment in 2D mesh +function (𝒞::Coboundary{1,2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} + e = half4edge(𝒞.topology, ind) isnothing(e.half.elem) ? [e.elem] : [e.elem, e.half.elem] end diff --git a/test/toporelations.jl b/test/toporelations.jl index 4ab9b8751..9054800ec 100644 --- a/test/toporelations.jl +++ b/test/toporelations.jl @@ -1,13 +1,27 @@ @testset "TopologicalRelation" begin @testset "GridTopology" begin - # 3 segments + # 3 grid t = GridTopology(3) ∂ = Boundary{1,0}(t) @test ∂(1) == [1, 2] @test ∂(2) == [2, 3] @test ∂(3) == [3, 4] + 𝒞 = Coboundary{0,1}(t) + @test 𝒞(1) == [1] + @test 𝒞(2) == [1, 2] + @test 𝒞(3) == [2, 3] + @test 𝒞(4) == [3] + 𝒜 = Adjacency{1}(t) + @test 𝒜(1) == [2] + @test 𝒜(2) == [1, 3] + @test 𝒜(3) == [2] + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == [2] + @test 𝒜(2) == [1, 3] + @test 𝒜(3) == [2, 4] + @test 𝒜(4) == [3] - # quadrangles in 2D grid + # 2x3 grid t = GridTopology(2, 3) ∂ = Boundary{2,0}(t) @test ∂(1) == [1, 2, 5, 4] @@ -16,9 +30,19 @@ @test ∂(4) == [5, 6, 9, 8] @test ∂(5) == [7, 8, 11, 10] @test ∂(6) == [8, 9, 12, 11] - - # segments of quadrangles in 2D grid - t = GridTopology(2, 3) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == [1] + @test 𝒞(2) == [1, 2] + @test 𝒞(3) == [2] + @test 𝒞(4) == [1, 3] + @test 𝒞(5) == [1, 2, 3, 4] + @test 𝒞(6) == [2, 4] + @test 𝒞(7) == [3, 5] + @test 𝒞(8) == [3, 4, 5, 6] + @test 𝒞(9) == [4, 6] + @test 𝒞(10) == [5] + @test 𝒞(11) == [5, 6] + @test 𝒞(12) == [6] ∂ = Boundary{1,0}(t) @test ∂(1) == [1, 4] @test ∂(2) == [2, 5] @@ -37,11 +61,45 @@ @test ∂(15) == [5, 6] @test ∂(16) == [8, 9] @test ∂(17) == [11, 12] + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == [2, 3] + @test 𝒜(2) == [1, 4] + @test 𝒜(3) == [4, 1, 5] + @test 𝒜(4) == [3, 2, 6] + @test 𝒜(5) == [6, 3] + @test 𝒜(6) == [5, 4] - # segments of quadrangles in 2D (periodic) grid + # 2x2 grid + t = GridTopology(2, 2) + ∂ = Boundary{2,0}(t) + @test ∂(1) == [1, 2, 5, 4] + @test ∂(2) == [2, 3, 6, 5] + @test ∂(3) == [4, 5, 8, 7] + @test ∂(4) == [5, 6, 9, 8] + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == [1] + @test 𝒞(2) == [1, 2] + @test 𝒞(3) == [2] + @test 𝒞(4) == [1, 3] + @test 𝒞(5) == [1, 2, 3, 4] + @test 𝒞(6) == [2, 4] + @test 𝒞(7) == [3] + @test 𝒞(8) == [3, 4] + @test 𝒞(9) == [4] + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == [2, 4] + @test 𝒜(2) == [1, 3, 5] + @test 𝒜(3) == [2, 6] + @test 𝒜(4) == [5, 1, 7] + @test 𝒜(5) == [4, 6, 2, 8] + @test 𝒜(6) == [5, 3, 9] + @test 𝒜(7) == [8, 4] + @test 𝒜(8) == [7, 9, 5] + @test 𝒜(9) == [8, 6] + + # 2x2 (periodic x aperiodic) grid t = GridTopology((2, 2), (true, false)) ∂ = Boundary{1,0}(t) - @test nfacets(t) == 10 @test ∂(1) == [1, 3] @test ∂(2) == [2, 4] @test ∂(3) == [3, 5] @@ -52,11 +110,17 @@ @test ∂(8) == [2, 1] @test ∂(9) == [4, 3] @test ∂(10) == [6, 5] + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == [2, 1] + @test 𝒞(2) == [1, 2] + @test 𝒞(3) == [2, 1, 4, 3] + @test 𝒞(4) == [1, 2, 3, 4] + @test 𝒞(5) == [4, 3] + @test 𝒞(6) == [3, 4] - # segments of quadrangles in 2D (periodic) grid + # 2x2 (aperiodic x periodic) grid t = GridTopology((2, 2), (false, true)) ∂ = Boundary{1,0}(t) - @test nfacets(t) == 10 @test ∂(1) == [1, 4] @test ∂(2) == [2, 5] @test ∂(3) == [3, 6] @@ -67,11 +131,17 @@ @test ∂(8) == [4, 5] @test ∂(9) == [2, 3] @test ∂(10) == [5, 6] - - # segments of quadrangles in 2D (periodic) grid + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == [3, 1] + @test 𝒞(2) == [3, 4, 1, 2] + @test 𝒞(3) == [4, 2] + @test 𝒞(4) == [1, 3] + @test 𝒞(5) == [1, 2, 3, 4] + @test 𝒞(6) == [2, 4] + + # 2x2 (periodic x periodic) grid t = GridTopology((2, 2), (true, true)) ∂ = Boundary{1,0}(t) - @test nfacets(t) == 8 @test ∂(1) == [1, 3] @test ∂(2) == [2, 4] @test ∂(3) == [3, 1] @@ -80,8 +150,116 @@ @test ∂(6) == [3, 4] @test ∂(7) == [2, 1] @test ∂(8) == [4, 3] + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == [4, 3, 2, 1] + @test 𝒞(2) == [3, 4, 1, 2] + @test 𝒞(3) == [2, 1, 4, 3] + @test 𝒞(4) == [1, 2, 3, 4] + + # 3x3 grid + t = GridTopology(3, 3) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == [2, 4] + @test 𝒜(2) == [1, 3, 5] + @test 𝒜(3) == [2, 6] + @test 𝒜(4) == [5, 1, 7] + @test 𝒜(5) == [4, 6, 2, 8] + @test 𝒜(6) == [5, 3, 9] + @test 𝒜(7) == [8, 4] + @test 𝒜(8) == [7, 9, 5] + @test 𝒜(9) == [8, 6] + + # 3x3 grid (periodic x aperiodic) grid + t = GridTopology((3, 3), (true, false)) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == [3, 2, 4] + @test 𝒜(2) == [1, 3, 5] + @test 𝒜(3) == [2, 1, 6] + @test 𝒜(4) == [6, 5, 1, 7] + @test 𝒜(5) == [4, 6, 2, 8] + @test 𝒜(6) == [5, 4, 3, 9] + @test 𝒜(7) == [9, 8, 4] + @test 𝒜(8) == [7, 9, 5] + @test 𝒜(9) == [8, 7, 6] + + # 3x3 grid (periodic x periodic) grid + t = GridTopology((3, 3), (true, true)) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == [3, 2, 7, 4] + @test 𝒜(2) == [1, 3, 8, 5] + @test 𝒜(3) == [2, 1, 9, 6] + @test 𝒜(4) == [6, 5, 1, 7] + @test 𝒜(5) == [4, 6, 2, 8] + @test 𝒜(6) == [5, 4, 3, 9] + @test 𝒜(7) == [9, 8, 4, 1] + @test 𝒜(8) == [7, 9, 5, 2] + @test 𝒜(9) == [8, 7, 6, 3] + + # 3x4 grid + t = GridTopology(3, 4) + ∂ = Boundary{2,1}(t) + @test ∂(1) == [1, 2, 17, 18] + @test ∂(2) == [2, 3, 22, 23] + @test ∂(3) == [3, 4, 27, 28] + @test ∂(4) == [5, 6, 18, 19] + @test ∂(5) == [6, 7, 23, 24] + @test ∂(6) == [7, 8, 28, 29] + @test ∂(7) == [9, 10, 19, 20] + @test ∂(8) == [10, 11, 24, 25] + @test ∂(9) == [11, 12, 29, 30] + @test ∂(10) == [13, 14, 20, 21] + @test ∂(11) == [14, 15, 25, 26] + @test ∂(12) == [15, 16, 30, 31] + + # 3x4 (periodic x aperiodic) grid + t = GridTopology((3, 4), (true, false)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == [1, 2, 13, 14] + @test ∂(2) == [2, 3, 18, 19] + @test ∂(3) == [3, 1, 23, 24] + @test ∂(4) == [4, 5, 14, 15] + @test ∂(5) == [5, 6, 19, 20] + @test ∂(6) == [6, 4, 24, 25] + @test ∂(7) == [7, 8, 15, 16] + @test ∂(8) == [8, 9, 20, 21] + @test ∂(9) == [9, 7, 25, 26] + @test ∂(10) == [10, 11, 16, 17] + @test ∂(11) == [11, 12, 21, 22] + @test ∂(12) == [12, 10, 26, 27] + + # 3x4 (aperiodic x periodic) grid + t = GridTopology((3, 4), (false, true)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == [1, 2, 17, 18] + @test ∂(2) == [2, 3, 21, 22] + @test ∂(3) == [3, 4, 25, 26] + @test ∂(4) == [5, 6, 18, 19] + @test ∂(5) == [6, 7, 22, 23] + @test ∂(6) == [7, 8, 26, 27] + @test ∂(7) == [9, 10, 19, 20] + @test ∂(8) == [10, 11, 23, 24] + @test ∂(9) == [11, 12, 27, 28] + @test ∂(10) == [13, 14, 20, 17] + @test ∂(11) == [14, 15, 24, 21] + @test ∂(12) == [15, 16, 28, 25] + + # 3x4 (periodic x periodic) grid + t = GridTopology((3, 4), (true, true)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == [1, 2, 13, 14] + @test ∂(2) == [2, 3, 17, 18] + @test ∂(3) == [3, 1, 21, 22] + @test ∂(4) == [4, 5, 14, 15] + @test ∂(5) == [5, 6, 18, 19] + @test ∂(6) == [6, 4, 22, 23] + @test ∂(7) == [7, 8, 15, 16] + @test ∂(8) == [8, 9, 19, 20] + @test ∂(9) == [9, 7, 23, 24] + @test ∂(10) == [10, 11, 16, 13] + @test ∂(11) == [11, 12, 20, 17] + @test ∂(12) == [12, 10, 24, 21] - # quadrangles of hexahedrons in 3D grid + # 2x2x2 grid t = GridTopology(2, 2, 2) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 13, 14, 25, 26] @@ -92,8 +270,17 @@ @test ∂(6) == [8, 9, 22, 23, 29, 30] @test ∂(7) == [10, 11, 20, 21, 32, 33] @test ∂(8) == [11, 12, 23, 24, 35, 36] + 𝒜 = Adjacency{3}(t) + @test 𝒜(1) == [2, 3, 5] + @test 𝒜(2) == [1, 4, 6] + @test 𝒜(3) == [4, 1, 7] + @test 𝒜(4) == [3, 2, 8] + @test 𝒜(5) == [6, 7, 1] + @test 𝒜(6) == [5, 8, 2] + @test 𝒜(7) == [8, 5, 3] + @test 𝒜(8) == [7, 6, 4] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (periodic x aperiodic x aperiodic) grid t = GridTopology((2, 2, 2), (true, false, false)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 9, 10, 21, 22] @@ -105,7 +292,7 @@ @test ∂(7) == [7, 8, 16, 17, 28, 29] @test ∂(8) == [8, 7, 19, 20, 31, 32] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (aperiodic x periodic x aperiodic) grid t = GridTopology((2, 2, 2), (false, true, false)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 13, 14, 21, 22] @@ -117,7 +304,7 @@ @test ∂(7) == [10, 11, 18, 17, 28, 29] @test ∂(8) == [11, 12, 20, 19, 31, 32] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (aperiodic x aperiodic x periodic) grid t = GridTopology((2, 2, 2), (false, false, true)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 13, 14, 25, 26] @@ -129,7 +316,7 @@ @test ∂(7) == [10, 11, 20, 21, 30, 29] @test ∂(8) == [11, 12, 23, 24, 32, 31] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (periodic x periodic x aperiodic) grid t = GridTopology((2, 2, 2), (true, true, false)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 9, 10, 17, 18] @@ -141,7 +328,7 @@ @test ∂(7) == [7, 8, 14, 13, 24, 25] @test ∂(8) == [8, 7, 16, 15, 27, 28] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (periodic x aperiodic x periodic) grid t = GridTopology((2, 2, 2), (true, false, true)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 9, 10, 21, 22] @@ -153,7 +340,7 @@ @test ∂(7) == [7, 8, 16, 17, 26, 25] @test ∂(8) == [8, 7, 19, 20, 28, 27] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (aperiodic x periodic x periodic) grid t = GridTopology((2, 2, 2), (false, true, true)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 13, 14, 21, 22] @@ -165,7 +352,7 @@ @test ∂(7) == [10, 11, 18, 17, 26, 25] @test ∂(8) == [11, 12, 20, 19, 28, 27] - # quadrangles of hexahedrons in 3D (periodic) grid + # 2x2x2 (periodic x periodic x periodic) grid t = GridTopology((2, 2, 2), (true, true, true)) ∂ = Boundary{3,2}(t) @test ∂(1) == [1, 2, 9, 10, 17, 18] @@ -177,71 +364,7 @@ @test ∂(7) == [7, 8, 14, 13, 22, 21] @test ∂(8) == [8, 7, 16, 15, 24, 23] - # edges of quadrangles in 2D grid - t = GridTopology(3, 4) - ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 17, 18] - @test ∂(2) == [2, 3, 22, 23] - @test ∂(3) == [3, 4, 27, 28] - @test ∂(4) == [5, 6, 18, 19] - @test ∂(5) == [6, 7, 23, 24] - @test ∂(6) == [7, 8, 28, 29] - @test ∂(7) == [9, 10, 19, 20] - @test ∂(8) == [10, 11, 24, 25] - @test ∂(9) == [11, 12, 29, 30] - @test ∂(10) == [13, 14, 20, 21] - @test ∂(11) == [14, 15, 25, 26] - @test ∂(12) == [15, 16, 30, 31] - - # edges of quadrangles in 2D (periodic) grid - t = GridTopology((3, 4), (true, false)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 13, 14] - @test ∂(2) == [2, 3, 18, 19] - @test ∂(3) == [3, 1, 23, 24] - @test ∂(4) == [4, 5, 14, 15] - @test ∂(5) == [5, 6, 19, 20] - @test ∂(6) == [6, 4, 24, 25] - @test ∂(7) == [7, 8, 15, 16] - @test ∂(8) == [8, 9, 20, 21] - @test ∂(9) == [9, 7, 25, 26] - @test ∂(10) == [10, 11, 16, 17] - @test ∂(11) == [11, 12, 21, 22] - @test ∂(12) == [12, 10, 26, 27] - - # edges of quadrangles in 2D (periodic) grid - t = GridTopology((3, 4), (false, true)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 17, 18] - @test ∂(2) == [2, 3, 21, 22] - @test ∂(3) == [3, 4, 25, 26] - @test ∂(4) == [5, 6, 18, 19] - @test ∂(5) == [6, 7, 22, 23] - @test ∂(6) == [7, 8, 26, 27] - @test ∂(7) == [9, 10, 19, 20] - @test ∂(8) == [10, 11, 23, 24] - @test ∂(9) == [11, 12, 27, 28] - @test ∂(10) == [13, 14, 20, 17] - @test ∂(11) == [14, 15, 24, 21] - @test ∂(12) == [15, 16, 28, 25] - - # edges of quadrangles in 2D (periodic) grid - t = GridTopology((3, 4), (true, true)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 13, 14] - @test ∂(2) == [2, 3, 17, 18] - @test ∂(3) == [3, 1, 21, 22] - @test ∂(4) == [4, 5, 14, 15] - @test ∂(5) == [5, 6, 18, 19] - @test ∂(6) == [6, 4, 22, 23] - @test ∂(7) == [7, 8, 15, 16] - @test ∂(8) == [8, 9, 19, 20] - @test ∂(9) == [9, 7, 23, 24] - @test ∂(10) == [10, 11, 16, 13] - @test ∂(11) == [11, 12, 20, 17] - @test ∂(12) == [12, 10, 24, 21] - - # 2x3x2 hexahedrons + # 2x3x2 grid t = GridTopology(2, 3, 2) ∂ = Boundary{3,0}(t) @test ∂(1) == [1, 2, 5, 4, 13, 14, 17, 16] @@ -249,68 +372,7 @@ @test ∂(3) == [4, 5, 8, 7, 16, 17, 20, 19] @test ∂(12) == [20, 21, 24, 23, 32, 33, 36, 35] - # quadrangles in 2D grid - t = GridTopology(2, 3) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [2, 3] - @test 𝒜(2) == [1, 4] - @test 𝒜(3) == [4, 1, 5] - @test 𝒜(4) == [3, 2, 6] - @test 𝒜(5) == [6, 3] - @test 𝒜(6) == [5, 4] - - # quadrangles in 2D grid - t = GridTopology(3, 3) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 6] - @test 𝒜(4) == [5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 3, 9] - @test 𝒜(7) == [8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 6] - - # quadrangles in 2D grid with periodicity - t = GridTopology((3, 3), (true, false)) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [3, 2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 1, 6] - @test 𝒜(4) == [6, 5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 4, 3, 9] - @test 𝒜(7) == [9, 8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 7, 6] - - # quadrangles in 2D grid with periodicity - t = GridTopology((3, 3), (true, true)) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [3, 2, 7, 4] - @test 𝒜(2) == [1, 3, 8, 5] - @test 𝒜(3) == [2, 1, 9, 6] - @test 𝒜(4) == [6, 5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 4, 3, 9] - @test 𝒜(7) == [9, 8, 4, 1] - @test 𝒜(8) == [7, 9, 5, 2] - @test 𝒜(9) == [8, 7, 6, 3] - - # quadrangles in 3D grid - t = GridTopology(2, 2, 2) - 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == [2, 3, 5] - @test 𝒜(2) == [1, 4, 6] - @test 𝒜(3) == [4, 1, 7] - @test 𝒜(4) == [3, 2, 8] - @test 𝒜(5) == [6, 7, 1] - @test 𝒜(6) == [5, 8, 2] - @test 𝒜(7) == [8, 5, 3] - @test 𝒜(8) == [7, 6, 4] - - # quadrangles in 3D grid + # 3x2x2 grid t = GridTopology(3, 2, 2) 𝒜 = Adjacency{3}(t) @test 𝒜(1) == [2, 4, 7] @@ -326,7 +388,7 @@ @test 𝒜(11) == [10, 12, 8, 5] @test 𝒜(12) == [11, 9, 6] - # quadrangles in 3D grid with periodicity + # 3x2x2 (periodic x aperiodic x aperiodic) grid t = GridTopology((3, 2, 2), (true, false, false)) 𝒜 = Adjacency{3}(t) @test 𝒜(1) == [3, 2, 4, 7] @@ -342,19 +404,6 @@ @test 𝒜(11) == [10, 12, 8, 5] @test 𝒜(12) == [11, 10, 9, 6] - # vertices in 2D grid - t = GridTopology(2, 2) - 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == [2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 6] - @test 𝒜(4) == [5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 3, 9] - @test 𝒜(7) == [8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 6] - # invalid relations t = GridTopology(2, 3) @test_throws AssertionError Boundary{3,0}(t) From 9c7e2189c3429e6bee2a217c885f6dbd66046fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 6 Jun 2024 11:58:08 -0300 Subject: [PATCH 085/423] More tests fo Coboundary --- test/toporelations.jl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/toporelations.jl b/test/toporelations.jl index 9054800ec..f27169846 100644 --- a/test/toporelations.jl +++ b/test/toporelations.jl @@ -270,6 +270,34 @@ @test ∂(6) == [8, 9, 22, 23, 29, 30] @test ∂(7) == [10, 11, 20, 21, 32, 33] @test ∂(8) == [11, 12, 23, 24, 35, 36] + 𝒞 = Coboundary{0,3}(t) + @test 𝒞(1) == [1] + @test 𝒞(2) == [1, 2] + @test 𝒞(3) == [2] + @test 𝒞(4) == [1, 3] + @test 𝒞(5) == [1, 2, 3, 4] + @test 𝒞(6) == [2, 4] + @test 𝒞(7) == [3] + @test 𝒞(8) == [3, 4] + @test 𝒞(9) == [4] + @test 𝒞(10) == [1, 5] + @test 𝒞(11) == [1, 2, 5, 6] + @test 𝒞(12) == [2, 6] + @test 𝒞(13) == [1, 3, 5, 7] + @test 𝒞(14) == [1, 2, 3, 4, 5, 6, 7, 8] + @test 𝒞(15) == [2, 4, 6, 8] + @test 𝒞(16) == [3, 7] + @test 𝒞(17) == [3, 4, 7, 8] + @test 𝒞(18) == [4, 8] + @test 𝒞(19) == [5] + @test 𝒞(20) == [5, 6] + @test 𝒞(21) == [6] + @test 𝒞(22) == [5, 7] + @test 𝒞(23) == [5, 6, 7, 8] + @test 𝒞(24) == [6, 8] + @test 𝒞(25) == [7] + @test 𝒞(26) == [7, 8] + @test 𝒞(27) == [8] 𝒜 = Adjacency{3}(t) @test 𝒜(1) == [2, 3, 5] @test 𝒜(2) == [1, 4, 6] From 8d418a345c9a5b6252dc4ddfa31128e69f359565 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:53:42 -0300 Subject: [PATCH 086/423] Update to Makie.jl v0.21 (#871) --- Project.toml | 2 +- ext/grid.jl | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 1dc20fa34..1a41b7b2b 100644 --- a/Project.toml +++ b/Project.toml @@ -33,7 +33,7 @@ Colorfy = "0.1" CoordRefSystems = "0.7" Distances = "0.10" LinearAlgebra = "1.9" -Makie = "0.20" +Makie = "0.21" NearestNeighbors = "0.4" Random = "1.9" Rotations = "1.5.1" diff --git a/ext/grid.jl b/ext/grid.jl index 7e7752521..d8ed0847a 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -29,20 +29,6 @@ function vizgrid3D!(plot) end end -# defining a Makie.data_limits method is necessary because -# Makie.scale!, Makie.translate! and Makie.rotate! -# don't adjust axis limits automatically -function Makie.data_limits(plot::Viz{<:Tuple{Grid}}) - grid = plot[:object][] - bbox = boundingbox(grid) - pmin = aspoint3f(minimum(bbox)) - pmax = aspoint3f(maximum(bbox)) - Makie.Rect3f([pmin, pmax]) -end - -aspoint3f(p::Point{2}) = Makie.Point3f(ustrip.(to(p))..., 0) -aspoint3f(p::Point{3}) = Makie.Point3f(ustrip.(to(p))...) - # ---------------- # SPECIALIZATIONS # ---------------- From 801dee366802b603fd012555b6d5c61ac457a819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 6 Jun 2024 19:43:29 -0300 Subject: [PATCH 087/423] Fix #310: Implement circular equality --- src/Meshes.jl | 1 + src/polytopes.jl | 15 +++++++++++++++ src/polytopes/ring.jl | 13 +++++++++++++ test/polytopes.jl | 12 ++++++++++++ 4 files changed, 41 insertions(+) diff --git a/src/Meshes.jl b/src/Meshes.jl index b0cb7f549..9dff5d095 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -205,6 +205,7 @@ export angles, innerangles, normal, + ≗, # multi-geometries Multi, diff --git a/src/polytopes.jl b/src/polytopes.jl index 2778bc91a..bae5b0b3b 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -161,6 +161,21 @@ See also [`Ngon`](@ref) and [`PolyArea`](@ref). """ const Polygon = Polytope{2} +""" + ≗(polygon₁, polygon₂) + +Tells whether or not the `polygon₁` and `polygon₂` +are equal up to circular shifts. +""" +function ≗(p₁::Polygon, p₂::Polygon) + rings₁ = rings(p₁) + rings₂ = rings(p₂) + nring₁ = length(rings₁) + nring₂ = length(rings₂) + nring₁ == nring₂ || return false + all(r₁ ≗ r₂ for (r₁, r₂) in zip(rings₁, rings₂)) +end + """ rings(polygon) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 48b66fd31..fbb944dab 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -34,6 +34,19 @@ nvertices(r::Ring) = length(r.vertices) ==(r₁::Ring, r₂::Ring) = r₁.vertices == r₂.vertices +""" + ≗(ring₁, ring₂) + +Tells whether or not the `ring₁` and `ring₂` +are equal up to circular shifts. +""" +function ≗(r₁::Ring, r₂::Ring) + n = length(r₁.vertices) + i = findfirst(==(first(r₁.vertices)), r₂.vertices) + isnothing(i) && return false + r₁.vertices == r₂.vertices[i:(i+n-1)] +end + function Base.isapprox(r₁::Ring, r₂::Ring; kwargs...) nvertices(r₁) ≠ nvertices(r₂) && return false all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(r₁.vertices, r₂.vertices)) diff --git a/test/polytopes.jl b/test/polytopes.jl index 92c26ba71..f19d2ad98 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -101,6 +101,12 @@ c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 + # circular equality + c1 = Ring(point.([(1, 1), (2, 2), (3, 3)])) + c2 = Ring(point.([(2, 2), (3, 3), (1, 1)])) + c3 = Ring(point.([(3, 3), (1, 1), (2, 2)])) + @test c1 ≗ c2 ≗ c3 + c = Rope(point.([(1, 1), (2, 2)])) @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @@ -368,6 +374,12 @@ @test Point(0.5f0, 0.5f0) ∈ t @test Point(0.5e0, 0.5e0) ∈ t + # circular equality + t1 = Triangle(T.((1, 1)), T.((2, 2)), T.((3, 3))) + t2 = Triangle(T.((2, 2)), T.((3, 3)), T.((1, 1))) + t3 = Triangle(T.((3, 3)), T.((1, 1)), T.((2, 2))) + @test t1 ≗ t2 ≗ t3 + # point at edge of triangle @test point(3, 1) ∈ Triangle(point(1, 1), point(5, 1), point(3, 3)) From 098cb91d20b86ace065ac0698bbff5e46e21c12c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 08:03:13 -0300 Subject: [PATCH 088/423] :robot: Format .jl files (#872) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/polytopes/ring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index fbb944dab..2956cd456 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -44,7 +44,7 @@ function ≗(r₁::Ring, r₂::Ring) n = length(r₁.vertices) i = findfirst(==(first(r₁.vertices)), r₂.vertices) isnothing(i) && return false - r₁.vertices == r₂.vertices[i:(i+n-1)] + r₁.vertices == r₂.vertices[i:(i + n - 1)] end function Base.isapprox(r₁::Ring, r₂::Ring; kwargs...) From f9976fafeda88dbdd5ca26ebe26a499164fd2693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 7 Jun 2024 09:42:35 -0300 Subject: [PATCH 089/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1a41b7b2b..f20e7c7cb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.43.5" +version = "0.44.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From d640d60bdea4f4d0dde0cbb1e60eb62bff2d42b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 10 Jun 2024 10:13:33 -0300 Subject: [PATCH 090/423] Add DelaunayTesselation method --- Project.toml | 2 ++ docs/src/algorithms/tesselation.md | 27 +++++++++++++++++++++++++++ src/Meshes.jl | 18 +++++++++++++++--- src/tesselation.jl | 28 ++++++++++++++++++++++++++++ src/tesselation/delaunay.jl | 28 ++++++++++++++++++++++++++++ test/runtests.jl | 1 + test/tesselation.jl | 9 +++++++++ 7 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 docs/src/algorithms/tesselation.md create mode 100644 src/tesselation.jl create mode 100644 src/tesselation/delaunay.jl create mode 100644 test/tesselation.jl diff --git a/Project.toml b/Project.toml index f20e7c7cb..59e8cad67 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" CircularArrays = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" Colorfy = "03fe91ce-8ec6-4610-8e8d-e7491ccca690" CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" +DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" @@ -31,6 +32,7 @@ Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" CoordRefSystems = "0.7" +DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" Makie = "0.21" diff --git a/docs/src/algorithms/tesselation.md b/docs/src/algorithms/tesselation.md new file mode 100644 index 000000000..2eebed432 --- /dev/null +++ b/docs/src/algorithms/tesselation.md @@ -0,0 +1,27 @@ +# Tesselation + +```@example tesselation +using Meshes # hide +import CairoMakie as Mke # hide +``` + +```@docs +tesselate +TessselationMethod +``` + +## DelaunayTesselation + +```@docs +DelaunayTesselation +``` + +```@example tesselation +points = rand(Point{2}, 100) + +mesh = tesselate(points, DelaunayTesselation()) + +viz(mesh, showsegments = true) +viz!(points, color = :red) +Mke.current_figure() +``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 9dff5d095..481cdc774 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -15,9 +15,15 @@ using Random using Bessels: gamma using Unitful: AbstractQuantity, numtype using StatsBase: AbstractWeights, Weights, quantile -using Distances: PreMetric, Euclidean, Haversine, Mahalanobis, SphericalAngle, evaluate, result_type -using Rotations: Rotation, QuatRotation, Angle2d, rotation_between -using NearestNeighbors: KDTree, BallTree, knn, inrange +using Distances: PreMetric, Euclidean, Mahalanobis +using Distances: Haversine, SphericalAngle +using Distances: evaluate, result_type +using Rotations: Rotation, QuatRotation, Angle2d +using Rotations: rotation_between +using NearestNeighbors: KDTree, BallTree +using NearestNeighbors: knn, inrange +using DelaunayTriangulation: triangulate +using DelaunayTriangulation: each_solid_triangle using Transducers: Filter, Map, TakeWhile, tcollect, ⨟ using Base.Cartesian: @nloops, @nref, @ntuple using Base: @propagate_inbounds @@ -101,6 +107,7 @@ include("boundingboxes.jl") include("hulls.jl") include("sampling.jl") include("pointification.jl") +include("tesselation.jl") include("discretization.jl") include("refinement.jl") include("coarsening.jl") @@ -463,6 +470,11 @@ export # pointification pointify, + # tesselation + TesselationMethod, + DelaunayTesselation, + tesselate, + # discretization DiscretizationMethod, BoundaryDiscretizationMethod, diff --git a/src/tesselation.jl b/src/tesselation.jl new file mode 100644 index 000000000..45ce5b932 --- /dev/null +++ b/src/tesselation.jl @@ -0,0 +1,28 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + DiscretizationMethod + +A method for tesselating point sets into meshes. +""" +abstract type TesselationMethod end + +""" + tesselate(pointset, [method]) + +Tesselate `pointset` with tesselation `method`. + +If the `method` is ommitted, a default algorithm is used. +""" +function tesselate end + +tesselate(points::AbstractVector{<:Point{2}}, method::TesselationMethod) = + tesselate(PointSet(points), method) + +# ---------------- +# IMPLEMENTATIONS +# ---------------- + +include("tesselation/delaunay.jl") diff --git a/src/tesselation/delaunay.jl b/src/tesselation/delaunay.jl new file mode 100644 index 000000000..a538475ff --- /dev/null +++ b/src/tesselation/delaunay.jl @@ -0,0 +1,28 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + DelaunayTesselation([rng]) + +Unconstrained Delaunay tesselation of point sets. +Optionally, specify the random number generator `rng`. + +## References + +* Cheng et al. 2012. [Delaunay Mesh Generation] + (https://people.eecs.berkeley.edu/~jrs/meshbook.html) +""" +struct DelaunayTesselation{RNG<:AbstractRNG} <: TesselationMethod + rng::RNG +end + +DelaunayTesselation(rng=Random.default_rng()) = DelaunayTesselation(rng) + +function tesselate(pset::PointSet{2}, method::DelaunayTesselation) + points = collect(pset) + coords = map(p -> ustrip.(to(p)), points) + triang = triangulate(coords, rng=method.rng) + connec = connect.(each_solid_triangle(triang)) + SimpleMesh(points, connec) +end diff --git a/test/runtests.jl b/test/runtests.jl index 62e721bdc..710066dce 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -125,6 +125,7 @@ testfiles = [ "hulls.jl", "sampling.jl", "pointification.jl", + "tesselation.jl", "discretization.jl", "refinement.jl", "coarsening.jl", diff --git a/test/tesselation.jl b/test/tesselation.jl new file mode 100644 index 000000000..d0a914101 --- /dev/null +++ b/test/tesselation.jl @@ -0,0 +1,9 @@ +@testset "Tesselation" begin + @testset "Delaunay" begin + pts = randpoint2(100) + pset = PointSet(pts) + mesh1 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + mesh2 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) + @test mesh1 == mesh2 + end +end From b648b82f2d07c439ba08edaff82f36a7054a9119 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:28:56 -0300 Subject: [PATCH 091/423] :robot: Format .jl files (#873) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/tesselation.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tesselation.jl b/src/tesselation.jl index 45ce5b932..a1e7288d5 100644 --- a/src/tesselation.jl +++ b/src/tesselation.jl @@ -18,8 +18,7 @@ If the `method` is ommitted, a default algorithm is used. """ function tesselate end -tesselate(points::AbstractVector{<:Point{2}}, method::TesselationMethod) = - tesselate(PointSet(points), method) +tesselate(points::AbstractVector{<:Point{2}}, method::TesselationMethod) = tesselate(PointSet(points), method) # ---------------- # IMPLEMENTATIONS From 400c1a0fa25ba8933de1e61ad18e89d82814b261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 10 Jun 2024 15:25:01 -0300 Subject: [PATCH 092/423] Rename Dehn1899 --> DehnTriangulation --- docs/src/algorithms/discretization.md | 50 +++++++++++++++++---------- src/Meshes.jl | 4 +-- src/discretization.jl | 6 ++-- src/discretization/dehn.jl | 6 ++-- test/discretization.jl | 10 +++--- test/predicates.jl | 4 +-- 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index 57e5f15a5..5a7c0cfec 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -31,26 +31,10 @@ viz(fig[1,2], mesh, showsegments = true) fig ``` -## RegularDiscretization - -```@docs -RegularDiscretization -``` - -```@example discretization -sphere = Sphere((0.,0.,0.), 1.) - -mesh = discretize(sphere, RegularDiscretization(10,10)) - -fig = Mke.Figure(size = (400, 400)) -viz(fig[1,1], mesh, showsegments = true) -fig -``` - -## Dehn1899 +## DehnTriangulation ```@docs -Dehn1899 +DehnTriangulation ``` ```@example discretization @@ -96,7 +80,7 @@ polyarea = PolyArea([(0.22926679, 0.47329807), (0.23094065, 0.44913536), (0.2569 (0.37951034, 0.31436795), (0.37547874, 0.30905423), (0.36070493, 0.3204269), (0.33518887, 0.348486), (0.29893062, 0.3932315), (0.25193012, 0.45466346)]) -mesh = discretize(polyarea, Dehn1899()) +mesh = discretize(polyarea, DehnTriangulation()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) @@ -144,3 +128,31 @@ viz(fig[1,1], polyarea) viz(fig[1,2], mesh, showsegments = true) fig ``` + +## RegularDiscretization + +```@docs +RegularDiscretization +``` + +```@example discretization +sphere = Sphere((0.,0.,0.), 1.) + +mesh = discretize(sphere, RegularDiscretization(10,10)) + +viz(mesh, showsegments = true) +``` + +## Tetrahedralization + +```@docs +Tetrahedralization +``` + +```@example discretization +box = Box((0., 0., 0.), (1., 1., 1.)) + +mesh = discretize(box, Tetrahedralization()) + +viz(mesh, colors = 1:nelements(mesh)) +``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 481cdc774..4e8d9c7cf 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -479,10 +479,10 @@ export DiscretizationMethod, BoundaryDiscretizationMethod, FanTriangulation, - RegularDiscretization, + DehnTriangulation, FIST, - Dehn1899, Tetrahedralization, + RegularDiscretization, discretize, discretizewithin, simplexify, diff --git a/src/discretization.jl b/src/discretization.jl index 8026133a2..96f188ae3 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -170,7 +170,7 @@ simplexify(box::Box{2}) = discretize(box, FanTriangulation()) simplexify(box::Box{3}) = discretize(box, Tetrahedralization()) -simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? FIST() : Dehn1899()) +simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? FIST() : DehnTriangulation()) simplexify(poly::Polyhedron) = discretize(poly, Tetrahedralization()) @@ -216,7 +216,7 @@ end # ---------------- include("discretization/fan.jl") -include("discretization/regular.jl") -include("discretization/fist.jl") include("discretization/dehn.jl") +include("discretization/fist.jl") include("discretization/tetra.jl") +include("discretization/regular.jl") diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index 1b860c95f..f646b53a7 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - Dehn1899() + DehnTriangulation() Max Dehns' triangulation proved in 1899. @@ -19,9 +19,9 @@ with small number of vertices. * Devadoss, S & Rourke, J. 2011. [Discrete and computational geometry] (https://press.princeton.edu/books/hardcover/9780691145532/discrete-and-computational-geometry) """ -struct Dehn1899 <: BoundaryDiscretizationMethod end +struct DehnTriangulation <: BoundaryDiscretizationMethod end -function discretizewithin(ring::Ring{2}, ::Dehn1899) +function discretizewithin(ring::Ring{2}, ::DehnTriangulation) # points on resulting mesh points = collect(vertices(ring)) diff --git a/test/discretization.jl b/test/discretization.jl index ec5c48dae..45f534b77 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -92,7 +92,7 @@ @test all(intersects(poly), mesh) end - @testset "Dehn1899" begin + @testset "DehnTriangulation" begin octa = Octagon( point(0.2, 0.2), point(0.5, -0.5), @@ -103,7 +103,7 @@ point(0.2, 0.8), point(-0.5, 0.5) ) - mesh = discretize(octa, Dehn1899()) + mesh = discretize(octa, DehnTriangulation()) @test nvertices(mesh) == 8 @test nelements(mesh) == 6 @test eltype(mesh) <: Triangle @@ -118,7 +118,7 @@ point(0.2, 0.8, 0.0), point(-0.5, 0.5, 0.0) ) - mesh = discretize(octa, Dehn1899()) + mesh = discretize(octa, DehnTriangulation()) @test nvertices(mesh) == 8 @test nelements(mesh) == 6 @test eltype(mesh) <: Triangle @@ -210,7 +210,7 @@ @testset "Miscellaneous" begin rng = StableRNG(123) - for method in [FIST(rng), Dehn1899()] + for method in [FIST(rng), DehnTriangulation()] triangle = Triangle(point(0, 0), point(1, 0), point(0, 1)) mesh = discretize(triangle, method) @test vertices(mesh) == [point(0, 0), point(1, 0), point(0, 1)] @@ -261,7 +261,7 @@ @testset "Difficult examples" begin rng = StableRNG(123) - for method in [FIST(rng), Dehn1899()] + for method in [FIST(rng), DehnTriangulation()] poly = readpoly(T, joinpath(datadir, "taubin.line")) mesh = discretize(poly, method) @test Set(vertices(poly)) == Set(vertices(mesh)) diff --git a/test/predicates.jl b/test/predicates.jl index 250d7f383..a18a8c300 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -409,8 +409,8 @@ @test !intersects(poly2, ball3) @test intersects(poly1, ball4) @test intersects(poly2, ball4) - mesh1 = discretize(poly1, Dehn1899()) - mesh2 = discretize(poly2, Dehn1899()) + mesh1 = discretize(poly1, DehnTriangulation()) + mesh2 = discretize(poly2, DehnTriangulation()) @test intersects(mesh1, mesh1) @test intersects(mesh2, mesh2) @test intersects(mesh1, mesh2) From dbddf3f82b422dbbe8689d19e10ed06b22af0859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 10 Jun 2024 15:51:35 -0300 Subject: [PATCH 093/423] Rename FIST --> HeldTriangulation --- docs/src/algorithms/discretization.md | 8 ++++---- src/Meshes.jl | 2 +- src/discretization.jl | 4 ++-- src/discretization/{fist.jl => held.jl} | 8 ++++---- test/discretization.jl | 12 ++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) rename src/discretization/{fist.jl => held.jl} (95%) diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index 5a7c0cfec..5aac4fbee 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -88,14 +88,14 @@ viz(fig[1,2], mesh, showsegments = true) fig ``` -## FIST +## HeldTriangulation ```@docs -FIST +HeldTriangulation ``` ```@example discretization -mesh = discretize(polyarea, FIST()) +mesh = discretize(polyarea, HeldTriangulation()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) @@ -121,7 +121,7 @@ inners = [[(0.87789994, 0.32551613), (0.5614043, 0.540334), (0.9494598, 0.396227 polyarea = PolyArea([outer, inners...]) -mesh = discretize(polyarea, FIST()) +mesh = discretize(polyarea, HeldTriangulation()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) diff --git a/src/Meshes.jl b/src/Meshes.jl index 4e8d9c7cf..2f102d41a 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -480,7 +480,7 @@ export BoundaryDiscretizationMethod, FanTriangulation, DehnTriangulation, - FIST, + HeldTriangulation, Tetrahedralization, RegularDiscretization, discretize, diff --git a/src/discretization.jl b/src/discretization.jl index 96f188ae3..199678694 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -170,7 +170,7 @@ simplexify(box::Box{2}) = discretize(box, FanTriangulation()) simplexify(box::Box{3}) = discretize(box, Tetrahedralization()) -simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? FIST() : DehnTriangulation()) +simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? HeldTriangulation() : DehnTriangulation()) simplexify(poly::Polyhedron) = discretize(poly, Tetrahedralization()) @@ -217,6 +217,6 @@ end include("discretization/fan.jl") include("discretization/dehn.jl") -include("discretization/fist.jl") +include("discretization/held.jl") include("discretization/tetra.jl") include("discretization/regular.jl") diff --git a/src/discretization/fist.jl b/src/discretization/held.jl similarity index 95% rename from src/discretization/fist.jl rename to src/discretization/held.jl index caf888e9d..89d2b5220 100644 --- a/src/discretization/fist.jl +++ b/src/discretization/held.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - FIST([rng]; shuffle=true) + HeldTriangulation([rng]; shuffle=true) Fast Industrial-Strength Triangulation (FIST) of polygons. @@ -26,14 +26,14 @@ generator `rng`. constrained Delaunay triangulation of polygons] (https://www.sciencedirect.com/science/article/pii/S092577211830004X) """ -struct FIST{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod +struct HeldTriangulation{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod rng::RNG shuffle::Bool end -FIST(rng=Random.default_rng(); shuffle=true) = FIST(rng, shuffle) +HeldTriangulation(rng=Random.default_rng(); shuffle=true) = HeldTriangulation(rng, shuffle) -function discretizewithin(ring::Ring{2}, method::FIST) +function discretizewithin(ring::Ring{2}, method::HeldTriangulation) # helper function to shuffle ears earshuffle!(𝒬) = method.shuffle && shuffle!(method.rng, 𝒬) diff --git a/test/discretization.jl b/test/discretization.jl index 45f534b77..d429bbab1 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -124,7 +124,7 @@ @test eltype(mesh) <: Triangle end - @testset "FIST" begin + @testset "HeldTriangulation" begin 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4, 5] @@ -161,7 +161,7 @@ connec = connect.([(4, 5, 6), (3, 4, 6), (3, 6, 1), (1, 2, 3)], Triangle) target = SimpleMesh(points, connec) poly = PolyArea(points) - mesh = discretize(poly, FIST(shuffle=false)) + mesh = discretize(poly, HeldTriangulation(shuffle=false)) @test mesh == target @test Set(vertices(poly)) == Set(vertices(mesh)) @test nelements(mesh) == length(vertices(mesh)) - 2 @@ -188,7 +188,7 @@ ]) ) rng = StableRNG(123) - mesh = discretize(poly, FIST(rng)) + mesh = discretize(poly, HeldTriangulation(rng)) @test nvertices(mesh) == 16 @test nelements(mesh) == 14 @@ -203,14 +203,14 @@ ]) ) rng = StableRNG(123) - mesh = discretize(poly, FIST(rng)) + mesh = discretize(poly, HeldTriangulation(rng)) @test nvertices(mesh) == 5 @test nelements(mesh) == 3 end @testset "Miscellaneous" begin rng = StableRNG(123) - for method in [FIST(rng), DehnTriangulation()] + for method in [DehnTriangulation(), HeldTriangulation(rng)] triangle = Triangle(point(0, 0), point(1, 0), point(0, 1)) mesh = discretize(triangle, method) @test vertices(mesh) == [point(0, 0), point(1, 0), point(0, 1)] @@ -261,7 +261,7 @@ @testset "Difficult examples" begin rng = StableRNG(123) - for method in [FIST(rng), DehnTriangulation()] + for method in [DehnTriangulation(), HeldTriangulation(rng)] poly = readpoly(T, joinpath(datadir, "taubin.line")) mesh = discretize(poly, method) @test Set(vertices(poly)) == Set(vertices(mesh)) From 4a0d813a58ecf2451a2b4e9d6fa682a4b5a122ae Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 10 Jun 2024 17:56:41 -0300 Subject: [PATCH 094/423] Add `Proj` transform (#874) * Add 'Proj' transform * Add tests * Apply suggestions * Add more tests * Fix 'applycoord' fallback * Apply suggestions --- src/Meshes.jl | 3 +- src/transforms.jl | 11 +++- src/transforms/proj.jl | 35 ++++++++++ src/utils.jl | 7 ++ test/transforms.jl | 144 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 src/transforms/proj.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 2f102d41a..a36871181 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -506,10 +506,11 @@ export CoordinateTransform, Rotate, Translate, - Affine, Scale, + Affine, Stretch, StdCoords, + Proj, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index fda5e8873..6da3d9c58 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -63,8 +63,12 @@ apply(t::CoordinateTransform, g::GeometryOrDomain) = applycoord(t, g), nothing revert(t::CoordinateTransform, g::GeometryOrDomain, c) = applycoord(inverse(t), g) # apply transform recursively -applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} = - G((applycoord(t, getfield(g, n)) for n in fieldnames(G))...) +@generated function applycoord(t::CoordinateTransform, g::G) where {G<:GeometryOrDomain} + ctor = constructor(G) + names = fieldnames(G) + exprs = (:(applycoord(t, g.$name)) for name in names) + :($ctor($(exprs...))) +end # stop recursion at non-geometric types applycoord(::CoordinateTransform, x) = x @@ -85,12 +89,13 @@ applycoord(t::CoordinateTransform, g::CircularVector{<:Geometry}) = # IMPLEMENTATIONS # ---------------- -include("transforms/scale.jl") include("transforms/rotate.jl") include("transforms/translate.jl") +include("transforms/scale.jl") include("transforms/affine.jl") include("transforms/stretch.jl") include("transforms/stdcoords.jl") +include("transforms/proj.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl new file mode 100644 index 000000000..8caa21c16 --- /dev/null +++ b/src/transforms/proj.jl @@ -0,0 +1,35 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + Proj(CRS) + +Convert the coordinates of geometry or domain to a given +coordinate reference system `CRS`. + +## Examples + +```julia +Proj(Polar) +Proj(WebMercator) +Proj(Mercator{WGS84Latest}) +``` +""" +struct Proj{CRS} <: CoordinateTransform end + +Proj(CRS) = Proj{CRS}() + +parameters(::Proj{CRS}) where {CRS} = (; CRS) + +applycoord(::Proj, v::Vec) = v + +applycoord(::Proj{CRS}, p::Point) where {CRS} = Point(convert(CRS, coords(p))) + +# -------------- +# SPECIAL CASES +# -------------- + +applycoord(t::Proj, g::RectilinearGrid) = applycoord(t, convert(SimpleMesh, g)) + +applycoord(t::Proj, g::StructuredGrid) = applycoord(t, convert(SimpleMesh, g)) diff --git a/src/utils.jl b/src/utils.jl index d2120b634..cabb76fbe 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -12,6 +12,13 @@ Throws an `AssertionError(msg)` if `cond` is `false`. """ assertion(cond, msg) = cond || throw(AssertionError(msg)) +""" + constructor(G) + +Given a (parametric) type `G{T₁,T₂,...}`, return the type `G`. +""" +constructor(G::Type{<:GeometryOrDomain}) = getfield(Meshes, nameof(G)) + """ fitdims(dims, D) diff --git a/test/transforms.jl b/test/transforms.jl index c4aa6c8b4..bdc3e97c7 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -954,6 +954,150 @@ @test r == r2 end + @testset "Proj" begin + @test !isaffine(Proj(Polar)) + @test !TB.isrevertible(Proj(Polar)) + @test !TB.isinvertible(Proj(Polar)) + @test TB.parameters(Proj(Polar)) == (; CRS=Polar) + + # ---- + # VEC + # ---- + + f = Proj(Polar) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r == v + + # ------ + # POINT + # ------ + + f = Proj(Polar) + g = point(1, 1) + r, c = TB.apply(f, g) + @test r ≈ Point(Polar(T(√2), T(π / 4))) + + # -------- + # SEGMENT + # -------- + + f = Proj(Polar) + g = Segment(point(0, 0), point(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Segment(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # ---- + # BOX + # ---- + + f = Proj(Polar) + g = Box(point(0, 0), point(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # --------- + # TRIANGLE + # --------- + + f = Proj(Polar) + g = Triangle(point(0, 0), point(1, 0), point(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # ---------- + # MULTIGEOM + # ---------- + + f = Proj(Polar) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + + # ------ + # PLANE + # ------ + + f = Proj(Cylindrical) + g = Plane(point(1, 1, 1), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Plane(Point(Cylindrical(T(√2), T(π / 4), T(1))), vector(0, 0, 1)) + + # --------- + # CYLINDER + # --------- + + f = Proj(Cylindrical) + g = Cylinder(point(0, 0, 0), point(1, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Cylinder(Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(√2), T(π / 4), T(1)))) + + # --------- + # POINTSET + # --------- + + f = Proj(Polar) + d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Proj(Polar) + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Proj(Polar) + d = CartesianGrid((10, 10), point(1, 1), T.((1, 1))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid((10, 10), Point(Polar(T(√2), T(π / 4))), T.((1, 1))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Proj(Polar) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa SimpleMesh + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Proj(Polar) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa SimpleMesh + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Proj(Polar) + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + end + @testset "Repair{0}" begin @test !isaffine(Repair) poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) From ec485e57d91b945c55e6891e61c0697c7dd2fece Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 11 Jun 2024 09:39:30 -0300 Subject: [PATCH 095/423] Add missing methods and tests for `RectilinearGrid` and `StructuredGrid` in Coordinate Transforms (#875) * Add missing methods and tests for 'RectilinearGrid' and 'StructuredGrid' in Coordinate Transforms * Apply suggestions --- src/transforms/scale.jl | 6 ++++++ test/transforms.jl | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index bac7c88b6..55d004122 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -51,3 +51,9 @@ function applycoord(t::Scale, g::CartesianGrid) offs = offset(g) CartesianGrid(dims, orig, spac, offs) end + +applycoord(t::Scale{Dim}, g::RectilinearGrid{Datum,Dim}) where {Datum,Dim} = + RectilinearGrid{Datum}(ntuple(i -> t.factors[i] * xyz(g)[i], Dim)) + +applycoord(t::Scale{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} = + StructuredGrid{Datum}(ntuple(i -> t.factors[i] * XYZ(g)[i], Dim)) diff --git a/test/transforms.jl b/test/transforms.jl index bdc3e97c7..2ef174c66 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -753,6 +753,28 @@ @test r ≈ CartesianGrid(point(1, 2), point(11, 22), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Scale(T(1), T(2)) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Scale(T(1), T(2)) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # ----------- # SIMPLEMESH # ----------- @@ -905,6 +927,28 @@ @test r ≈ CartesianGrid(point(1, -4), point(11, 16), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Stretch(T(1), T(2)) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Stretch(T(1), T(2)) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # ----------- # SIMPLEMESH # ----------- From 397e743725c6453f8a6f2b4f4881cc411de8919f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:24:20 -0300 Subject: [PATCH 096/423] Add support for EPSG/ESRI codes in 'Proj' transform (#876) --- src/transforms/proj.jl | 9 ++++++++- test/transforms.jl | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 8caa21c16..9cccc6a25 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -4,9 +4,10 @@ """ Proj(CRS) + Proj(code) Convert the coordinates of geometry or domain to a given -coordinate reference system `CRS`. +coordinate reference system `CRS` or EPSG/ESRI `code`. ## Examples @@ -14,12 +15,18 @@ coordinate reference system `CRS`. Proj(Polar) Proj(WebMercator) Proj(Mercator{WGS84Latest}) +Proj(EPSG{3395}) +Proj(ESRI{54017}) ``` """ struct Proj{CRS} <: CoordinateTransform end Proj(CRS) = Proj{CRS}() +Proj(code::Type{<:EPSG}) = Proj{CoordRefSystems.get(code)}() + +Proj(code::Type{<:ESRI}) = Proj{CoordRefSystems.get(code)}() + parameters(::Proj{CRS}) where {CRS} = (; CRS) applycoord(::Proj, v::Vec) = v diff --git a/test/transforms.jl b/test/transforms.jl index 2ef174c66..174adf346 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1003,6 +1003,8 @@ @test !TB.isrevertible(Proj(Polar)) @test !TB.isinvertible(Proj(Polar)) @test TB.parameters(Proj(Polar)) == (; CRS=Polar) + @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) + @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) # ---- # VEC From 6026f724ecdd50b72386a7466d86dccb2ba0a424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 11 Jun 2024 10:58:02 -0300 Subject: [PATCH 097/423] Add DelaunayTriangulation --- src/Meshes.jl | 1 + src/discretization.jl | 1 + src/discretization/delaunay.jl | 29 ++++++ test/discretization.jl | 179 +++++++++++++++++---------------- 4 files changed, 125 insertions(+), 85 deletions(-) create mode 100644 src/discretization/delaunay.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index a36871181..b4a88b5fb 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -481,6 +481,7 @@ export FanTriangulation, DehnTriangulation, HeldTriangulation, + DelaunayTriangulation, Tetrahedralization, RegularDiscretization, discretize, diff --git a/src/discretization.jl b/src/discretization.jl index 199678694..ec50dd83b 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -218,5 +218,6 @@ end include("discretization/fan.jl") include("discretization/dehn.jl") include("discretization/held.jl") +include("discretization/delaunay.jl") include("discretization/tetra.jl") include("discretization/regular.jl") diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl new file mode 100644 index 000000000..ec71b4053 --- /dev/null +++ b/src/discretization/delaunay.jl @@ -0,0 +1,29 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + DelaunayTriangulation() + +Constrained Delaunay triangulation of polygons. +Optionally, specify the random number generator `rng`. + +## References + +* Cheng et al. 2012. [Delaunay Mesh Generation] + (https://people.eecs.berkeley.edu/~jrs/meshbook.html) +""" +struct DelaunayTriangulation{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod + rng::RNG +end + +DelaunayTriangulation(rng=Random.default_rng()) = DelaunayTriangulation(rng) + +function discretizewithin(ring::Ring{2}, method::DelaunayTriangulation) + points = vertices(ring) + coords = map(p -> ustrip.(to(p)), points) + bnodes = [1:nvertices(ring); 1] + triang = triangulate(coords, boundary_nodes=bnodes, rng=method.rng) + connec = connect.(each_solid_triangle(triang)) + SimpleMesh(points, connec) +end diff --git a/test/discretization.jl b/test/discretization.jl index d429bbab1..da42c469f 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -11,87 +11,6 @@ @test collect(elements(mesh)) == tris end - @testset "RegularDiscretization" begin - bezier = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) - mesh = discretize(bezier, RegularDiscretization(10)) - @test nvertices(mesh) == 11 - @test nelements(mesh) == 10 - @test eltype(mesh) <: Segment - @test nvertices.(mesh) ⊆ [2] - - box = Box(point(0, 0), point(2, 2)) - mesh = discretize(box, RegularDiscretization(10)) - @test mesh isa CartesianGrid - @test nvertices(mesh) == 121 - @test nelements(mesh) == 100 - @test eltype(mesh) <: Quadrangle - @test nvertices.(mesh) ⊆ [4] - - sphere = Sphere(point(0, 0), T(1)) - mesh = discretize(sphere, RegularDiscretization(10)) - @test nvertices(mesh) == 10 - @test nelements(mesh) == 10 - @test eltype(mesh) <: Segment - @test nvertices.(mesh) ⊆ [2] - - sphere = Sphere(point(0, 0, 0), T(1)) - mesh = discretize(sphere, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - ellips = Ellipsoid((T(3), T(2), T(1))) - mesh = discretize(ellips, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - ball = Ball(point(0, 0), T(1)) - mesh = discretize(ball, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 1 - @test nelements(mesh) == 10 * 10 + 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - disk = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)) - mesh = discretize(disk, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 1 - @test nelements(mesh) == 10 * 10 + 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - cylsurf = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) - mesh = discretize(cylsurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * 11 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - consurf = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) - mesh = discretize(consurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * 11 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - parsurf = rand(ParaboloidSurface) - mesh = discretize(parsurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * (10 + 1) - @test nelements(mesh) == 10 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - poly = PolyArea(point.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) - mesh = discretize(poly, RegularDiscretization(50)) - @test mesh isa SubGrid{2} - grid = parent(mesh) - @test grid isa CartesianGrid - @test eltype(mesh) <: Quadrangle - @test all(intersects(poly), mesh) - end - @testset "DehnTriangulation" begin octa = Octagon( point(0.2, 0.2), @@ -208,13 +127,22 @@ @test nelements(mesh) == 3 end - @testset "Miscellaneous" begin + @testset "DelaunayTriangulation" begin rng = StableRNG(123) - for method in [DehnTriangulation(), HeldTriangulation(rng)] + poly = Pentagon(point(0, 0), point(1, 0), point(1, 1), point(0.5, 2), point(0, 1)) + mesh = discretize(poly, DelaunayTriangulation(rng)) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 + end + + @testset "Miscellaneous triangulations" begin + rng = StableRNG(123) + for method in [DehnTriangulation(), HeldTriangulation(rng), DelaunayTriangulation(rng)] triangle = Triangle(point(0, 0), point(1, 0), point(0, 1)) mesh = discretize(triangle, method) @test vertices(mesh) == [point(0, 0), point(1, 0), point(0, 1)] - @test collect(elements(mesh)) == [triangle] + @test nelements(mesh) == 1 + @test mesh[1] ≗ triangle quadrangle = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) mesh = discretize(quadrangle, method) @@ -259,7 +187,7 @@ end end - @testset "Difficult examples" begin + @testset "Difficult triangulations" begin rng = StableRNG(123) for method in [DehnTriangulation(), HeldTriangulation(rng)] poly = readpoly(T, joinpath(datadir, "taubin.line")) @@ -381,6 +309,87 @@ @test nelements(bmesh) == 5 end + @testset "RegularDiscretization" begin + bezier = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) + mesh = discretize(bezier, RegularDiscretization(10)) + @test nvertices(mesh) == 11 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + box = Box(point(0, 0), point(2, 2)) + mesh = discretize(box, RegularDiscretization(10)) + @test mesh isa CartesianGrid + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + + sphere = Sphere(point(0, 0), T(1)) + mesh = discretize(sphere, RegularDiscretization(10)) + @test nvertices(mesh) == 10 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + sphere = Sphere(point(0, 0, 0), T(1)) + mesh = discretize(sphere, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + ellips = Ellipsoid((T(3), T(2), T(1))) + mesh = discretize(ellips, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + ball = Ball(point(0, 0), T(1)) + mesh = discretize(ball, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 1 + @test nelements(mesh) == 10 * 10 + 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + disk = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)) + mesh = discretize(disk, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 1 + @test nelements(mesh) == 10 * 10 + 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + cylsurf = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) + mesh = discretize(cylsurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * 11 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + consurf = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) + mesh = discretize(consurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * 11 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + parsurf = rand(ParaboloidSurface) + mesh = discretize(parsurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * (10 + 1) + @test nelements(mesh) == 10 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + poly = PolyArea(point.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) + mesh = discretize(poly, RegularDiscretization(50)) + @test mesh isa SubGrid{2} + grid = parent(mesh) + @test grid isa CartesianGrid + @test eltype(mesh) <: Quadrangle + @test all(intersects(poly), mesh) + end + @testset "Discretize" begin ball = Ball(point(0, 0), T(1)) mesh = discretize(ball) From 43dfd207a3c4a57fb782b1971123d5a8b530df8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 11 Jun 2024 15:19:02 -0300 Subject: [PATCH 098/423] DelaunayTriangulation in simplexify if n > 5000 --- src/discretization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discretization.jl b/src/discretization.jl index ec50dd83b..5e150f708 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -170,7 +170,7 @@ simplexify(box::Box{2}) = discretize(box, FanTriangulation()) simplexify(box::Box{3}) = discretize(box, Tetrahedralization()) -simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? HeldTriangulation() : DehnTriangulation()) +simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? DelaunayTriangulation() : DehnTriangulation()) simplexify(poly::Polyhedron) = discretize(poly, Tetrahedralization()) From 8dbb81044e55fed06e9edcd439e67ed8e9cb3af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 11 Jun 2024 15:34:55 -0300 Subject: [PATCH 099/423] Add DelaunayTriangulation to docs --- docs/src/algorithms/discretization.md | 17 ++++++++++++++++- docs/src/algorithms/tesselation.md | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index 5aac4fbee..71940e6c8 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -103,6 +103,21 @@ viz(fig[1,2], mesh, showsegments = true) fig ``` +## DelaunayTriangulation + +```@docs +DelaunayTriangulation +``` + +```@example discretization +mesh = discretize(polyarea, DelaunayTriangulation()) + +fig = Mke.Figure(size = (800, 400)) +viz(fig[1,1], polyarea) +viz(fig[1,2], mesh, showsegments = true) +fig +``` + As can be seen in the following example, all discretization methods for [`Polygon`](@ref) automatically work in the presence of holes: @@ -121,7 +136,7 @@ inners = [[(0.87789994, 0.32551613), (0.5614043, 0.540334), (0.9494598, 0.396227 polyarea = PolyArea([outer, inners...]) -mesh = discretize(polyarea, HeldTriangulation()) +mesh = discretize(polyarea, DelaunayTriangulation()) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], polyarea) diff --git a/docs/src/algorithms/tesselation.md b/docs/src/algorithms/tesselation.md index 2eebed432..d8250aa35 100644 --- a/docs/src/algorithms/tesselation.md +++ b/docs/src/algorithms/tesselation.md @@ -7,7 +7,7 @@ import CairoMakie as Mke # hide ```@docs tesselate -TessselationMethod +TesselationMethod ``` ## DelaunayTesselation From c079f271fb409d6e073631ed89b1313e259986cb Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:43:12 -0300 Subject: [PATCH 100/423] Add `LengthUnit` transform (#880) * Add 'LengthUnits' transform * Apply suggestions * Add tests * Apply suggestions --- src/Meshes.jl | 1 + src/transforms.jl | 1 + src/transforms/lengthunit.jl | 60 ++++++++++++++ test/transforms.jl | 153 +++++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 src/transforms/lengthunit.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index b4a88b5fb..98f1443ff 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -512,6 +512,7 @@ export Stretch, StdCoords, Proj, + LengthUnit, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 6da3d9c58..005bfd2d9 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -96,6 +96,7 @@ include("transforms/affine.jl") include("transforms/stretch.jl") include("transforms/stdcoords.jl") include("transforms/proj.jl") +include("transforms/lengthunit.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl new file mode 100644 index 000000000..8654da69c --- /dev/null +++ b/src/transforms/lengthunit.jl @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + LengthUnit(unit) + +Convert the length unit of coordinates of a geometry or domain to `unit`. + +## Examples + +```julia +LengthUnit(u"cm") +LengthUnit(u"km") +``` +""" +struct LengthUnit{U} <: CoordinateTransform + unit::U +end + +parameters(t::LengthUnit) = (; unit=t.unit) + +applycoord(t::LengthUnit, v::Vec) = uconvert.(t.unit, v) + +applycoord(t::LengthUnit, p::Point) = Point(_lenunit(coords(p), t.unit)) + +function _lenunit(c::Cartesian, u) + d = datum(c) + v = CoordRefSystems.cvalues(c) + Cartesian{d}(uconvert.(u, v)) +end + +function _lenunit(c::Polar, u) + d = datum(c) + ρ = uconvert(u, c.ρ) + Polar{d}(ρ, c.ϕ) +end + +function _lenunit(c::Cylindrical, u) + d = datum(c) + ρ = uconvert(u, c.ρ) + z = uconvert(u, c.z) + Cylindrical{d}(ρ, c.ϕ, z) +end + +function _lenunit(c::Spherical, u) + d = datum(c) + r = uconvert(u, c.r) + Spherical{d}(r, c.θ, c.ϕ) +end + +_lenunit(c, _) = throw(ArgumentError("the length unit of $(prettyname(c)) cannot be changed")) + +# -------------- +# SPECIAL CASES +# -------------- + +applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g))) + +applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) diff --git a/test/transforms.jl b/test/transforms.jl index 174adf346..1ff02c0a4 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1144,6 +1144,159 @@ @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) end + @testset "LengthUnit" begin + @test !isaffine(LengthUnit(u"km")) + @test !TB.isrevertible(LengthUnit(u"cm")) + @test !TB.isinvertible(LengthUnit(u"km")) + @test TB.parameters(LengthUnit(u"cm")) == (; unit=u"cm") + + # ---- + # VEC + # ---- + + f = LengthUnit(u"km") + v = vector(1000, 0) + r, c = TB.apply(f, v) + @test r ≈ Vec(T(1) * u"km", T(0) * u"km") + + # ------ + # POINT + # ------ + + f = LengthUnit(u"cm") + g = point(1, 1) + r, c = TB.apply(f, g) + @test r ≈ Point(T(100) * u"cm", T(100) * u"cm") + + f = LengthUnit(u"km") + g = Point(Polar(T(1000), T(π / 4))) + r, c = TB.apply(f, g) + @test r ≈ Point(Polar(T(1) * u"km", T(π / 4) * u"rad")) + + f = LengthUnit(u"cm") + g = Point(Cylindrical(T(1), T(π / 4), T(1))) + r, c = TB.apply(f, g) + @test r ≈ Point(Cylindrical(T(100) * u"cm", T(π / 4) * u"rad", T(100) * u"cm")) + + f = LengthUnit(u"km") + g = Point(Spherical(T(1000), T(π / 4), T(π / 4))) + r, c = TB.apply(f, g) + @test r ≈ Point(Spherical(T(1) * u"km", T(π / 4) * u"rad", T(π / 4) * u"rad")) + + f = LengthUnit(u"cm") + g = Point(Mercator(T(1), T(1))) + @test_throws ArgumentError TB.apply(f, g) + + # -------- + # SEGMENT + # -------- + + f = LengthUnit(u"km") + g = Segment(point(0, 0), point(1000, 1000)) + r, c = TB.apply(f, g) + @test r ≈ Segment(Point(T(0) * u"km", T(0) * u"km"), Point(T(1) * u"km", T(1) * u"km")) + + # ---- + # BOX + # ---- + + f = LengthUnit(u"cm") + g = Box(point(0, 0), point(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(Point(T(0) * u"cm", T(0) * u"cm"), Point(T(100) * u"cm", T(100) * u"cm")) + + # --------- + # TRIANGLE + # --------- + + f = LengthUnit(u"km") + g = Triangle(point(0, 0), point(1000, 0), point(1000, 1000)) + r, c = TB.apply(f, g) + @test r ≈ Triangle( + Point(T(0) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(1) * u"km") + ) + + # ---------- + # MULTIGEOM + # ---------- + + f = LengthUnit(u"cm") + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + + # --------- + # POINTSET + # --------- + + f = LengthUnit(u"km") + d = PointSet([point(0, 0), point(1000, 0), point(1000, 1000)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([ + Point(T(0) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(1) * u"km") + ]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = LengthUnit(u"cm") + t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = LengthUnit(u"km") + d = CartesianGrid((10, 10), point(1000, 1000), T.((1, 1))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), T.((1, 1))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = LengthUnit(u"cm") + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = LengthUnit(u"km") + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ----------- + # SIMPLEMESH + # ----------- + + f = LengthUnit(u"cm") + p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + end + @testset "Repair{0}" begin @test !isaffine(Repair) poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) From 45b76f075d7adcf9e78734d2712faee856d50cc5 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:53:57 -0300 Subject: [PATCH 101/423] Fix `BezierCurve` test (#882) * Fix 'BezierCurve' test * Add rng --- test/primitives.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/primitives.jl b/test/primitives.jl index f7ae267ce..0dbe65c0e 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -366,10 +366,12 @@ @test boundary(b) == Multi([point(0, 0), point(1, 1)]) @test perimeter(b) == zero(ℳ) - b = BezierCurve(point.(randn(100), randn(100))) + rng = StableRNG(123) + b = BezierCurve(point.(randn(rng, 100), randn(rng, 100))) t1 = @timed b(T(0.2)) t2 = @timed b(T(0.2), Horner()) - @test t1.time > t2.time + @test t1.time < 5e-4 + @test t2.time < 5e-4 @test t2.bytes < 100 b2 = rand(BezierCurve{2}) From 6587a4c46c23251c473ac9ca3d1f1417393e4ea5 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:10:15 -0300 Subject: [PATCH 102/423] Fix 'householderbasis' (#883) --- src/utils.jl | 2 +- test/utils.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index cabb76fbe..24116fc28 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -75,7 +75,7 @@ function householderbasis(n::Vec{3,ℒ}) where {ℒ} i = argmax(n .+ n̂) n̂ᵢ = Vec(ntuple(j -> j == i ? n̂ : zero(ℒ), 3)) h = n + n̂ᵢ - H = I - 2h * transpose(h) / (transpose(h) * h) + H = (I - 2h * transpose(h) / (transpose(h) * h)) * unit(ℒ) u, v = [H[:, j] for j in 1:3 if j != i] i == 2 && ((u, v) = (v, u)) Vec(u), Vec(v) diff --git a/test/utils.jl b/test/utils.jl index a1ddde27e..067deb939 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -19,6 +19,14 @@ @test v isa Vec{3} @test ustrip.(u × v) ≈ n ./ norm(n) end + n = Vec(T(1) * u"cm", T(1) * u"cm", T(1) * u"cm") + u, v = householderbasis(n) + @test unit(eltype(u)) == u"cm" + @test unit(eltype(v)) == u"cm" + n = Vec(T(1) * u"km", T(1) * u"km", T(1) * u"km") + u, v = householderbasis(n) + @test unit(eltype(u)) == u"km" + @test unit(eltype(v)) == u"km" @test Meshes.mayberound(1.1, 1.0, 0.2) ≈ 1.0 @test Meshes.mayberound(1.1, 1.0, 0.10000000000000001) ≈ 1.1 From 4f3024204117f2ae72eb44eeaed82db4b1f7f149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 13 Jun 2024 07:27:17 -0300 Subject: [PATCH 103/423] Add Proj and LengthUnit to docs --- docs/Project.toml | 2 ++ docs/src/transforms.md | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index ae8c85bbf..a4a907b4c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,10 +1,12 @@ [deps] CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8" Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" PlyIO = "42171d58-473b-503a-8d5f-782019eb09ec" Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] Documenter = "0.27" diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 938147426..4540ad358 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -50,16 +50,16 @@ viz(fig[1,2], mesh) fig ``` -## Affine +## Scale ```@docs -Affine +Scale ``` ```@example transforms grid = CartesianGrid(10, 10) -mesh = grid |> Affine(Angle2d(π/4), [10., 20.]) +mesh = grid |> Scale(2., 3.) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) @@ -67,16 +67,16 @@ viz(fig[1,2], mesh) fig ``` -## Scale +## Affine ```@docs -Scale +Affine ``` ```@example transforms grid = CartesianGrid(10, 10) -mesh = grid |> Scale(2., 3.) +mesh = grid |> Affine(Angle2d(π/4), [10., 20.]) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) @@ -120,6 +120,36 @@ viz(fig[1,2], mesh) fig ``` +## Proj + +```@docs +Proj +``` + +```@example transforms +# load coordinate reference system +using CoordRefSystems: Polar + +# triangle with Cartesian coordinates +triangle = Triangle((0, 0), (1, 0), (1, 1)) + +# reproject to polar coordinates +triangle |> Proj(Polar) +``` + +## LengthUnit + +```@docs +LengthUnit +``` + +```@example transforms +using Unitful: m, cm + +# convert meters to centimeters +Point(1m, 2m, 3m) |> LengthUnit(cm) +``` + ## Repair ```@docs From 3802f13349ad43d3187d27665587fd3aa36dfc94 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:06:45 -0300 Subject: [PATCH 104/423] Add `Shadow` transform (#885) * Add 'Shadow' transform * Apply suggestions * Add tests * Add comments * Update tests * Apply suggestions * Apply suggestions * Apply suggestions * Apply suggestions --- src/Meshes.jl | 1 + src/transforms.jl | 1 + src/transforms/shadow.jl | 84 +++++++++++++++++++++++++ test/transforms.jl | 130 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 src/transforms/shadow.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 98f1443ff..33d0e586b 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -513,6 +513,7 @@ export StdCoords, Proj, LengthUnit, + Shadow, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 005bfd2d9..63a69bfa5 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -97,6 +97,7 @@ include("transforms/stretch.jl") include("transforms/stdcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") +include("transforms/shadow.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl new file mode 100644 index 000000000..d9679efad --- /dev/null +++ b/src/transforms/shadow.jl @@ -0,0 +1,84 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +function _index(d) + if d == 'x' + 1 + elseif d == 'y' + 2 + elseif d == 'z' + 3 + else + throw(ArgumentError("'$d' isn't a valid dimension name")) + end +end + +""" + Shadow(dims) + +Project the geometry or domain onto the given `dims`, +producing a "shadow" of the original object. + +## Examples + +```julia +Shadow(:xy) +Shadow("xz") +Shadow(1, 2) +Shadow((1, 3)) +``` +""" +struct Shadow{Dim} <: GeometricTransform + dims::Dims{Dim} +end + +Shadow(dims::Int...) = Shadow(dims) + +Shadow(dims::AbstractString) = Shadow(Dims(_index(d) for d in dims)) + +Shadow(dims::Symbol) = Shadow(string(dims)) + +parameters(t::Shadow) = (; dims=t.dims) + +apply(t::Shadow, v::Vec) = _shadow(v, _sort(t.dims)), nothing + +apply(t::Shadow, g::GeometryOrDomain) = _shadow(g, _sort(t.dims)), nothing + +_sort(dims) = sort(SVector(dims)) + +_shadow(v::Vec, dims) = v[dims] + +_shadow(p::Point, dims) = withdatum(p, to(p)[dims]) + +function _shadow(g::CartesianGrid, dims) + sz = size(g)[dims] + or = _shadow(minimum(g), dims) + sp = spacing(g)[dims] + of = offset(g)[dims] + CartesianGrid(sz, or, sp, of) +end + +_shadow(g::RectilinearGrid, dims) = RectilinearGrid{datum(crs(g))}(xyz(g)[dims]) + +function _shadow(g::StructuredGrid, dims) + ndims = length(size(g)) + inds = ntuple(i -> ifelse(i ∈ dims, :, 1), ndims) + StructuredGrid{datum(crs(g))}(map(X -> X[inds...], XYZ(g)[dims])) +end + +# apply shadow transform recursively +@generated function _shadow(g::G, dims) where {G<:GeometryOrDomain} + ctor = constructor(G) + names = fieldnames(G) + exprs = (:(_shadow(g.$name, dims)) for name in names) + :($ctor($(exprs...))) +end + +# stop recursion at non-geometric types +_shadow(x, _) = x + +# special treatment for lists of geometries +_shadow(g::NTuple{<:Any,<:Geometry}, dims) = map(gᵢ -> _shadow(gᵢ, dims), g) +_shadow(g::AbstractVector{<:Geometry}, dims) = tcollect(_shadow(gᵢ, dims) for gᵢ in g) +_shadow(g::CircularVector{<:Geometry}, dims) = CircularVector(tcollect(_shadow(gᵢ, dims) for gᵢ in g)) diff --git a/test/transforms.jl b/test/transforms.jl index 1ff02c0a4..d24500076 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1297,6 +1297,136 @@ @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) end + @testset "Shadow" begin + @test !isaffine(Shadow(:xy)) + @test !TB.isrevertible(Shadow("xy")) + @test !TB.isinvertible(Shadow(:xy)) + @test TB.parameters(Shadow("xy")) == (; dims=(1, 2)) + @test TB.parameters(Shadow(:yx)) == (; dims=(2, 1)) + @test TB.parameters(Shadow("xz")) == (; dims=(1, 3)) + @test TB.parameters(Shadow(:yz)) == (; dims=(2, 3)) + @test_throws ArgumentError Shadow(:xk) + + # ---- + # VEC + # ---- + + f = Shadow(:xy) + v = vector(1, 2, 3) + r, c = TB.apply(f, v) + @test r == vector(1, 2) + + # ------ + # POINT + # ------ + + f = Shadow(:xz) + g = point(1, 2, 3) + r, c = TB.apply(f, g) + @test r == point(1, 3) + + # -------- + # SEGMENT + # -------- + + f = Shadow(:yz) + g = Segment(point(1, 2, 3), point(4, 5, 6)) + r, c = TB.apply(f, g) + @test r == Segment(point(2, 3), point(5, 6)) + + # ---- + # BOX + # ---- + + f = Shadow(:xy) + g = Box(point(1, 2, 3), point(4, 5, 6)) + r, c = TB.apply(f, g) + @test r isa Box + @test r == Box(point(1, 2), point(4, 5)) + + # --------- + # TRIANGLE + # --------- + + f = Shadow(:xz) + g = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + r, c = TB.apply(f, g) + @test r == Triangle(point(1, 3), point(4, 6), point(7, 9)) + + # ---------- + # MULTIGEOM + # ---------- + + f = Shadow(:yz) + t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r == Multi([f(t), f(t)]) + + # --------- + # POINTSET + # --------- + + f = Shadow(:xy) + d = PointSet([point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)]) + r, c = TB.apply(f, d) + @test r == PointSet([point(1, 2), point(4, 5), point(7, 8)]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Shadow(:xz) + t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r == GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .== [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Shadow(:yz) + d = CartesianGrid((10, 11, 12), point(1, 2, 3), T.((1.0, 1.1, 1.2))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r == CartesianGrid((11, 12), point(2, 3), T.((1.1, 1.2))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Shadow(:xy) + d = convert(RectilinearGrid, cartgrid(10, 11, 12)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r == RectilinearGrid(Meshes.xyz(d)[1], Meshes.xyz(d)[2]) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Shadow(:xz) + d = convert(StructuredGrid, cartgrid(10, 11, 12)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r == StructuredGrid(Meshes.XYZ(d)[1][:, 1, :], Meshes.XYZ(d)[3][:, 1, :]) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Shadow(:yz) + p = point.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r == SimpleMesh(f.(vertices(d)), topology(d)) + end + @testset "Repair{0}" begin @test !isaffine(Repair) poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) From e521e9076b9447d5dcd8b9943b4d8f89ad049eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 13 Jun 2024 17:16:13 -0300 Subject: [PATCH 105/423] Move _index in shadow.jl --- src/transforms/shadow.jl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index d9679efad..f1203fa1e 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -2,18 +2,6 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function _index(d) - if d == 'x' - 1 - elseif d == 'y' - 2 - elseif d == 'z' - 3 - else - throw(ArgumentError("'$d' isn't a valid dimension name")) - end -end - """ Shadow(dims) @@ -45,6 +33,18 @@ apply(t::Shadow, v::Vec) = _shadow(v, _sort(t.dims)), nothing apply(t::Shadow, g::GeometryOrDomain) = _shadow(g, _sort(t.dims)), nothing +function _index(d) + if d == 'x' + 1 + elseif d == 'y' + 2 + elseif d == 'z' + 3 + else + throw(ArgumentError("'$d' isn't a valid dimension name")) + end +end + _sort(dims) = sort(SVector(dims)) _shadow(v::Vec, dims) = v[dims] From 93e69c9939fa9b1ab997074b94c13f282118bf3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 08:03:58 -0300 Subject: [PATCH 106/423] Remove compat in docs --- docs/Project.toml | 4 ---- docs/make.jl | 11 ++--------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index a4a907b4c..4e040dcac 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,11 +2,7 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8" Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" PlyIO = "42171d58-473b-503a-8d5f-782019eb09ec" Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[compat] -Documenter = "0.27" diff --git a/docs/make.jl b/docs/make.jl index 83512406c..e35dbcd0b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,13 +1,6 @@ using Documenter, Meshes -using DocumenterTools: Themes -istravis = "TRAVIS" ∈ keys(ENV) - -Themes.compile( - joinpath(@__DIR__, "src/assets/light.scss"), - joinpath(@__DIR__, "src/assets/themes/documenter-light.css") -) -Themes.compile(joinpath(@__DIR__, "src/assets/dark.scss"), joinpath(@__DIR__, "src/assets/themes/documenter-dark.css")) +prettyurls = get(ENV, "CI", nothing) == "true" makedocs( format=Documenter.HTML( @@ -15,7 +8,7 @@ makedocs( "assets/favicon.ico", asset("https://fonts.googleapis.com/css?family=Montserrat|Source+Code+Pro&display=swap", class=:css) ], - prettyurls=istravis, + prettyurls=prettyurls, mathengine=KaTeX( Dict( :macros => Dict( From b7f2a2a808418ef5e373650ded7dd859b5c51ed1 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:07:26 -0300 Subject: [PATCH 107/423] Scale: Add a special case for some Primitives (#889) * Scale: Add special case for 'Sphere' and 'Ellipsoid' * Fix code * Apply suggestions * Apply suggestions * Apply suggestions --- src/primitives/ellipsoid.jl | 7 +- src/primitives/sphere.jl | 2 + src/transforms/scale.jl | 22 ++++++ test/transforms.jl | 146 +++++++++++++++++++++++++++++++----- 4 files changed, 156 insertions(+), 21 deletions(-) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 749a3fbf8..80153d913 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -19,8 +19,10 @@ Ellipsoid(radii::NTuple{3,ℒ}, center::Point{3,C}, rotation::R) where {ℒ<:Len Ellipsoid(radii::NTuple{3}, center::Point{3}, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) +Ellipsoid(radii::NTuple{3}, center::NTuple{3}, rotation) = Ellipsoid(radii, Point(center), rotation) + Ellipsoid(radii::NTuple{3,T}, center=(zero(T), zero(T), zero(T)), rotation=I) where {T} = - Ellipsoid(radii, Point(center), rotation) + Ellipsoid(radii, center, rotation) paramdim(::Type{<:Ellipsoid}) = 2 @@ -30,6 +32,9 @@ center(e::Ellipsoid) = e.center rotation(e::Ellipsoid) = e.rotation +Base.isapprox(e₁::Ellipsoid, e₂::Ellipsoid) = + all(e₁.radii .≈ e₂.radii) && e₁.center ≈ e₂.center && e₁.rotation ≈ e₂.rotation + function (e::Ellipsoid)(θ, φ) T = numtype(lentype(e)) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index b59e999a2..952958680 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -68,6 +68,8 @@ center(s::Sphere) = s.center radius(s::Sphere) = s.radius +Base.isapprox(s₁::Sphere, s₂::Sphere) = s₁.center ≈ s₂.center && s₁.radius ≈ s₂.radius + function (s::Sphere{2})(φ) T = numtype(lentype(s)) if (φ < 0 || φ > 1) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 55d004122..3534c3106 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -44,6 +44,28 @@ applycoord(t::Scale, v::Vec) = t.factors .* v # SPECIAL CASES # -------------- +applycoord(t::Scale, b::Ball) = applycoord(t, discretize(b)) + +applycoord(t::Scale, s::Sphere) = applycoord(t, discretize(s)) + +applycoord(t::Scale{1}, s::Sphere) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) + +applycoord(t::Scale{3}, s::Sphere{3}) = Ellipsoid(t.factors .* radius(s), applycoord(t, center(s))) + +applycoord(t::Scale, e::Ellipsoid) = applycoord(t, discretize(e)) + +applycoord(t::Scale, d::Disk) = applycoord(t, discretize(d)) + +applycoord(t::Scale, c::Circle) = applycoord(t, discretize(c)) + +applycoord(t::Scale, c::Cylinder) = applycoord(t, discretize(c)) + +applycoord(t::Scale, c::CylinderSurface) = applycoord(t, discretize(c)) + +applycoord(t::Scale, p::ParaboloidSurface) = applycoord(t, discretize(p)) + +applycoord(t::Scale, tr::Torus) = applycoord(t, discretize(tr)) + function applycoord(t::Scale, g::CartesianGrid) dims = size(g) orig = applycoord(t, minimum(g)) diff --git a/test/transforms.jl b/test/transforms.jl index d24500076..761ff4015 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -670,6 +670,132 @@ @test r ≈ Box(point(0, 0), point(1, 2)) @test TB.revert(f, r, c) ≈ g + # ----- + # BALL + # ----- + + f = Scale(T(1), T(2)) + g = Ball(point(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + # ------- + # SPHERE + # ------- + + f = Scale(T(1), T(2)) + g = Sphere(point(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + f = Scale(T(1), T(2), T(3)) + g = Sphere(point(1, 2, 3), T(4)) + r, c = TB.apply(f, g) + @test r isa Ellipsoid + @test r ≈ Ellipsoid(T.((4, 8, 12)), point(1, 4, 9)) + m = TB.revert(f, r, c) + @test m isa SimpleMesh + @test m ≈ discretize(g) + + f = Scale(T(2)) + g = Sphere(point(1, 2), T(3)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(point(2, 4), T(6)) + @test TB.revert(f, r, c) ≈ g + + f = Scale(T(2)) + g = Sphere(point(1, 2, 3), T(4)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(point(2, 4, 6), T(8)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # ELLIPSOID + # ---------- + + f = Scale(T(1), T(2), T(3)) + g = Ellipsoid(T.((1, 2, 3))) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + # ----- + # DISK + # ----- + + f = Scale(T(1), T(2), T(3)) + g = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + # ------- + # CIRCLE + # ------- + + f = Scale(T(1), T(2), T(3)) + g = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + # ---------------- + # CYLINDERSURFACE + # ---------------- + + f = Scale(T(1), T(2), T(3)) + g = CylinderSurface(T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + + # ------------------ + # PARABOLOIDSURFACE + # ------------------ + + f = Scale(T(1), T(2), T(3)) + g = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test TB.revert(f, r, c) ≈ m + + # ------ + # TORUS + # ------ + + f = Scale(T(1), T(2), T(3)) + g = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test TB.revert(f, r, c) ≈ m + # --------- # TRIANGLE # --------- @@ -707,16 +833,6 @@ @test r ≈ g @test TB.revert(f, r, c) ≈ g - # --------- - # CYLINDER - # --------- - - f = Scale(T(1), T(1), T(2)) - g = Cylinder(T(1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(point(0, 0, 0), point(0, 0, 2)) - @test TB.revert(f, r, c) ≈ g - # --------- # POINTSET # --------- @@ -881,16 +997,6 @@ @test r ≈ g @test TB.revert(f, r, c) ≈ g - # --------- - # CYLINDER - # --------- - - f = Stretch(T(1), T(1), T(2)) - g = Cylinder(T(1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(point(0, 0, -0.5), point(0, 0, 1.5)) - @test TB.revert(f, r, c) ≈ g - # --------- # POINTSET # --------- From eb044f1a05a204a3eba45cfab68dcfba10545ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 15:34:40 -0300 Subject: [PATCH 108/423] More fixes to docs --- .gitignore | 1 - docs/src/assets/dark.scss | 155 ------------------------------ docs/src/assets/light.scss | 121 ----------------------- docs/src/geometries/primitives.md | 2 +- 4 files changed, 1 insertion(+), 278 deletions(-) delete mode 100644 docs/src/assets/dark.scss delete mode 100644 docs/src/assets/light.scss diff --git a/.gitignore b/.gitignore index 2de6290f9..a14a49f25 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,3 @@ Manifest.toml docs/build /.benchmarkci /benchmark/*.json -docs/src/assets/themes/ diff --git a/docs/src/assets/dark.scss b/docs/src/assets/dark.scss deleted file mode 100644 index 37236360c..000000000 --- a/docs/src/assets/dark.scss +++ /dev/null @@ -1,155 +0,0 @@ -@charset "UTF-8"; -// The customizable variables can be found here: -// https://github.com/JuliaDocs/Documenter.jl/tree/master/assets/html/scss -// under documenter/_variables or documenter/_overrides. But some stuff are Bulma defaults -// as well, so you may need to look them up too: https://bulma.io/documentation/customize/variables/ - - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// These define the template: -$maincolor: rgb(78, 134, 151); // main color of the org theme -$secondcolor: rgb(197, 96, 255); // secondary color that serves as accents - // it is used as-is for links in light theme -$mainwhite: #fff; // color representing white -$mainblack: #202020; // color representing black -$darkbg: #1e1e20; // dark theme main page background - -// These commands set up the fonts for the main text and code blocks. -// the fonts must be included into the assets of the `makdocs` command, with e.g. -// format = Documenter.HTML( -// prettyurls = CI, -// assets = [ -// "assets/logo.ico", -// asset("https://fonts.googleapis.com/css?family=Montserrat|Source+Code+Pro&display=swap", class=:css), -// ], -// ), -$family-sans-serif: 'Montserrat', sans-serif; -$family-monospace: 'Source Code Pro', monospace; -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -// variables controlling the siderbar's shadow -$shadow-color: #bbb !default; -$shadow-size: 0.2rem !default; -$shadow-blur: 0.4rem !default; - -// Two cool helper functions: -$lightness-unit: 8% !default; -// Uses adjust-color to create a darker version of $color -@function darken-color($color, $factor) { - @return adjust-color($color, $lightness: -$factor*$lightness-unit); -} -// Uses adjust-color to create a lighter version of $color -@function lighten-color($color, $factor) { - @return adjust-color($color, $lightness: $factor*$lightness-unit); -} -// This template file overrides some of the Documenter theme variables to customize the theme: -$themename: "documenter-dark"; // CSS file must be called `$(themename).css` -// Instruct documenter/*.scss files that this is a dark theme -$documenter-is-dark-theme: true; - -$boldcolor: lighten-color($maincolor, 4.5); - -$body-background-color: $darkbg; // main page background - -// this is the color the links get, and also when they are hovered -$link: lighten-color($secondcolor, 2); -$link-hover: lighten-color($link, 3); - -// Main text color: -$text: darken-color($mainwhite, 0.2); -// Bold text color, also affects headers -$text-strong: $boldcolor; - -// Code text color: -$code: #fff; -//$code-background: rgba(0.5,0,0, 0.05); -$codebg: darken-color($maincolor, 3); -$code-background: $codebg; // for inline code -$pre-background: $codebg; // for code blocks -$documenter-docstring-header-background: lighten-color($body-background-color, 0.5); - -// Sidebar -$documenter-sidebar-background: darken-color($maincolor, 1.2); //background color for sidebar -$documenter-sidebar-color: $text; //font color for sidebar -$documenter-sidebar-menu-hover-color: $documenter-sidebar-color; -$documenter-sidebar-menu-hover-background: darken-color($documenter-sidebar-background, 1.2); - -$documenter-sidebar-menu-active-background: $darkbg; -$documenter-sidebar-menu-active-color: $mainwhite; -$documenter-sidebar-menu-active-hover-background: darken-color($documenter-sidebar-background, 1); -$documenter-sidebar-menu-active-hover-color: $documenter-sidebar-menu-active-color; -// these two change what happens with input boxes (the search box): -$input-hover-border-color: $secondcolor; -$input-focus-border-color: $mainwhite; - -$documenter-docstring-shadow: 3px 3px 4px invert($shadow-color); - -// Admonition stuff -$admbg: lighten-color($body-background-color, 0.5); -$admonition-background: ( - 'default': $admbg, 'info': $admbg, 'success': $admbg, 'warning': $admbg, - 'danger': $admbg, 'compat': $admbg -); -$admonition-header-background: ( - 'default': #ba3f1f, 'warning': #a88b17, 'danger': #c7524c, - 'success': #42ac68, 'info': #28c); - -// All secondary themes have to be nested in a theme--$(themename) class. When Documenter -// switches themes, it applies this class to and then disables the primary -// stylesheet. -@import "documenter/utilities"; -@import "documenter/variables"; -@import "bulma/utilities/all"; -@import "bulma/base/minireset.sass"; -@import "bulma/base/helpers.sass"; - -html.theme--#{$themename} { - @import "bulma/base/generic.sass"; - - @import "documenter/overrides"; - - @import "bulma/elements/all"; - @import "bulma/form/all"; - @import "bulma/components/all"; - @import "bulma/grid/all"; - @import "bulma/layout/all"; - - // Additional overrides, if need be - - @import "documenter/elements"; - @import "documenter/components/all"; - @import "documenter/patches"; - @import "documenter/layout/all"; - - @import "documenter/theme_overrides"; - - // $shadow-color: #202224; - - #documenter .docs-sidebar { // This makes sidebar have shadow at all displays - border-right: none; - box-shadow: 1.2*$shadow-size 0rem 1*$shadow-blur invert($shadow-color); - - form.docs-search > input { // these controls are for the searchbar - color: $mainwhite; - background-color: darken-color($documenter-sidebar-background, 1); - border-color: darken-color($documenter-sidebar-background, 2); - margin-top: 1.0rem; - margin-bottom: 1.0rem; // adjust the margins between search and other elements - &::placeholder { - color: $mainwhite; // placeholder text color ("Search here...") - } - } - } - // FIXME: Hack to get a proper theme for highlight.js in the dark theme - @import "highlightjs/a11y-dark"; - // Also, a11y-dark does not highlight string interpolation properly. - .hljs-subst { - color: #f8f8f2; - } -} - -#documenter .admonition-header { // Color of notes - background-color: $maincolor; -} diff --git a/docs/src/assets/light.scss b/docs/src/assets/light.scss deleted file mode 100644 index 1efed7db3..000000000 --- a/docs/src/assets/light.scss +++ /dev/null @@ -1,121 +0,0 @@ -@charset "UTF-8"; -// The customizable variables can be found here: -// https://github.com/JuliaDocs/Documenter.jl/tree/master/assets/html/scss -// under documenter/_variables or documenter/_overrides. But some stuff are Bulma defaults -// as well, so you may need to look them up too: https://bulma.io/documentation/customize/variables/ - - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// These define the template: -$maincolor: rgb(78, 134, 151); // main color of the org theme -$secondcolor: rgb(48, 23, 140); // secondary color that serves as accents - // it is used as-is for links in light theme -$mainwhite: #fff; // color representing white -$mainblack: #202020; // color representing black -$darkbg: #1e1e20; // dark theme main page background - -// These commands set up the fonts for the main text and code blocks. -// the fonts must be included into the assets of the `makdocs` command, with e.g. -// format = Documenter.HTML( -// prettyurls = CI, -// assets = [ -// "assets/logo.ico", -// asset("https://fonts.googleapis.com/css?family=Montserrat|Source+Code+Pro&display=swap", class=:css), -// ], -// ), -$family-sans-serif: 'Montserrat', sans-serif; -$family-monospace: 'Source Code Pro', monospace; -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -// variables controlling the siderbar's shadow -$shadow-color: #bbb !default; -$shadow-size: 0.2rem !default; -$shadow-blur: 0.4rem !default; - -// Two cool helper functions: -$lightness-unit: 8% !default; -// Uses adjust-color to create a darker version of $color -@function darken-color($color, $factor) { - @return adjust-color($color, $lightness: -$factor*$lightness-unit); -} -// Uses adjust-color to create a lighter version of $color -@function lighten-color($color, $factor) { - @return adjust-color($color, $lightness: $factor*$lightness-unit); -} -// This template file overrides some of the Documenter theme variables to customize the theme: -$themename: "documenter-light"; // CSS file must be called `$(themename).css` - -$boldcolor: $maincolor; // darken-color($maincolor, 1); - -$body-background-color: $mainwhite; // main page background - -// Sidebar -$documenter-sidebar-background: $maincolor; //background color for sidebar -$documenter-sidebar-color: $mainwhite; //font color all sidebar -$documenter-sidebar-menu-hover-color: $documenter-sidebar-color; // text color -$documenter-sidebar-menu-hover-background: darken-color($documenter-sidebar-background, 1.5); - -$documenter-sidebar-menu-active-background: $mainwhite; -$documenter-sidebar-menu-active-color: $mainblack; -$documenter-sidebar-menu-active-hover-background: lighten-color($documenter-sidebar-background, 5.2); -$documenter-sidebar-menu-active-hover-color: $documenter-sidebar-menu-active-color; - -// this is the color the links get, and also when they are hovered -$link: $secondcolor; -$link-hover: darken-color($link, 1); - -// Main text color: -$text: $mainblack; -// Bold text color, also affects headers -$text-strong: $boldcolor; - -// Section headers (markdown ###) text color: -// TODO - -// Code text color: -$code: #000000; -//$code-background: rgba(0.5,0,0, 0.05); -$codebg: lighten-color($maincolor, 6.2); -// $codebg: lighten-color($secondcolor, 8); -$code-background: $codebg; // for inline code -$pre-background: $codebg; // for code blocks -$documenter-docstring-header-background: darken-color($body-background-color, 0.4); - -// main text font size -$body-font-size: 1.0em; // the default is 1.0 -// Sidebar text font size -$documenter-sidebar-size: 1.0em; // the default is 1.0 as well - -// these two change what happens with input boxes (the search box): -$input-hover-border-color: $secondcolor; -$input-focus-border-color: $mainwhite; - -// Include the original theme which will now use the updated variables. -// This should be the last thing in the SCSS file. -@import "documenter-light"; - -#documenter .docs-sidebar { // This makes sidebar have shadow at all displays - border-right: none; - box-shadow: 1.2*$shadow-size 0rem 1*$shadow-blur $shadow-color; - - form.docs-search > input { // these controls are for the searchbar - color: $mainwhite; - background-color: darken-color($documenter-sidebar-background, 1); - border-color: darken-color($documenter-sidebar-background, 2); - margin-top: 1.0rem; - margin-bottom: 1.0rem; // adjust the margins between search and other elements - &::placeholder { - color: $mainwhite; // placeholder text color ("Search here...") - } - } -} - -#documenter .content p { // Justify text in paragraphs - text-align: justify; -} - -#documenter .admonition-header { // Color of notes - background-color: $maincolor; -} diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 977672dda..1eef9f4c6 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -79,7 +79,7 @@ Sphere Ball((0.,0.,0.), 1.) |> viz ``` -## Ellipsoid +### Ellipsoid ```@docs Ellipsoid From c37ec918e902a01fecdce4d6d8a1e69fb683bf6b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:04:31 -0300 Subject: [PATCH 109/423] Fix the implementation of the `LengthUnit` transform (#894) * Fix the implementation of the 'LengthUnit' transform * Apply suggestions --- src/transforms/lengthunit.jl | 4 ++++ test/transforms.jl | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index 8654da69c..207f282fc 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -55,6 +55,10 @@ _lenunit(c, _) = throw(ArgumentError("the length unit of $(prettyname(c)) cannot # SPECIAL CASES # -------------- +applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len) + +applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens) + applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g))) applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) diff --git a/test/transforms.jl b/test/transforms.jl index 761ff4015..f61219a84 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1312,6 +1312,27 @@ @test r isa Box @test r ≈ Box(Point(T(0) * u"cm", T(0) * u"cm"), Point(T(100) * u"cm", T(100) * u"cm")) + # ------- + # SPHERE + # ------- + + f = LengthUnit(u"km") + g = Sphere(point(0, 0), T(1000)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(Point(T(0) * u"km", T(0) * u"km"), T(1) * u"km") + + # ---------- + # ELLIPSOID + # ---------- + + f = LengthUnit(u"cm") + g = Ellipsoid(T.((1, 1, 1)), point(0, 0, 0)) + r, c = TB.apply(f, g) + @test r isa Ellipsoid + @test r ≈ + Ellipsoid((T(100) * u"cm", T(100) * u"cm", T(100) * u"cm"), Point(T(0) * u"cm", T(0) * u"cm", T(0) * u"cm")) + # --------- # TRIANGLE # --------- @@ -1366,10 +1387,10 @@ # -------------- f = LengthUnit(u"km") - d = CartesianGrid((10, 10), point(1000, 1000), T.((1, 1))) + d = CartesianGrid((10, 10), point(1000, 1000), T.((1000, 1000))) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), T.((1, 1))) + @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), (T(1) * u"km", T(1) * u"km")) # ---------------- # RECTILINEARGRID From 2c652a0bbc4aa9ee4c50596f8f454fd17d8ee48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 16:11:33 -0300 Subject: [PATCH 110/423] Fix #891: add centroid for ParaboloidSurface --- src/primitives/paraboloidsurface.jl | 10 ++++++++++ test/primitives.jl | 1 + 2 files changed, 11 insertions(+) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 1b0d6eb32..33f0fa876 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -91,6 +91,16 @@ The axis is always aligned with the z direction. """ axis(p::ParaboloidSurface{P,ℒ}) where {P,ℒ} = Line(p.apex, p.apex + Vec(ℒ(0), ℒ(0), p.focallength)) +function centroid(p::ParaboloidSurface) + c = p.apex + r = p.radius + f = p.focallength + z = r^2 / 4f + x = zero(z) + y = zero(z) + c + Vec(x, y, z / 2) +end + Base.isapprox(p₁::ParaboloidSurface, p₂::ParaboloidSurface) = p₁.apex ≈ p₂.apex && isapproxequal(p₁.focallength, p₂.focallength) && isapproxequal(p₁.radius, p₂.radius) diff --git a/test/primitives.jl b/test/primitives.jl index 0dbe65c0e..2e5a266be 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1013,6 +1013,7 @@ @test radius(p) == T(1) * u"m" @test axis(p) == Line(point(0, 0, 0), point(0, 0, T(2))) @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" + @test centroid(p) == point(0, 0, 1/16) p1 = ParaboloidSurface(point(1, 2, 3), T(1), T(1)) p2 = ParaboloidSurface(point(1, 2, 3), T(1)) From 789f31cb583cc30bf25a3272860b534031ea6402 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:26:58 -0300 Subject: [PATCH 111/423] :robot: Format .jl files (#895) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- test/primitives.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/primitives.jl b/test/primitives.jl index 2e5a266be..85a1cc3d2 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1013,7 +1013,7 @@ @test radius(p) == T(1) * u"m" @test axis(p) == Line(point(0, 0, 0), point(0, 0, T(2))) @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" - @test centroid(p) == point(0, 0, 1/16) + @test centroid(p) == point(0, 0, 1 / 16) p1 = ParaboloidSurface(point(1, 2, 3), T(1), T(1)) p2 = ParaboloidSurface(point(1, 2, 3), T(1)) From e4f610b6ad1fcba39255252ac00c3cc3d9aa33f3 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:53:45 -0300 Subject: [PATCH 112/423] Shadow: Add more special cases (#896) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Shadow: Add more special cases * Add tests * Update src/transforms/shadow.jl Co-authored-by: Júlio Hoffimann * Update tests --------- Co-authored-by: Júlio Hoffimann --- src/transforms/shadow.jl | 26 +++++++++++ test/transforms.jl | 99 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index f1203fa1e..022f2279a 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -51,6 +51,32 @@ _shadow(v::Vec, dims) = v[dims] _shadow(p::Point, dims) = withdatum(p, to(p)[dims]) +_shadow(::Plane, _) = throw(ArgumentError("Shadow transform doesn't yet support planes")) + +_shadow(e::Ellipsoid, dims) = _shadow(discretize(e), dims) + +_shadow(d::Disk, dims) = _shadow(discretize(d), dims) + +_shadow(c::Circle, dims) = _shadow(discretize(c), dims) + +_shadow(c::Cylinder, dims) = _shadow(discretize(c), dims) + +_shadow(c::CylinderSurface, dims) = _shadow(discretize(c), dims) + +_shadow(c::Cone, dims) = _shadow(discretize(c), dims) + +_shadow(c::ConeSurface, dims) = _shadow(discretize(c), dims) + +_shadow(f::Frustum, dims) = _shadow(discretize(f), dims) + +_shadow(f::FrustumSurface, dims) = _shadow(discretize(f), dims) + +_shadow(p::ParaboloidSurface, dims) = _shadow(discretize(p), dims) + +_shadow(t::Torus, dims) = _shadow(discretize(t), dims) + +_shadow(t::CylindricalTrajectory, dims) = _shadow(GeometrySet(collect(t)), dims) + function _shadow(g::CartesianGrid, dims) sz = size(g)[dims] or = _shadow(minimum(g), dims) diff --git a/test/transforms.jl b/test/transforms.jl index f61219a84..b0d4eecfd 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1471,6 +1471,105 @@ @test r isa Box @test r == Box(point(1, 2), point(4, 5)) + # ------ + # PLANE + # ------ + + f = Shadow(:xz) + g = Plane(point(0, 0, 0), vector(0, 0, 1)) + @test_throws ArgumentError TB.apply(f, g) + + # ---------- + # ELLIPSOID + # ---------- + + f = Shadow(:yz) + g = Ellipsoid(T.((1, 2, 3))) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ----- + # DISK + # ----- + + f = Shadow(:xy) + g = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ------- + # CIRCLE + # ------- + + f = Shadow(:xz) + g = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ---------------- + # CYLINDERSURFACE + # ---------------- + + f = Shadow(:yz) + g = CylinderSurface(T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ------------ + # CONESURFACE + # ------------ + + f = Shadow(:xy) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + g = ConeSurface(Disk(p, T(2)), point(0, 0, 1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # --------------- + # FRUSTUMSURFACE + # --------------- + + f = Shadow(:xz) + pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + g = FrustumSurface(Disk(pb, T(1)), Disk(pt, T(2))) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ------------------ + # PARABOLOIDSURFACE + # ------------------ + + f = Shadow(:yz) + g = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + + # ------ + # TORUS + # ------ + + f = Shadow(:xy) + g = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa SimpleMesh + @test r ≈ f(m) + # --------- # TRIANGLE # --------- From 70036070cedcf288a1396d279f094737d673f884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 18:39:24 -0300 Subject: [PATCH 113/423] Add VoronoiTesselation --- src/Meshes.jl | 5 ++++- src/tesselation.jl | 1 + src/tesselation/delaunay.jl | 6 +++--- src/tesselation/voronoi.jl | 37 +++++++++++++++++++++++++++++++++++++ test/tesselation.jl | 12 ++++++++++-- 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 src/tesselation/voronoi.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index 33d0e586b..93407f793 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -22,8 +22,10 @@ using Rotations: Rotation, QuatRotation, Angle2d using Rotations: rotation_between using NearestNeighbors: KDTree, BallTree using NearestNeighbors: knn, inrange -using DelaunayTriangulation: triangulate +using DelaunayTriangulation: triangulate, voronoi using DelaunayTriangulation: each_solid_triangle +using DelaunayTriangulation: each_polygon +using DelaunayTriangulation: get_polygon_points using Transducers: Filter, Map, TakeWhile, tcollect, ⨟ using Base.Cartesian: @nloops, @nref, @ntuple using Base: @propagate_inbounds @@ -473,6 +475,7 @@ export # tesselation TesselationMethod, DelaunayTesselation, + VoronoiTesselation, tesselate, # discretization diff --git a/src/tesselation.jl b/src/tesselation.jl index a1e7288d5..a7bb445e4 100644 --- a/src/tesselation.jl +++ b/src/tesselation.jl @@ -25,3 +25,4 @@ tesselate(points::AbstractVector{<:Point{2}}, method::TesselationMethod) = tesse # ---------------- include("tesselation/delaunay.jl") +include("tesselation/voronoi.jl") diff --git a/src/tesselation/delaunay.jl b/src/tesselation/delaunay.jl index a538475ff..bd5e484f0 100644 --- a/src/tesselation/delaunay.jl +++ b/src/tesselation/delaunay.jl @@ -20,9 +20,9 @@ end DelaunayTesselation(rng=Random.default_rng()) = DelaunayTesselation(rng) function tesselate(pset::PointSet{2}, method::DelaunayTesselation) - points = collect(pset) - coords = map(p -> ustrip.(to(p)), points) + # perform tesselation with raw coordinates + coords = map(p -> ustrip.(to(p)), pset) triang = triangulate(coords, rng=method.rng) connec = connect.(each_solid_triangle(triang)) - SimpleMesh(points, connec) + SimpleMesh(collect(pset), connec) end diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl new file mode 100644 index 000000000..dbc971450 --- /dev/null +++ b/src/tesselation/voronoi.jl @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + VoronoiTesselation([rng]) + +Unconstrained Voronoi tesselation of point sets. +Optionally, specify the random number generator `rng`. + +## References + +* Cheng et al. 2012. [Delaunay Mesh Generation] + (https://people.eecs.berkeley.edu/~jrs/meshbook.html) +""" +struct VoronoiTesselation{RNG<:AbstractRNG} <: TesselationMethod + rng::RNG +end + +VoronoiTesselation(rng=Random.default_rng()) = VoronoiTesselation(rng) + +function tesselate(pset::PointSet{2}, method::VoronoiTesselation) + # perform tesselation with raw coordinates + coords = map(p -> ustrip.(to(p)), pset) + triang = triangulate(coords, rng=method.rng) + vorono = voronoi(triang, clip=true) + + # mesh with all (possibly unused) points + points = get_polygon_points(vorono) + polygs = each_polygon(vorono) + tuples = [Tuple(inds[begin:end-1]) for inds in polygs] + connec = connect.(tuples) + mesh = SimpleMesh(points, connec) + + # remove unused points + mesh |> Repair{1}() +end diff --git a/test/tesselation.jl b/test/tesselation.jl index d0a914101..856bbcf7e 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -2,8 +2,16 @@ @testset "Delaunay" begin pts = randpoint2(100) pset = PointSet(pts) - mesh1 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - mesh2 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) + mesh1 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) + mesh2 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test mesh1 == mesh2 + end + + @testset "Voronoi" begin + pts = randpoint2(100) + pset = PointSet(pts) + mesh1 = tesselate(pts, VoronoiTesselation(StableRNG(2024))) + mesh2 = tesselate(pset, VoronoiTesselation(StableRNG(2024))) @test mesh1 == mesh2 end end From 1fc87ed39f4642aa1f435c456bd39a7f541f3009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 18:53:44 -0300 Subject: [PATCH 114/423] Add Delaunay/Voronoi and Shadow to docs --- docs/make.jl | 1 + docs/src/algorithms/tesselation.md | 16 ++++++++++++++++ docs/src/transforms.md | 17 +++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/docs/make.jl b/docs/make.jl index e35dbcd0b..1e87a8812 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -40,6 +40,7 @@ makedocs( "algorithms/sampling.md", "algorithms/partitioning.md", "algorithms/discretization.md", + "algorithms/tesselation.md", "algorithms/refinement.md", "algorithms/coarsening.md", "algorithms/simplification.md", diff --git a/docs/src/algorithms/tesselation.md b/docs/src/algorithms/tesselation.md index d8250aa35..29d448433 100644 --- a/docs/src/algorithms/tesselation.md +++ b/docs/src/algorithms/tesselation.md @@ -25,3 +25,19 @@ viz(mesh, showsegments = true) viz!(points, color = :red) Mke.current_figure() ``` + +## VoronoiTesselation + +```@docs +VoronoiTesselation +``` + +```@example tesselation +points = rand(Point{2}, 100) + +mesh = tesselate(points, VoronoiTesselation()) + +viz(mesh, showsegments = true) +viz!(points, color = :red) +Mke.current_figure() +``` diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 4540ad358..e5e1f23f3 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -150,6 +150,23 @@ using Unitful: m, cm Point(1m, 2m, 3m) |> LengthUnit(cm) ``` +## Shadow + +```@docs +Shadow +``` + +```@example transforms +ball = Ball((0, 0, 0), 1) +disk = ball |> Shadow("xy") + + +fig = Mke.Figure(size = (800, 400)) +viz(fig[1,1], ball) +viz(fig[1,2], disk) +fig +``` + ## Repair ```@docs From a8c97045927f1ab6f0f283ca93f1c1d7b3b9d993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Jun 2024 18:54:28 -0300 Subject: [PATCH 115/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 59e8cad67..8dfa0dbc2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.44.0" +version = "0.45.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From e7eb760d5f0d0326f3f6cfde2f2e0b118156b04a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:54:55 -0300 Subject: [PATCH 116/423] :robot: Format .jl files (#897) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/tesselation/voronoi.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index dbc971450..63ceba7ea 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -28,7 +28,7 @@ function tesselate(pset::PointSet{2}, method::VoronoiTesselation) # mesh with all (possibly unused) points points = get_polygon_points(vorono) polygs = each_polygon(vorono) - tuples = [Tuple(inds[begin:end-1]) for inds in polygs] + tuples = [Tuple(inds[begin:(end - 1)]) for inds in polygs] connec = connect.(tuples) mesh = SimpleMesh(points, connec) From ce00fb1dcde9b405a216d50af84ac6a3dd8d0285 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:30:55 -0300 Subject: [PATCH 117/423] :robot: Format .jl files (#899) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> From 5e270ad8922c0cb413ba788214222473a3e11234 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:43:24 -0300 Subject: [PATCH 118/423] Refactor inner constructors (#902) --- src/mesh/cartesiangrid.jl | 16 +++++++--------- src/mesh/rectilineargrid.jl | 11 +++++------ src/mesh/structuredgrid.jl | 11 +++++------ src/primitives/ball.jl | 4 +--- src/primitives/circle.jl | 4 +--- src/primitives/cylinder.jl | 4 +--- src/primitives/cylindersurface.jl | 5 +---- src/primitives/disk.jl | 4 +--- src/primitives/ellipsoid.jl | 6 ++---- src/primitives/paraboloidsurface.jl | 6 ++---- src/primitives/sphere.jl | 3 +-- src/primitives/torus.jl | 6 ++---- src/trajecs.jl | 6 ++---- 13 files changed, 31 insertions(+), 55 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 1efda3959..5a8b3da21 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -56,21 +56,19 @@ struct CartesianGrid{Dim,C<:CRS,ℒ<:Len} <: Grid{Dim,C} offset::Dims{Dim} topology::GridTopology{Dim} - function CartesianGrid{Dim,C,ℒ}(origin, spacing, offset, topology) where {Dim,C<:CRS,ℒ<:Len} + function CartesianGrid( + origin::Point{Dim,C}, + spacing::NTuple{Dim,ℒ}, + offset::Dims{Dim}, + topology::GridTopology{Dim} + ) where {Dim,C<:CRS,ℒ<:Len} if !all(>(zero(ℒ)), spacing) throw(ArgumentError("spacing must be positive")) end - new(origin, spacing, offset, topology) + new{Dim,C,float(ℒ)}(origin, spacing, offset, topology) end end -CartesianGrid( - origin::Point{Dim,C}, - spacing::NTuple{Dim,ℒ}, - offset::Dims{Dim}, - topology::GridTopology{Dim} -) where {Dim,C<:CRS,ℒ<:Len} = CartesianGrid{Dim,C,float(ℒ)}(origin, spacing, offset, topology) - CartesianGrid(origin::Point{Dim}, spacing::NTuple{Dim}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 9f65a9ed7..3d7c90272 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -23,13 +23,12 @@ julia> RectilinearGrid(x, y) struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} xyz::NTuple{Dim,V} topology::GridTopology{Dim} - RectilinearGrid{Datum,Dim,ℒ,V}(xyz, topology) where {Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} = new(xyz, topology) -end -function RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} - coords = float.(xyz) - V = eltype(coords) - RectilinearGrid{Datum,Dim,eltype(V),V}(coords, topology) + function RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} + coords = float.(xyz) + V = eltype(coords) + new{Datum,Dim,eltype(V),V}(coords, topology) + end end RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector}, topology::GridTopology{Dim}) where {Datum,Dim} = diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index 136b77e04..21f88aad7 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -23,13 +23,12 @@ julia> StructuredGrid(X, Y) struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} - StructuredGrid{Datum,Dim,ℒ,A}(XYZ, topology) where {Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} = new(XYZ, topology) -end -function StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} - coords = float.(XYZ) - A = eltype(coords) - StructuredGrid{Datum,Dim,eltype(A),A}(coords, topology) + function StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} + coords = float.(XYZ) + A = eltype(coords) + new{Datum,Dim,eltype(A),A}(coords, topology) + end end StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray}, topology::GridTopology{Dim}) where {Datum,Dim} = diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index e89029677..345a6250b 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -12,11 +12,9 @@ See also [`Sphere`](@ref). struct Ball{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} center::Point{Dim,C} radius::ℒ - Ball{Dim,C,ℒ}(center, radius) where {Dim,C<:CRS,ℒ<:Len} = new(center, radius) + Ball(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = new{Dim,C,float(ℒ)}(center, radius) end -Ball(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = Ball{Dim,C,float(ℒ)}(center, radius) - Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) Ball(center::Tuple, radius) = Ball(Point(center), radius) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 09ede78bd..d0dc447b8 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -13,11 +13,9 @@ See also [`Disk`](@ref). struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} plane::P radius::ℒ - Circle{C,P,ℒ}(plane, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(plane, radius) + Circle(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) end -Circle(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Circle{C,P,float(ℒ)}(plane, radius) - Circle(plane::Plane, radius) = Circle(plane, addunit(radius, u"m")) """ diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index b5ca32657..373a8cbac 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -28,11 +28,9 @@ struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} bot::P top::P radius::ℒ - Cylinder{C,P,ℒ}(bot, top, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(bot, top, radius) + Cylinder(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(bot, top, radius) end -Cylinder(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Cylinder{C,P,float(ℒ)}(bot, top, radius) - Cylinder(bot::P, top::P, radius) where {P<:Plane} = Cylinder(bot, top, addunit(radius, u"m")) function Cylinder(start::Point{3}, finish::Point{3}, radius) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 35a00ddc4..867070cee 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -28,12 +28,9 @@ struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} bot::P top::P radius::ℒ - CylinderSurface{C,P,ℒ}(bot, top, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(bot, top, radius) + CylinderSurface(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(bot, top, radius) end -CylinderSurface(bot::P, top::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = - CylinderSurface{C,P,float(ℒ)}(bot, top, radius) - CylinderSurface(bot::P, top::P, radius) where {P<:Plane} = CylinderSurface(bot, top, addunit(radius, u"m")) function CylinderSurface(start::Point{3}, finish::Point{3}, radius) diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index b3d5c670d..b50acfa61 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -13,11 +13,9 @@ See also [`Circle`](@ref). struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} plane::P radius::ℒ - Disk{C,P,ℒ}(plane, radius) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new(plane, radius) + Disk(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) end -Disk(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = Disk{C,P,float(ℒ)}(plane, radius) - Disk(plane::Plane, radius) = Disk(plane, addunit(radius, u"m")) paramdim(::Type{<:Disk}) = 2 diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 80153d913..c9d1ad72a 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -11,12 +11,10 @@ struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{3,C} radii::NTuple{3,ℒ} center::Point{3,C} rotation::R - Ellipsoid{ℒ,C,R}(radii, center, rotation) where {ℒ<:Len,C<:CRS,R} = new(radii, center, rotation) + Ellipsoid(radii::NTuple{3,ℒ}, center::Point{3,C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = + new{float(ℒ),C,R}(radii, center, rotation) end -Ellipsoid(radii::NTuple{3,ℒ}, center::Point{3,C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = - Ellipsoid{float(ℒ),C,R}(radii, center, rotation) - Ellipsoid(radii::NTuple{3}, center::Point{3}, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) Ellipsoid(radii::NTuple{3}, center::NTuple{3}, rotation) = Ellipsoid(radii, Point(center), rotation) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 33f0fa876..c046df63f 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -36,12 +36,10 @@ struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{3,C} apex::Point{3,C} radius::ℒ focallength::ℒ - ParaboloidSurface{C,ℒ}(apex, radius, focallength) where {C<:CRS,ℒ<:Len} = new(apex, radius, focallength) + ParaboloidSurface(apex::Point{3,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = + new{C,float(ℒ)}(apex, radius, focallength) end -ParaboloidSurface(apex::Point{3,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = - ParaboloidSurface{C,float(ℒ)}(apex, radius, focallength) - ParaboloidSurface(apex::Point{3}, radius::Len, focallength::Len) = ParaboloidSurface(apex, promote(radius, focallength)...) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 952958680..d25dd8b09 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -12,10 +12,9 @@ See also [`Ball`](@ref). struct Sphere{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} center::Point{Dim,C} radius::ℒ - Sphere{Dim,C,ℒ}(center, radius) where {Dim,C<:CRS,ℒ<:Len} = new(center, radius) + Sphere(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = new{Dim,C,float(ℒ)}(center, radius) end -Sphere(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = Sphere{Dim,C,float(ℒ)}(center, radius) Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 76a271a4f..95875ab75 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -14,12 +14,10 @@ struct Torus{C<:CRS,ℒ<:Len} <: Primitive{3,C} normal::Vec{3,ℒ} major::ℒ minor::ℒ - Torus{C,ℒ}(center, normal, major, minor) where {C<:CRS,ℒ<:Len} = new(center, normal, major, minor) + Torus(center::Point{3,C}, normal::Vec{3,ℒ}, major::ℒ, minor::ℒ) where {C<:CRS,ℒ<:Len} = + new{C,float(ℒ)}(center, normal, major, minor) end -Torus(center::Point{3,C}, normal::Vec{3,ℒ}, major::ℒ, minor::ℒ) where {C<:CRS,ℒ<:Len} = - Torus{C,float(ℒ)}(center, normal, major, minor) - function Torus(center::Point{3}, normal::Vec{3}, major::Len, minor::Len) ℒ = promote_type(eltype(normal), typeof(major), typeof(minor)) Torus(center, Vec{3,ℒ}(normal), ℒ(major), ℒ(minor)) diff --git a/src/trajecs.jl b/src/trajecs.jl index f3724d22d..4bc9b93d0 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -10,12 +10,10 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. struct CylindricalTrajectory{C<:CRS,ℒ<:Len} <: Domain{3,C} centroids::Vector{Point{3,C}} radius::ℒ - CylindricalTrajectory{C,ℒ}(centroids, radius) where {C<:CRS,ℒ<:Len} = new(centroids, radius) + CylindricalTrajectory(centroids::Vector{Point{3,C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = + new{C,float(ℒ)}(centroids, radius) end -CylindricalTrajectory(centroids::Vector{Point{3,C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = - CylindricalTrajectory{C,float(ℒ)}(centroids, radius) - CylindricalTrajectory(centroids, radius::Len) = CylindricalTrajectory(collect(centroids), radius) CylindricalTrajectory(centroids, radius) = CylindricalTrajectory(centroids, addunit(radius, u"m")) From 5a5a288ee00c076ac68d0ede1856a0fdd81b93a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 10:16:28 -0300 Subject: [PATCH 119/423] :robot: Format .jl files (#903) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/mesh/rectilineargrid.jl | 5 ++++- src/primitives/sphere.jl | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 3d7c90272..1d8b7677c 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -24,7 +24,10 @@ struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Dim,Ca xyz::NTuple{Dim,V} topology::GridTopology{Dim} - function RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} + function RectilinearGrid{Datum}( + xyz::NTuple{Dim,<:AbstractVector{<:Len}}, + topology::GridTopology{Dim} + ) where {Datum,Dim} coords = float.(xyz) V = eltype(coords) new{Datum,Dim,eltype(V),V}(coords, topology) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index d25dd8b09..f2ef87cab 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -15,7 +15,6 @@ struct Sphere{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} Sphere(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = new{Dim,C,float(ℒ)}(center, radius) end - Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) Sphere(center::Tuple, radius) = Sphere(Point(center), radius) From 7d8e2d485d3754c2d836ed39f103c2d03f312cdd Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 20 Jun 2024 09:21:42 -0300 Subject: [PATCH 120/423] Define `==` and `isapprox` for all geometries (#901) * Define '==' for all geometries * Apply suggestions * Add tests for primitives * Add tests for polytopes * Add tests for Multi * Define a promotion rule for Vec * Apply suggestion: Use '==' in Ray comparison * Uncomment tests * Define 'isapprox' for all geometries * Forward keyword arguments in all methods * Add tests for primitives * Add 'equaltest' helper * Add 'isapproxtest' helper * Fix typo * Apply suggestions * Apply suggestions --- src/multigeoms.jl | 5 +- src/polytopes/hexahedron.jl | 6 +- src/polytopes/ngon.jl | 8 +- src/polytopes/polyarea.jl | 6 +- src/polytopes/pyramid.jl | 6 +- src/polytopes/ring.jl | 6 +- src/polytopes/rope.jl | 6 +- src/polytopes/segment.jl | 6 +- src/polytopes/tetrahedron.jl | 6 +- src/primitives/ball.jl | 5 ++ src/primitives/bezier.jl | 6 ++ src/primitives/box.jl | 5 +- src/primitives/circle.jl | 5 ++ src/primitives/cone.jl | 5 ++ src/primitives/conesurface.jl | 5 ++ src/primitives/cylinder.jl | 5 +- src/primitives/cylindersurface.jl | 8 +- src/primitives/disk.jl | 5 ++ src/primitives/ellipsoid.jl | 10 ++- src/primitives/frustum.jl | 5 ++ src/primitives/frustumsurface.jl | 5 ++ src/primitives/line.jl | 3 + src/primitives/paraboloidsurface.jl | 9 ++- src/primitives/plane.jl | 8 +- src/primitives/point.jl | 5 +- src/primitives/ray.jl | 5 +- src/primitives/sphere.jl | 5 +- src/primitives/torus.jl | 9 +++ src/utils.jl | 9 ++- src/vectors.jl | 1 + test/multigeoms.jl | 6 ++ test/polytopes.jl | 48 +++++++++++- test/primitives.jl | 95 +++++++++++++++++++++++ test/runtests.jl | 69 +++-------------- test/testutils.jl | 113 ++++++++++++++++++++++++++++ 35 files changed, 401 insertions(+), 108 deletions(-) create mode 100644 test/testutils.jl diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 6d3117a22..11b83d8a9 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -54,9 +54,10 @@ rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] Base.parent(m::Multi) = m.geoms -==(m₁::Multi, m₂::Multi) = length(m₁.geoms) == length(m₂.geoms) && all(g -> g[1] == g[2], zip(m₁.geoms, m₂.geoms)) +==(m₁::Multi, m₂::Multi) = m₁.geoms == m₂.geoms -Base.isapprox(m₁::Multi, m₂::Multi) = all(g -> g[1] ≈ g[2], zip(m₁.geoms, m₂.geoms)) +Base.isapprox(m₁::Multi, m₂::Multi; atol=atol(lentype(m₁)), kwargs...) = + length(m₁.geoms) == length(m₂.geoms) && all(isapprox(g₁, g₂; atol, kwargs...) for (g₁, g₂) in zip(m₁.geoms, m₂.geoms)) # ----------- # IO METHODS diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index 8a269dd4c..a3c4d0608 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -11,8 +11,10 @@ A hexahedron with points `p1`, `p2`, ..., `p8`. nvertices(::Type{<:Hexahedron}) = 8 -Base.isapprox(h₁::Hexahedron, h₂::Hexahedron; kwargs...) = - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(h₁.vertices, h₂.vertices)) +==(h₁::Hexahedron, h₂::Hexahedron) = h₁.vertices == h₂.vertices + +Base.isapprox(h₁::Hexahedron, h₂::Hexahedron; atol=atol(lentype(h₁)), kwargs...) = + all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(h₁.vertices, h₂.vertices)) function (h::Hexahedron)(u, v, w) if (u < 0 || u > 1) || (v < 0 || v > 1) || (w < 0 || w > 1) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 15889bf63..b8faca136 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -52,10 +52,10 @@ Base.unique!(ngon::Ngon) = ngon nvertices(::Type{<:Ngon{N}}) where {N} = N -function Base.isapprox(p₁::Ngon, p₂::Ngon; kwargs...) - nvertices(p₁) ≠ nvertices(p₂) && return false - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) -end +==(p₁::Ngon, p₂::Ngon) = p₁.vertices == p₂.vertices + +Base.isapprox(p₁::Ngon, p₂::Ngon; atol=atol(lentype(p₁)), kwargs...) = + nvertices(p₁) == nvertices(p₂) && all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) rings(ngon::Ngon) = [Ring(pointify(ngon))] diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 4b2f24b93..636a71f68 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -69,10 +69,8 @@ PolyArea(outer...; fix=true) = PolyArea(collect(outer); fix) ==(p₁::PolyArea, p₂::PolyArea) = p₁.rings == p₂.rings -function Base.isapprox(p₁::PolyArea, p₂::PolyArea; kwargs...) - length(p₁.rings) ≠ length(p₂.rings) && return false - all(isapprox(r₁, r₂; kwargs...) for (r₁, r₂) in zip(p₁.rings, p₂.rings)) -end +Base.isapprox(p₁::PolyArea, p₂::PolyArea; atol=atol(lentype(p₁)), kwargs...) = + length(p₁.rings) == length(p₂.rings) && all(isapprox(r₁, r₂; atol, kwargs...) for (r₁, r₂) in zip(p₁.rings, p₂.rings)) vertices(p::PolyArea) = mapreduce(vertices, vcat, p.rings) diff --git a/src/polytopes/pyramid.jl b/src/polytopes/pyramid.jl index da59a89dc..36974c6b8 100644 --- a/src/polytopes/pyramid.jl +++ b/src/polytopes/pyramid.jl @@ -11,7 +11,9 @@ A pyramid with points `p1`, `p2`, `p3`, `p4`, `p5`. nvertices(::Type{<:Pyramid}) = 5 -Base.isapprox(p₁::Pyramid, p₂::Pyramid; kwargs...) = - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) +==(p₁::Pyramid, p₂::Pyramid) = p₁.vertices == p₂.vertices + +Base.isapprox(p₁::Pyramid, p₂::Pyramid; atol=atol(lentype(p₁)), kwargs...) = + all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid{Dim}}) where {Dim} = Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 2956cd456..14b6e668d 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -47,10 +47,8 @@ function ≗(r₁::Ring, r₂::Ring) r₁.vertices == r₂.vertices[i:(i + n - 1)] end -function Base.isapprox(r₁::Ring, r₂::Ring; kwargs...) - nvertices(r₁) ≠ nvertices(r₂) && return false - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(r₁.vertices, r₂.vertices)) -end +Base.isapprox(r₁::Ring, r₂::Ring; atol=atol(lentype(r₁)), kwargs...) = + nvertices(r₁) == nvertices(r₂) && all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(r₁.vertices, r₂.vertices)) Base.close(r::Ring) = r diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index e54acf1e6..ce7359f72 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -21,10 +21,8 @@ nvertices(r::Rope) = length(r.vertices) ==(r₁::Rope, r₂::Rope) = r₁.vertices == r₂.vertices -function Base.isapprox(r₁::Rope, r₂::Rope; kwargs...) - nvertices(r₁) ≠ nvertices(r₂) && return false - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(r₁.vertices, r₂.vertices)) -end +Base.isapprox(r₁::Rope, r₂::Rope; atol=atol(lentype(r₁)), kwargs...) = + nvertices(r₁) == nvertices(r₂) && all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(r₁.vertices, r₂.vertices)) Base.close(r::Rope) = Ring(r.vertices) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index 8da928fa8..8ca2fed3e 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -26,8 +26,10 @@ function center(s::Segment) withdatum(s, (to(a) + to(b)) / 2) end -Base.isapprox(s₁::Segment, s₂::Segment; kwargs...) = - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(s₁.vertices, s₂.vertices)) +==(s₁::Segment, s₂::Segment) = s₁.vertices == s₂.vertices + +Base.isapprox(s₁::Segment, s₂::Segment; atol=atol(lentype(s₁)), kwargs...) = + all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(s₁.vertices, s₂.vertices)) function (s::Segment)(t) if t < 0 || t > 1 diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index 9c5164766..be1837f23 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -11,8 +11,10 @@ A tetrahedron with points `p1`, `p2`, `p3`, `p4`. nvertices(::Type{<:Tetrahedron}) = 4 -Base.isapprox(t₁::Tetrahedron, t₂::Tetrahedron; kwargs...) = - all(isapprox(v₁, v₂; kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) +==(t₁::Tetrahedron, t₂::Tetrahedron) = t₁.vertices == t₂.vertices + +Base.isapprox(t₁::Tetrahedron, t₂::Tetrahedron; atol=atol(lentype(t₁)), kwargs...) = + all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) function (t::Tetrahedron)(u, v, w) z = (1 - u - v - w) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index 345a6250b..d45906d80 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -29,6 +29,11 @@ center(b::Ball) = b.center radius(b::Ball) = b.radius +==(b₁::Ball, b₂::Ball) = b₁.center == b₂.center && b₁.radius == b₂.radius + +Base.isapprox(b₁::Ball, b₂::Ball; atol=atol(lentype(b₁)), kwargs...) = + isapprox(b₁.center, b₂.center; atol, kwargs...) && isapprox(b₁.radius, b₂.radius; atol, kwargs...) + function (b::Ball{2})(ρ, φ) T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 03f9c388c..598261f77 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -35,6 +35,12 @@ ncontrols(b::BezierCurve) = length(b.controls) degree(b::BezierCurve) = ncontrols(b) - 1 +==(b₁::BezierCurve, b₂::BezierCurve) = b₁.controls == b₂.controls + +Base.isapprox(b₁::BezierCurve, b₂::BezierCurve; atol=atol(lentype(b₁)), kwargs...) = + length(b₁.controls) == length(b₂.controls) && + all(isapprox(p₁, p₂; atol, kwargs...) for (p₁, p₂) in zip(b₁.controls, b₂.controls)) + """ Evaluation method used to obtain a point along a Bézier curve from a parametric expression. diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 5288ae264..1f7b512fc 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -43,7 +43,10 @@ diagonal(b::Box) = norm(b.max - b.min) sides(b::Box) = Tuple(b.max - b.min) -Base.isapprox(b₁::Box, b₂::Box) = b₁.min ≈ b₂.min && b₁.max ≈ b₂.max +==(b₁::Box, b₂::Box) = b₁.min == b₂.min && b₁.max == b₂.max + +Base.isapprox(b₁::Box, b₂::Box; atol=atol(lentype(b₁)), kwargs...) = + isapprox(b₁.min, b₂.min; atol, kwargs...) && isapprox(b₁.max, b₂.max; atol, kwargs...) function (b::Box)(uv...) if !all(x -> 0 ≤ x ≤ 1, uv) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index d0dc447b8..0edbd1d0d 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -47,6 +47,11 @@ center(c::Circle) = c.plane(0, 0) radius(c::Circle) = c.radius +==(c₁::Circle, c₂::Circle) = c₁.plane == c₂.plane && c₁.radius == c₂.radius + +Base.isapprox(c₁::Circle, c₂::Circle; atol=atol(lentype(c₁)), kwargs...) = + isapprox(c₁.plane, c₂.plane; atol, kwargs...) && isapprox(c₁.radius, c₂.radius; atol, kwargs...) + function (c::Circle)(φ) T = numtype(lentype(c)) if (φ < 0 || φ > 1) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 5aa805f5f..60c565ae1 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -30,4 +30,9 @@ height(c::Cone) = norm(center(base(c)) - apex(c)) halfangle(c::Cone) = atan(radius(base(c)), height(c)) +==(c₁::Cone, c₂::Cone) = boundary(c₁) == boundary(c₂) + +Base.isapprox(c₁::Cone, c₂::Cone; atol=atol(lentype(c₁)), kwargs...) = + isapprox(boundary(c₁), boundary(c₂); atol, kwargs...) + Random.rand(rng::Random.AbstractRNG, ::Type{Cone}) = Cone(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 0e51a0e8f..f59e14b6c 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -26,6 +26,11 @@ base(c::ConeSurface) = c.base apex(c::ConeSurface) = c.apex +==(c₁::ConeSurface, c₂::ConeSurface) = c₁.base == c₂.base && c₁.apex == c₂.apex + +Base.isapprox(c₁::ConeSurface, c₂::ConeSurface; atol=atol(lentype(c₁)), kwargs...) = + isapprox(c₁.base, c₂.base; atol, kwargs...) && isapprox(c₁.apex, c₂.apex; atol, kwargs...) + function (c::ConeSurface)(φ, h) T = numtype(lentype(c)) if (φ < 0 || φ > 1) || (h < 0 || h > 1) diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index 373a8cbac..4e354ba50 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -68,7 +68,10 @@ isright(c::Cylinder) = isright(boundary(c)) hasintersectingplanes(c::Cylinder) = hasintersectingplanes(boundary(c)) -Base.isapprox(c₁::Cylinder, c₂::Cylinder) = boundary(c₁) ≈ boundary(c₂) +==(c₁::Cylinder, c₂::Cylinder) = boundary(c₁) == boundary(c₂) + +Base.isapprox(c₁::Cylinder, c₂::Cylinder; atol=atol(lentype(c₁)), kwargs...) = + isapprox(boundary(c₁), boundary(c₂); atol, kwargs...) function (c::Cylinder)(ρ, φ, z) ℒ = lentype(c) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 867070cee..12d5c4224 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -82,8 +82,12 @@ function isright(c::CylinderSurface) isparallelv && isparallelw end -Base.isapprox(c₁::CylinderSurface, c₂::CylinderSurface) = - c₁.bot ≈ c₂.bot && c₁.top ≈ c₂.top && isapproxequal(c₁.radius, c₂.radius) +==(c₁::CylinderSurface, c₂::CylinderSurface) = c₁.bot == c₂.bot && c₁.top == c₂.top && c₁.radius == c₂.radius + +Base.isapprox(c₁::CylinderSurface, c₂::CylinderSurface; atol=atol(lentype(c₁)), kwargs...) = + isapprox(c₁.bot, c₂.bot; atol, kwargs...) && + isapprox(c₁.top, c₂.top; atol, kwargs...) && + isapprox(c₁.radius, c₂.radius; atol, kwargs...) function (c::CylinderSurface)(φ, z) ℒ = lentype(c) diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index b50acfa61..61e2c9b77 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -28,6 +28,11 @@ radius(d::Disk) = d.radius normal(d::Disk) = normal(d.plane) +==(d₁::Disk, d₂::Disk) = d₁.plane == d₂.plane && d₁.radius == d₂.radius + +Base.isapprox(d₁::Disk, d₂::Disk; atol=atol(lentype(d₁)), kwargs...) = + isapprox(d₁.plane, d₂.plane; atol, kwargs...) && isapprox(d₁.radius, d₂.radius; atol, kwargs...) + function (d::Disk)(ρ, φ) T = numtype(lentype(d)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index c9d1ad72a..7bdf7de65 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -30,8 +30,14 @@ center(e::Ellipsoid) = e.center rotation(e::Ellipsoid) = e.rotation -Base.isapprox(e₁::Ellipsoid, e₂::Ellipsoid) = - all(e₁.radii .≈ e₂.radii) && e₁.center ≈ e₂.center && e₁.rotation ≈ e₂.rotation +==(e₁::Ellipsoid, e₂::Ellipsoid) = e₁.radii == e₂.radii && e₁.center == e₂.center && e₁.rotation == e₂.rotation + +function Base.isapprox(e₁::Ellipsoid, e₂::Ellipsoid; atol=atol(lentype(e₁)), kwargs...) + u = Unitful.promote_unit(unit(lentype(e₁)), unit(lentype(e₂))) + all(isapprox(r₁, r₂; atol, kwargs...) for (r₁, r₂) in zip(e₁.radii, e₂.radii)) && + isapprox(e₁.center, e₂.center; atol, kwargs...) && + isapprox(e₁.rotation, e₂.rotation; atol=ustrip(u, atol), kwargs...) +end function (e::Ellipsoid)(θ, φ) T = numtype(lentype(e)) diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index 94475c249..d438d6035 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -36,6 +36,11 @@ height(f::Frustum) = height(boundary(f)) axis(f::Frustum) = axis(boundary(f)) +==(f₁::Frustum, f₂::Frustum) = boundary(f₁) == boundary(f₂) + +Base.isapprox(f₁::Frustum, f₂::Frustum; atol=atol(lentype(f₁)), kwargs...) = + isapprox(boundary(f₁), boundary(f₂); atol, kwargs...) + function Random.rand(rng::Random.AbstractRNG, ::Type{Frustum}) bottom = rand(rng, Disk) ax = normal(plane(bottom)) diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 820ea224e..4015cb06d 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -36,6 +36,11 @@ height(f::FrustumSurface) = norm(center(bottom(f)) - center(top(f))) axis(f::FrustumSurface) = Line(center(bottom(f)), center(top(f))) +==(f₁::FrustumSurface, f₂::FrustumSurface) = f₁.bot == f₂.bot && f₁.top == f₂.top + +Base.isapprox(f₁::FrustumSurface, f₂::FrustumSurface; atol=atol(lentype(f₁)), kwargs...) = + isapprox(f₁.bot, f₂.bot; atol, kwargs...) && isapprox(f₁.top, f₂.top; atol, kwargs...) + function (f::FrustumSurface)(φ, z) ℒ = lentype(f) T = numtype(ℒ) diff --git a/src/primitives/line.jl b/src/primitives/line.jl index 8bee80698..9d275f85b 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -20,6 +20,9 @@ paramdim(::Type{<:Line}) = 1 ==(l₁::Line, l₂::Line) = l₁.a ∈ l₂ && l₁.b ∈ l₂ && l₂.a ∈ l₁ && l₂.b ∈ l₁ +Base.isapprox(l₁::Line, l₂::Line; atol=atol(lentype(l₁)), kwargs...) = + isapprox(l₁.a, l₂.a; atol, kwargs...) && isapprox(l₁.b, l₂.b; atol, kwargs...) + (l::Line)(t) = l.a + t * (l.b - l.a) Random.rand(rng::Random.AbstractRNG, ::Type{Line{Dim}}) where {Dim} = Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index c046df63f..455862094 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -99,8 +99,13 @@ function centroid(p::ParaboloidSurface) c + Vec(x, y, z / 2) end -Base.isapprox(p₁::ParaboloidSurface, p₂::ParaboloidSurface) = - p₁.apex ≈ p₂.apex && isapproxequal(p₁.focallength, p₂.focallength) && isapproxequal(p₁.radius, p₂.radius) +==(p₁::ParaboloidSurface, p₂::ParaboloidSurface) = + p₁.apex == p₂.apex && p₁.radius == p₂.radius && p₁.focallength == p₂.focallength + +Base.isapprox(p₁::ParaboloidSurface, p₂::ParaboloidSurface; atol=atol(lentype(p₁)), kwargs...) = + isapprox(p₁.apex, p₂.apex; atol, kwargs...) && + isapprox(p₁.focallength, p₂.focallength; atol, kwargs...) && + isapprox(p₁.radius, p₂.radius; atol, kwargs...) function (p::ParaboloidSurface)(ρ, θ) T = numtype(lentype(p)) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 7203e85b5..5a0542443 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -43,10 +43,10 @@ normal(p::Plane) = unormalize(ucross(p.u, p.v)) ==(p₁::Plane, p₂::Plane) = p₁(0, 0) ∈ p₂ && p₁(1, 0) ∈ p₂ && p₁(0, 1) ∈ p₂ && p₂(0, 0) ∈ p₁ && p₂(1, 0) ∈ p₁ && p₂(0, 1) ∈ p₁ -Base.isapprox(p₁::Plane, p₂::Plane) = - isapproxzero(udot(p₁(0, 0) - p₂(0, 0), normal(p₂))) && - isapproxzero(udot(p₂(0, 0) - p₁(0, 0), normal(p₁))) && - isapproxzero(norm(ucross(normal(p₁), normal(p₂)))) +Base.isapprox(p₁::Plane, p₂::Plane; atol=atol(lentype(p₁)), kwargs...) = + isapproxzero(udot(p₁(0, 0) - p₂(0, 0), normal(p₂)); atol, kwargs...) && + isapproxzero(udot(p₂(0, 0) - p₁(0, 0), normal(p₁)); atol, kwargs...) && + isapproxzero(norm(ucross(normal(p₁), normal(p₂))); atol, kwargs...) (p::Plane)(u, v) = p.p + u * p.u + v * p.v diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 65c8e022d..067384a08 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -49,10 +49,9 @@ paramdim(::Type{<:Point}) = 0 center(p::Point) = p -==(A::Point, B::Point) = A.coords == B.coords +==(A::Point, B::Point) = to(A) == to(B) -Base.isapprox(A::Point, B::Point; atol=CoordRefSystems.tol(A.coords), kwargs...) = - isapprox(A.coords, B.coords; atol, kwargs...) +Base.isapprox(A::Point, B::Point; atol=atol(lentype(A)), kwargs...) = isapprox(to(A), to(B); atol, kwargs...) """ coords(point) diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index c5e1938ab..9899a7e73 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -18,7 +18,10 @@ Ray(p::Tuple, v::Tuple) = Ray(Point(p), Vec(v)) paramdim(::Type{<:Ray}) = 1 -==(r₁::Ray, r₂::Ray) = (r₁.p ≈ r₂.p) && (r₁.p + r₁.v) ∈ r₂ && (r₂.p + r₂.v) ∈ r₁ +==(r₁::Ray, r₂::Ray) = r₁.p == r₂.p && (r₁.p + r₁.v) ∈ r₂ && (r₂.p + r₂.v) ∈ r₁ + +Base.isapprox(r₁::Ray, r₂::Ray; atol=atol(lentype(r₁)), kwargs...) = + isapprox(r₁.p, r₂.p; atol, kwargs...) && isapprox(r₁.v, r₂.v; atol, kwargs...) function (r::Ray)(t) if t < 0 diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index f2ef87cab..2b04e3f12 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -66,7 +66,10 @@ center(s::Sphere) = s.center radius(s::Sphere) = s.radius -Base.isapprox(s₁::Sphere, s₂::Sphere) = s₁.center ≈ s₂.center && s₁.radius ≈ s₂.radius +==(s₁::Sphere, s₂::Sphere) = s₁.center == s₂.center && s₁.radius == s₂.radius + +Base.isapprox(s₁::Sphere, s₂::Sphere; atol=atol(lentype(s₁)), kwargs...) = + isapprox(s₁.center, s₂.center; atol, kwargs...) && isapprox(s₁.radius, s₂.radius; atol, kwargs...) function (s::Sphere{2})(φ) T = numtype(lentype(s)) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 95875ab75..27dcbbc79 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -54,6 +54,15 @@ radii(t::Torus) = (t.major, t.minor) axis(t::Torus) = Line(t.center, t.center + t.normal) +==(t₁::Torus, t₂::Torus) = + t₁.center == t₂.center && t₁.normal == t₂.normal && t₁.major == t₂.major && t₁.minor == t₂.minor + +Base.isapprox(t₁::Torus, t₂::Torus; atol=atol(lentype(t₁)), kwargs...) = + isapprox(t₁.center, t₂.center; atol, kwargs...) && + isapprox(t₁.normal, t₂.normal; atol, kwargs...) && + isapprox(t₁.major, t₂.major; atol, kwargs...) && + isapprox(t₁.minor, t₂.minor; atol, kwargs...) + function (t::Torus)(θ, φ) ℒ = lentype(t) T = numtype(ℒ) diff --git a/src/utils.jl b/src/utils.jl index 24116fc28..5d7571dbb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -178,9 +178,9 @@ Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. Expr(:tuple, exprs...) end -isapproxequal(x, y) = isapprox(x, y, atol=atol(x)) -isapproxzero(x) = isapprox(x, zero(x), atol=atol(x)) -isapproxone(x) = isapprox(x, oneunit(x), atol=atol(x)) +isapproxequal(x, y; atol=atol(x), kwargs...) = isapprox(x, y; atol, kwargs...) +isapproxzero(x; atol=atol(x), kwargs...) = isapprox(x, zero(x); atol, kwargs...) +isapproxone(x; atol=atol(x), kwargs...) = isapprox(x, oneunit(x); atol, kwargs...) ispositive(x) = x > zero(x) isnegative(x) = x < zero(x) @@ -202,8 +202,11 @@ uinv(A) = inv(ustrip.(A)) * unit(eltype(A))^-1 unormalize(a::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(normalize(a) * unit(ℒ)) udot(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = ustrip(a ⋅ b) * unit(ℒ) +udot(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = udot(promote(a, b)...) ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b) * unit(ℒ)) +ucross(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = ucross(promote(a, b)...) + ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}, c::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b × c) * unit(ℒ)) urotbetween(u::Vec, v::Vec) = rotation_between(ustrip.(u), ustrip.(v)) diff --git a/src/vectors.jl b/src/vectors.jl index f296e5272..42c6ee49d 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -51,6 +51,7 @@ Vec(coords::Number...) = Vec(coords) # StaticVector interface Base.Tuple(v::Vec) = getfield(v, :coords) Base.getindex(v::Vec, i::Int) = getindex(getfield(v, :coords), i) +Base.promote_rule(::Type{Vec{Dim,ℒ₁}}, ::Type{Vec{Dim,ℒ₂}}) where {Dim,ℒ₁,ℒ₂} = Vec{Dim,promote_type(ℒ₁,ℒ₂)} function StaticArrays.similar_type(::Type{<:Vec}, ::Type{T}, ::Size{S}) where {T,S} L = prod(S) N = length(S) diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 9a26295ae..a48d771fd 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -47,6 +47,12 @@ ├─ Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)) └─ Box(min: (x: 1.0 m, y: 1.0 m), max: (x: 2.0 m, y: 2.0 m))""" + box1 = Box(point(0, 0), point(1, 1)) + box2 = Box(point(1, 1), point(2, 2)) + mbox = Multi([box1, box2]) + equaltest(mbox) + isapproxtest(mbox) + # constructor with iterator grid = cartgrid(10, 10) multi = Multi(grid) diff --git a/test/polytopes.jl b/test/polytopes.jl index f19d2ad98..5ae1f775b 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -37,6 +37,10 @@ @test !(s ≈ Segment(point(1, 1, 1), point(0, 0, 0))) @test !(s ≈ Segment(point(1, 1, 1), point(0, 1, 0))) + s = Segment(point(0, 0), point(1, 1)) + equaltest(s) + isapproxtest(s) + s = Segment(Point(1.0, 1.0, 1.0, 1.0), Point(2.0, 2.0, 2.0, 2.0)) @test all(Point(x, x, x, x) ∈ s for x in 1:0.01:2) @test all(p ∉ s for p in [Point(0.99, 0.99, 0.99, 0.99), Point(2.1, 2.1, 2.1, 2.1)]) @@ -101,6 +105,14 @@ c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 + c = Rope(point(0, 0), point(1, 0), point(0, 1)) + equaltest(c) + isapproxtest(c) + + c = Ring(point(0, 0), point(1, 0), point(0, 1)) + equaltest(c) + isapproxtest(c) + # circular equality c1 = Ring(point.([(1, 1), (2, 2), (3, 3)])) c2 = Ring(point.([(2, 2), (3, 3), (1, 1)])) @@ -366,6 +378,10 @@ @test rings(t) == [Ring(point(0, 0), point(1, 0), point(0, 1))] @test convexhull(t) == t + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + equaltest(t) + isapproxtest(t) + t = Triangle(point(0, 0), point(1, 0), point(0, 1)) @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" @@ -442,9 +458,6 @@ # QUADRANGLE # ----------- - # test periodicity of Quadrangle - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - # Quadrangle in 2D space q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test Meshes.crs(q) <: Cartesian{NoDatum} @@ -473,6 +486,10 @@ @test q(T(1), T(1)) == point(1, 1) @test q(T(0), T(1)) == point(0, 1) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + equaltest(q) + isapproxtest(q) + q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) @@ -529,6 +546,10 @@ @test Meshes.crs(poly) <: Cartesian{NoDatum} @test Meshes.lentype(poly) == ℳ + p = PolyArea(point(0, 0), point(1, 0), point(0, 1)) + equaltest(p) + isapproxtest(p) + # outer chain with 2 vertices is fixed by default poly = PolyArea(point.([(0, 0), (1, 0)])) @test rings(poly) == [Ring(point.([(0, 0), (0.5, 0.0), (1, 0)]))] @@ -753,6 +774,10 @@ @test t(T(0), T(0), T(1)) ≈ point(0, 0, 1) @test_throws DomainError((T(1), T(1), T(1)), "invalid barycentric coordinates for tetrahedron.") t(T(1), T(1), T(1)) + t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + equaltest(t) + isapproxtest(t) + t = rand(Tetrahedron{3}) @test t isa Tetrahedron @test embeddim(t) == 3 @@ -810,6 +835,19 @@ @test h(T(1), T(1), T(0)) == point(1, 1, 0) @test h(T(1), T(1), T(1)) == point(1, 1, 1) + h = Hexahedron( + point(0, 0, 0), + point(1, 0, 0), + point(1, 1, 0), + point(0, 1, 0), + point(0, 0, 1), + point(1, 0, 1), + point(1, 1, 1), + point(0, 1, 1) + ) + equaltest(h) + isapproxtest(h) + h = Hexahedron( point(0, 0, 0), point(1, 0, 0), @@ -931,6 +969,10 @@ @test m[4] isa Triangle @test m[5] isa Triangle + p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + equaltest(p) + isapproxtest(p) + p = rand(Pyramid{3}) @test p isa Pyramid @test embeddim(p) == 3 diff --git a/test/primitives.jl b/test/primitives.jl index 85a1cc3d2..277ad70fa 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -12,6 +12,13 @@ @test Meshes.lentype(Point((T(1), T(1)))) == ℳ @test Meshes.lentype(Point(T(1), T(1))) == ℳ + equaltest(point(1)) + equaltest(point(1, 2)) + equaltest(point(1, 2, 3)) + isapproxtest(point(1)) + isapproxtest(point(1, 2)) + isapproxtest(point(1, 2, 3)) + @test to(point(1)) == vector(1) @test to(point(1, 2)) == vector(1, 2) @test to(point(1, 2, 3)) == vector(1, 2, 3) @@ -190,6 +197,10 @@ @test boundary(r) == point(0, 0) @test perimeter(r) == zero(ℳ) + r = Ray(point(0, 0), vector(1, 1)) + equaltest(r) + isapproxtest(r) + r = Ray(point(0, 0), vector(1, 1)) @test r(T(0.0)) == point(0, 0) @test r(T(1.0)) == point(1, 1) @@ -253,6 +264,10 @@ @test isnothing(boundary(l)) @test perimeter(l) == zero(ℳ) + l = Line(point(0, 0), point(1, 1)) + equaltest(l) + isapproxtest(l) + l = Line(point(0, 0), point(1, 1)) @test (l(0), l(1)) == (point(0, 0), point(1, 1)) @@ -292,6 +307,10 @@ @test isnothing(boundary(p)) @test perimeter(p) == zero(ℳ) + p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + equaltest(p) + isapproxtest(p) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) @test p(T(1), T(0)) == point(1, 0, 0) @test p(T(0), T(1)) == point(0, 1, 0) @@ -351,6 +370,10 @@ @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ + b = BezierCurve(point(0, 0), point(1, 1)) + equaltest(b) + isapproxtest(b) + b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) for method in [DeCasteljau(), Horner()] @test b(T(0), method) == point(0, 0) @@ -429,6 +452,10 @@ @test maximum(b) == point(1, 1, 1) @test extrema(b) == (point(0, 0, 0), point(1, 1, 1)) + b = Box(point(0, 0), point(1, 1)) + equaltest(b) + isapproxtest(b) + b = Box(point(0), point(1)) @test boundary(b) == Multi([point(0), point(1)]) @test measure(b) == T(1) * u"m" @@ -550,6 +577,10 @@ @test Meshes.center(b) == point(1, 2, 3) @test radius(b) == T(5) * u"m" + b = Ball(point(0, 0), T(1)) + equaltest(b) + isapproxtest(b) + b = Ball(point(1, 2, 3), 4) @test Meshes.lentype(b) == ℳ @@ -633,6 +664,10 @@ @test isnothing(boundary(s)) @test perimeter(s) == zero(ℳ) + s = Sphere(point(0, 0), T(1)) + equaltest(s) + isapproxtest(s) + s = Sphere(point(1, 2, 3), 4) @test Meshes.lentype(s) == ℳ @@ -737,6 +772,10 @@ @test isnothing(boundary(e)) @test perimeter(e) == zero(ℳ) + e = Ellipsoid((T(3), T(2), T(1))) + equaltest(e) + isapproxtest(e) + e = Ellipsoid((T(3), T(2), T(1))) @test sprint(show, e) == "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" @@ -772,6 +811,11 @@ @test point(0, 0, 1) ∉ d @test boundary(d) == Circle(p, T(2)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + equaltest(d) + isapproxtest(d) + d = rand(Disk) @test d isa Disk @test embeddim(d) == 3 @@ -810,6 +854,11 @@ @test point(0, 0, 0) ∉ c @test isnothing(boundary(c)) + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + c = Circle(p, T(2)) + equaltest(c) + isapproxtest(c) + # 3D circumcircle p1 = point(0, 4, 0) p2 = point(0, -4, 0) @@ -878,6 +927,10 @@ @test c(1, 0.25, 0.5) ≈ Point(T(4.330127018922193), T(10.330127018922191), T(4.5)) @test_throws DomainError c(1.1, 0, 0) + c = Cylinder(T(1)) + equaltest(c) + isapproxtest(c) + c = Cylinder(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) @@ -950,6 +1003,10 @@ @test measure(c) == area(c) ≈ (2 * T(2)^2 * pi + 2 * T(2) * pi) * u"m^2" @test !Meshes.hasintersectingplanes(c) + c = CylinderSurface(T(1)) + equaltest(c) + isapproxtest(c) + c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) @@ -1015,6 +1072,10 @@ @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" @test centroid(p) == point(0, 0, 1 / 16) + p = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + equaltest(p) + isapproxtest(p) + p1 = ParaboloidSurface(point(1, 2, 3), T(1), T(1)) p2 = ParaboloidSurface(point(1, 2, 3), T(1)) p3 = ParaboloidSurface(point(1, 2, 3)) @@ -1093,6 +1154,13 @@ @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = point(0, 0, 1) + c = Cone(d, a) + equaltest(c) + isapproxtest(c) + c = rand(Cone) @test c isa Cone @test embeddim(c) == 3 @@ -1164,6 +1232,13 @@ @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ + p = Plane(point(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = point(0, 0, 1) + c = ConeSurface(d, a) + equaltest(c) + isapproxtest(c) + c = rand(ConeSurface) @test c isa ConeSurface @test embeddim(c) == 3 @@ -1200,6 +1275,14 @@ @test_throws AssertionError Frustum(db, db) + pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = Frustum(db, dt) + equaltest(f) + isapproxtest(f) + f = rand(Frustum) @test f isa Frustum @@ -1244,6 +1327,14 @@ @test_throws AssertionError FrustumSurface(db, db) + pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = FrustumSurface(db, dt) + equaltest(f) + isapproxtest(f) + f = rand(FrustumSurface) @test f isa FrustumSurface end @@ -1263,6 +1354,10 @@ @test_throws ArgumentError length(t) @test_throws ArgumentError volume(t) + t = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + equaltest(t) + isapproxtest(t) + # torus passing through three points p₁ = point(0, 0, 0) p₂ = point(1, 2, 3) diff --git a/test/runtests.jl b/test/runtests.jl index 710066dce..e88e286b0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,70 +25,21 @@ islinux = Sys.islinux() visualtests = !isCI || (isCI && islinux) datadir = joinpath(@__DIR__, "data") -# helper function to read *.line files containing polygons -# generated with RPG (https://github.com/cgalab/genpoly-rpg) -function readpoly(T, fname) - open(fname, "r") do f - # read outer chain - n = parse(Int, readline(f)) - outer = map(1:n) do _ - coords = readline(f) - x, y = parse.(T, split(coords)) - Point(x, y) - end - - # read inner chains - inners = [] - while !eof(f) - n = parse(Int, readline(f)) - inner = map(1:n) do _ - coords = readline(f) - x, y = parse.(T, split(coords)) - Point(x, y) - end - push!(inners, inner) - end - - # return polygonal area - @assert first(outer) == last(outer) - @assert all(first(i) == last(i) for i in inners) - rings = [outer, inners...] - PolyArea([r[begin:(end - 1)] for r in rings]) - end -end - -# helper function to read *.ply files containing meshes -function readply(T, fname) - ply = load_ply(fname) - x = T.(ply["vertex"]["x"]) - y = T.(ply["vertex"]["y"]) - z = T.(ply["vertex"]["z"]) - points = Point.(x, y, z) - connec = [connect(Tuple(c .+ 1)) for c in ply["face"]["vertex_indices"]] - SimpleMesh(points, connec) -end +# dummy definitions +include("dummy.jl") -point(coords...) = point(coords) -point(coords::Tuple) = Point(T.(coords)) +# helper functions +include("testutils.jl") -vector(coords...) = vector(coords) -vector(coords::Tuple) = Vec(T.(coords)) +point(args...) = point(T, args...) -cartgrid(dims...) = cartgrid(dims) -function cartgrid(dims::Dims{Dim}) where {Dim} - origin = ntuple(i -> T(0.0), Dim) - spacing = ntuple(i -> T(1.0), Dim) - offset = ntuple(i -> 1, Dim) - CartesianGrid(dims, origin, spacing, offset) -end +vector(args...) = vector(T, args...) -randpoint1(n) = randpoint(1, n) -randpoint2(n) = randpoint(2, n) -randpoint3(n) = randpoint(3, n) -randpoint(Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] +cartgrid(args...) = cartgrid(T, args...) -# dummy definitions -include("dummy.jl") +randpoint1(n) = randpoint(T, 1, n) +randpoint2(n) = randpoint(T, 2, n) +randpoint3(n) = randpoint(T, 3, n) # list of tests testfiles = [ diff --git a/test/testutils.jl b/test/testutils.jl new file mode 100644 index 000000000..1a95004c6 --- /dev/null +++ b/test/testutils.jl @@ -0,0 +1,113 @@ +# helper function to read *.line files containing polygons +# generated with RPG (https://github.com/cgalab/genpoly-rpg) +function readpoly(T, fname) + open(fname, "r") do f + # read outer chain + n = parse(Int, readline(f)) + outer = map(1:n) do _ + coords = readline(f) + x, y = parse.(T, split(coords)) + Point(x, y) + end + + # read inner chains + inners = [] + while !eof(f) + n = parse(Int, readline(f)) + inner = map(1:n) do _ + coords = readline(f) + x, y = parse.(T, split(coords)) + Point(x, y) + end + push!(inners, inner) + end + + # return polygonal area + @assert first(outer) == last(outer) + @assert all(first(i) == last(i) for i in inners) + rings = [outer, inners...] + PolyArea([r[begin:(end - 1)] for r in rings]) + end +end + +# helper function to read *.ply files containing meshes +function readply(T, fname) + ply = load_ply(fname) + x = T.(ply["vertex"]["x"]) + y = T.(ply["vertex"]["y"]) + z = T.(ply["vertex"]["z"]) + points = Point.(x, y, z) + connec = [connect(Tuple(c .+ 1)) for c in ply["face"]["vertex_indices"]] + SimpleMesh(points, connec) +end + +point(T::Type, coords...) = point(T, coords) +point(T::Type, coords::Tuple) = Point(T.(coords)) + +vector(T::Type, coords...) = vector(T, coords) +vector(T::Type, coords::Tuple) = Vec(T.(coords)) + +cartgrid(T::Type, dims...) = cartgrid(T, dims) +function cartgrid(T::Type, dims::Dims{Dim}) where {Dim} + origin = ntuple(i -> T(0.0), Dim) + spacing = ntuple(i -> T(1.0), Dim) + offset = ntuple(i -> 1, Dim) + CartesianGrid(dims, origin, spacing, offset) +end + +randpoint(T, Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] + +numconvert(T, x::Quantity{S,D,U}) where {S,D,U} = convert(Quantity{T,D,U}, x) + +withprecision(_, x) = x +withprecision(T, v::Vec) = numconvert.(T, v) +withprecision(T, p::Point) = Meshes.withdatum(p, withprecision(T, to(p))) +withprecision(T, len::Meshes.Len) = numconvert(T, len) +withprecision(T, lens::NTuple{Dim,Meshes.Len}) where {Dim} = numconvert.(T, lens) +withprecision(T, geoms::NTuple{Dim,<:Geometry}) where {Dim} = withprecision.(T, geoms) +withprecision(T, geoms::AbstractVector{<:Geometry}) = [withprecision(T, g) for g in geoms] +withprecision(T, geoms::CircularVector{<:Geometry}) = CircularVector([withprecision(T, g) for g in geoms]) +@generated function withprecision(T, g::G) where {G<:Meshes.GeometryOrDomain} + ctor = Meshes.constructor(G) + names = fieldnames(G) + exprs = (:(withprecision(T, g.$name)) for name in names) + :($ctor($(exprs...))) +end + +function equaltest(g) + @test g == withprecision(Float64, g) + @test g == withprecision(Float32, g) +end + +function isapproxtest(g::Geometry{1}) + τ64 = Meshes.atol(Float64) * u"m" + τ32 = Meshes.atol(Float32) * u"m" + g64 = withprecision(Float64, g) + g32 = withprecision(Float32, g) + @test isapprox(g, Translate(τ64)(g64), atol=1.1τ64) + @test isapprox(g, Translate(τ32)(g32), atol=1.1τ32) +end + +function isapproxtest(g::Geometry{2}) + τ64 = Meshes.atol(Float64) * u"m" + τ32 = Meshes.atol(Float32) * u"m" + g64 = withprecision(Float64, g) + g32 = withprecision(Float32, g) + @test isapprox(g, Translate(τ64, 0u"m")(g64), atol=1.1τ64) + @test isapprox(g, Translate(0u"m", τ64)(g64), atol=1.1τ64) + @test isapprox(g, Translate(τ32, 0u"m")(g32), atol=1.1τ32) + @test isapprox(g, Translate(0u"m", τ32)(g32), atol=1.1τ32) +end + +function isapproxtest(g::Geometry{3}) + τ64 = Meshes.atol(Float64) * u"m" + τ32 = Meshes.atol(Float32) * u"m" + g64 = withprecision(Float64, g) + g32 = withprecision(Float32, g) + @test isapprox(g, Translate(τ64, 0u"m", 0u"m")(g64), atol=1.1τ64) + @test isapprox(g, Translate(0u"m", τ64, 0u"m")(g64), atol=1.1τ64) + @test isapprox(g, Translate(0u"m", 0u"m", τ64)(g64), atol=1.1τ64) + @test isapprox(g, Translate(τ32, 0u"m", 0u"m")(g32), atol=1.1τ32) + @test isapprox(g, Translate(0u"m", τ32, 0u"m")(g32), atol=1.1τ32) + @test isapprox(g, Translate(0u"m", 0u"m", τ32)(g32), atol=1.1τ32) +end From 7d94e704c423dbbd109479cfbf05a6b27b7303af Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 09:27:01 -0300 Subject: [PATCH 121/423] :robot: Format .jl files (#904) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/primitives/ellipsoid.jl | 4 ++-- src/vectors.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 7bdf7de65..1ddfb5d61 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -35,8 +35,8 @@ rotation(e::Ellipsoid) = e.rotation function Base.isapprox(e₁::Ellipsoid, e₂::Ellipsoid; atol=atol(lentype(e₁)), kwargs...) u = Unitful.promote_unit(unit(lentype(e₁)), unit(lentype(e₂))) all(isapprox(r₁, r₂; atol, kwargs...) for (r₁, r₂) in zip(e₁.radii, e₂.radii)) && - isapprox(e₁.center, e₂.center; atol, kwargs...) && - isapprox(e₁.rotation, e₂.rotation; atol=ustrip(u, atol), kwargs...) + isapprox(e₁.center, e₂.center; atol, kwargs...) && + isapprox(e₁.rotation, e₂.rotation; atol=ustrip(u, atol), kwargs...) end function (e::Ellipsoid)(θ, φ) diff --git a/src/vectors.jl b/src/vectors.jl index 42c6ee49d..54b61da21 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -51,7 +51,7 @@ Vec(coords::Number...) = Vec(coords) # StaticVector interface Base.Tuple(v::Vec) = getfield(v, :coords) Base.getindex(v::Vec, i::Int) = getindex(getfield(v, :coords), i) -Base.promote_rule(::Type{Vec{Dim,ℒ₁}}, ::Type{Vec{Dim,ℒ₂}}) where {Dim,ℒ₁,ℒ₂} = Vec{Dim,promote_type(ℒ₁,ℒ₂)} +Base.promote_rule(::Type{Vec{Dim,ℒ₁}}, ::Type{Vec{Dim,ℒ₂}}) where {Dim,ℒ₁,ℒ₂} = Vec{Dim,promote_type(ℒ₁, ℒ₂)} function StaticArrays.similar_type(::Type{<:Vec}, ::Type{T}, ::Size{S}) where {T,S} L = prod(S) N = length(S) From a6ca3c5cb28c59e0456d2b272d18fa0c0af59a3e Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:32:49 -0300 Subject: [PATCH 122/423] Fix `VoronoiTesselation` implementation (#905) * Fix 'VoronoiTesselation' implementation * Apply suggestions --- src/tesselation/voronoi.jl | 8 +++++++- test/tesselation.jl | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index 63ceba7ea..effd2b2f5 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -20,13 +20,19 @@ end VoronoiTesselation(rng=Random.default_rng()) = VoronoiTesselation(rng) function tesselate(pset::PointSet{2}, method::VoronoiTesselation) + d = datum(crs(pset)) + u = unit(lentype(pset)) + # perform tesselation with raw coordinates coords = map(p -> ustrip.(to(p)), pset) triang = triangulate(coords, rng=method.rng) vorono = voronoi(triang, clip=true) # mesh with all (possibly unused) points - points = get_polygon_points(vorono) + points = map(get_polygon_points(vorono)) do (x, y) + coords = Cartesian{d}(x * u, y * u) + Point(coords) + end polygs = each_polygon(vorono) tuples = [Tuple(inds[begin:(end - 1)]) for inds in polygs] connec = connect.(tuples) diff --git a/test/tesselation.jl b/test/tesselation.jl index 856bbcf7e..7b9593563 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -1,17 +1,31 @@ @testset "Tesselation" begin @testset "Delaunay" begin - pts = randpoint2(100) + pts = randpoint2(10) pset = PointSet(pts) mesh1 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) mesh2 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) @test mesh1 == mesh2 + + # datum and unit propagation + tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] + pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) + mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test datum(Meshes.crs(mesh)) === WGS84Latest + @test unit(Meshes.lentype(mesh)) == u"km" end @testset "Voronoi" begin - pts = randpoint2(100) + pts = randpoint2(10) pset = PointSet(pts) mesh1 = tesselate(pts, VoronoiTesselation(StableRNG(2024))) mesh2 = tesselate(pset, VoronoiTesselation(StableRNG(2024))) @test mesh1 == mesh2 + + # datum and unit propagation + tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] + pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) + mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) + @test datum(Meshes.crs(mesh)) === WGS84Latest + @test unit(Meshes.lentype(mesh)) == u"km" end end From e2b9404a44743122b4129c4892373fb789a8a881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 20 Jun 2024 17:32:02 -0300 Subject: [PATCH 123/423] Remove font asset in docs --- docs/make.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 1e87a8812..fcc7145e2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,10 +4,7 @@ prettyurls = get(ENV, "CI", nothing) == "true" makedocs( format=Documenter.HTML( - assets=[ - "assets/favicon.ico", - asset("https://fonts.googleapis.com/css?family=Montserrat|Source+Code+Pro&display=swap", class=:css) - ], + assets=["assets/favicon.ico"], prettyurls=prettyurls, mathengine=KaTeX( Dict( From 006386c4fb83214f48d05873d2fb9eb535eef0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 20 Jun 2024 17:40:35 -0300 Subject: [PATCH 124/423] Update docs/make.jl --- docs/make.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index fcc7145e2..1ebe79386 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,7 +4,6 @@ prettyurls = get(ENV, "CI", nothing) == "true" makedocs( format=Documenter.HTML( - assets=["assets/favicon.ico"], prettyurls=prettyurls, mathengine=KaTeX( Dict( From 5e64387d1ee6d0d0e9b56cbf392fdfaa54d402b8 Mon Sep 17 00:00:00 2001 From: cserteGT3 Date: Fri, 21 Jun 2024 15:41:58 +0200 Subject: [PATCH 125/423] Export and document inverse function for transforms (#838) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * export inverse function for transforms * mention inverse transformations in docs * Apply suggestions from code review --------- Co-authored-by: Júlio Hoffimann --- docs/src/transforms.md | 8 ++++++++ src/Meshes.jl | 2 ++ 2 files changed, 10 insertions(+) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index e5e1f23f3..f17e35893 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -14,6 +14,14 @@ GeometricTransform CoordinateTransform ``` +Some transforms have an inverse that can be created with the [`inverse`](@ref) function. +The function [`isinvertible`](@ref) can be used to check if a transform is invertible. + +```@docs +inverse +isinvertible +``` + ## Rotate ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index 93407f793..f27826a3f 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -523,6 +523,8 @@ export LaplaceSmoothing, TaubinSmoothing, isaffine, + isinvertible, + inverse, # miscellaneous signarea, From 84007cb7b2dc9c241482a518dc96ef407ae3a051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 21 Jun 2024 10:42:30 -0300 Subject: [PATCH 126/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8dfa0dbc2..5c898a941 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.45.0" +version = "0.45.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 9cbd277bc10f1fd8941a9c18f0f320b5aaa301a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 21 Jun 2024 14:34:36 -0300 Subject: [PATCH 127/423] clean docs/make.jl --- docs/make.jl | 22 ++-------------------- docs/src/index.md | 4 ++-- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 1ebe79386..51a99958f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,27 +1,9 @@ using Documenter, Meshes -prettyurls = get(ENV, "CI", nothing) == "true" - makedocs( + warnonly=[:missing_docs, :cross_references], format=Documenter.HTML( - prettyurls=prettyurls, - mathengine=KaTeX( - Dict( - :macros => Dict( - "\\x" => "\\boldsymbol{x}", - "\\z" => "\\boldsymbol{z}", - "\\l" => "\\boldsymbol{\\lambda}", - "\\c" => "\\boldsymbol{c}", - "\\C" => "\\boldsymbol{C}", - "\\g" => "\\boldsymbol{g}", - "\\G" => "\\boldsymbol{G}", - "\\f" => "\\boldsymbol{f}", - "\\F" => "\\boldsymbol{F}", - "\\R" => "\\mathbb{R}", - "\\1" => "\\mathbb{1}" - ) - ) - ) + prettyurls=get(ENV, "CI", nothing) == "true", ), sitename="Meshes.jl", authors="Júlio Hoffimann and contributors", diff --git a/docs/src/index.md b/docs/src/index.md index 88ae58b03..1d494a8eb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -13,8 +13,8 @@ [Meshes.jl](https://github.com/JuliaGeometry/Meshes.jl) provides efficient implementations of concepts from computational geometry. It promotes rigorous mathematical definitions of spatial discretizations (a.k.a. meshes) that are -adequate for describing general manifolds embedded in $\R^n$, including surfaces -described with spherical coordinates, and geometries described with multiple +adequate for describing general manifolds embedded in $\mathbb{R}^3$, including +surfaces described with spherical coordinates, and geometries described with multiple coordinate reference systems. Unlike other existing efforts in the Julia ecosystem, this project is being carefully From 640e0cdefa28373814574be4bb9120bbdbbfd551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 21 Jun 2024 14:34:58 -0300 Subject: [PATCH 128/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5c898a941..63c63908c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.45.1" +version = "0.45.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 90c133945f20be12ca127db0361853b845671955 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:42:41 -0300 Subject: [PATCH 129/423] :robot: Format .jl files (#906) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- docs/make.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 51a99958f..79fc8f234 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,9 +2,7 @@ using Documenter, Meshes makedocs( warnonly=[:missing_docs, :cross_references], - format=Documenter.HTML( - prettyurls=get(ENV, "CI", nothing) == "true", - ), + format=Documenter.HTML(prettyurls=get(ENV, "CI", nothing) == "true"), sitename="Meshes.jl", authors="Júlio Hoffimann and contributors", pages=[ From 20eb2a24fd763e32e76afb3215778511bddacdc7 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:13:49 -0300 Subject: [PATCH 130/423] Update to CoordRefSystems.jl v0.8 & Refactor tesselation (#908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update to CoordRefSystems.jl v0.8 & Refactor tesselation * Apply suggestions * Apply suggestions * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: Júlio Hoffimann --- Project.toml | 2 +- src/primitives/point.jl | 8 ++++---- src/tesselation/delaunay.jl | 8 +++++--- src/tesselation/voronoi.jl | 15 ++++++++------- src/transforms/lengthunit.jl | 2 +- test/tesselation.jl | 30 ++++++++++++++++++++++++------ 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Project.toml b/Project.toml index 63c63908c..a14b197b0 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.7" +CoordRefSystems = "0.8" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 067384a08..ea682c272 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -65,7 +65,7 @@ coords(A::Point) = A.coords Return the vector from the origin to the `point`. """ -to(A::Point) = Vec(CoordRefSystems.cvalues(convert(Cartesian, A.coords))) +to(A::Point) = Vec(CoordRefSystems.values(convert(Cartesian, A.coords))) """ -(A::Point, B::Point) @@ -139,9 +139,9 @@ function Base.show(io::IO, point::Point) else print(io, "Point(") end - cvalues = CoordRefSystems.cvalues(point.coords) - cnames = CoordRefSystems.cnames(point.coords) - printfields(io, cvalues, cnames, singleline=true) + values = CoordRefSystems.values(point.coords) + names = CoordRefSystems.names(point.coords) + printfields(io, values, names, singleline=true) print(io, ")") end diff --git a/src/tesselation/delaunay.jl b/src/tesselation/delaunay.jl index bd5e484f0..601fae160 100644 --- a/src/tesselation/delaunay.jl +++ b/src/tesselation/delaunay.jl @@ -19,10 +19,12 @@ end DelaunayTesselation(rng=Random.default_rng()) = DelaunayTesselation(rng) -function tesselate(pset::PointSet{2}, method::DelaunayTesselation) +function tesselate(pset::PointSet, method::DelaunayTesselation) + assertion(CoordRefSystems.ncoords(crs(pset)) == 2, "points must have 2 coordinates") + # perform tesselation with raw coordinates - coords = map(p -> ustrip.(to(p)), pset) - triang = triangulate(coords, rng=method.rng) + rawval = map(p -> CoordRefSystems.rawvalues(coords(p)), pset) + triang = triangulate(rawval, rng=method.rng) connec = connect.(each_solid_triangle(triang)) SimpleMesh(collect(pset), connec) end diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index effd2b2f5..e393f9851 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -19,18 +19,19 @@ end VoronoiTesselation(rng=Random.default_rng()) = VoronoiTesselation(rng) -function tesselate(pset::PointSet{2}, method::VoronoiTesselation) - d = datum(crs(pset)) - u = unit(lentype(pset)) +function tesselate(pset::PointSet, method::VoronoiTesselation) + C = crs(pset) + T = numtype(lentype(pset)) + assertion(CoordRefSystems.ncoords(C) == 2, "points must have 2 coordinates") # perform tesselation with raw coordinates - coords = map(p -> ustrip.(to(p)), pset) - triang = triangulate(coords, rng=method.rng) + rawval = map(p -> CoordRefSystems.rawvalues(coords(p)), pset) + triang = triangulate(rawval, rng=method.rng) vorono = voronoi(triang, clip=true) # mesh with all (possibly unused) points - points = map(get_polygon_points(vorono)) do (x, y) - coords = Cartesian{d}(x * u, y * u) + points = map(get_polygon_points(vorono)) do xy + coords = CoordRefSystems.reconstruct(C, T.(xy)) Point(coords) end polygs = each_polygon(vorono) diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index 207f282fc..8ab39c91a 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -26,7 +26,7 @@ applycoord(t::LengthUnit, p::Point) = Point(_lenunit(coords(p), t.unit)) function _lenunit(c::Cartesian, u) d = datum(c) - v = CoordRefSystems.cvalues(c) + v = CoordRefSystems.values(c) Cartesian{d}(uconvert.(u, v)) end diff --git a/test/tesselation.jl b/test/tesselation.jl index 7b9593563..dacb5d506 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -6,12 +6,21 @@ mesh2 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) @test mesh1 == mesh2 - # datum and unit propagation + # CRS propagation tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test datum(Meshes.crs(mesh)) === WGS84Latest - @test unit(Meshes.lentype(mesh)) == u"km" + @test Meshes.crs(mesh) === Meshes.crs(pset) + + coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] + pset = PointSet(Point.(coords)) + mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test Meshes.crs(mesh) === Meshes.crs(pset) + + # error: the number of coordinates of the points must be 2 + pts = randpoint3(10) + pset = PointSet(pts) + @test_throws AssertionError tesselate(pset, DelaunayTesselation(StableRNG(2024))) end @testset "Voronoi" begin @@ -21,11 +30,20 @@ mesh2 = tesselate(pset, VoronoiTesselation(StableRNG(2024))) @test mesh1 == mesh2 - # datum and unit propagation + # CRS propagation tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test datum(Meshes.crs(mesh)) === WGS84Latest - @test unit(Meshes.lentype(mesh)) == u"km" + @test Meshes.crs(mesh) === Meshes.crs(pset) + + coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] + pset = PointSet(Point.(coords)) + mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) + @test Meshes.crs(mesh) === Meshes.crs(pset) + + # error: the number of coordinates of the points must be 2 + pts = randpoint3(10) + pset = PointSet(pts) + @test_throws AssertionError tesselate(pset, VoronoiTesselation(StableRNG(2024))) end end From 10e49b9a3f9053c94543c60fad8824c1cfe531a3 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:55:57 -0300 Subject: [PATCH 131/423] Update to CoordRefSystems.jl v0.9 (#911) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a14b197b0..943620255 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.8" +CoordRefSystems = "0.9" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" From 2939e87dd88cdd5e778075fdfed28ca8ac739485 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:00:31 -0300 Subject: [PATCH 132/423] Add `FlatCoords` transform (#912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'Planefy' transform * Rename to 'FlatCoords' & Add tests * Add to docs * Fix typo * Apply suggestions * Fix docs * Update src/transforms/flatcoords.jl --------- Co-authored-by: Júlio Hoffimann --- docs/src/transforms.md | 14 +++++++ src/Meshes.jl | 1 + src/transforms.jl | 1 + src/transforms/flatcoords.jl | 27 +++++++++++++ test/transforms.jl | 77 ++++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+) create mode 100644 src/transforms/flatcoords.jl diff --git a/docs/src/transforms.md b/docs/src/transforms.md index f17e35893..b137692a1 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -2,6 +2,7 @@ ```@example transforms using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -128,6 +129,19 @@ viz(fig[1,2], mesh) fig ``` +## FlatCoords + +```@docs +FlatCoords +``` + +```@example transforms +point = Point(LatLon(30, 60)) +triangle = Triangle(Point(LatLon(30, 60)), Point(LatLon(30, 61)), Point(LatLon(31, 60))) + +[point, triangle] |> FlatCoords() +``` + ## Proj ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index f27826a3f..f84b991bc 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -514,6 +514,7 @@ export Affine, Stretch, StdCoords, + FlatCoords, Proj, LengthUnit, Shadow, diff --git a/src/transforms.jl b/src/transforms.jl index 63a69bfa5..2348be191 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -95,6 +95,7 @@ include("transforms/scale.jl") include("transforms/affine.jl") include("transforms/stretch.jl") include("transforms/stdcoords.jl") +include("transforms/flatcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") diff --git a/src/transforms/flatcoords.jl b/src/transforms/flatcoords.jl new file mode 100644 index 000000000..206cb74e6 --- /dev/null +++ b/src/transforms/flatcoords.jl @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + FlatCoords() + +Flatten the coordinates of a geometry or domain to 2D Cartesian coordinates when possible. +""" +struct FlatCoords <: CoordinateTransform end + +applycoord(::FlatCoords, v::Vec) = v + +applycoord(::FlatCoords, p::Point) = Point(_flatcoords(coords(p))) + +function _flatcoords(coords::CRS) + if CoordRefSystems.ncoords(coords) ≠ 2 + throw(ArgumentError("points must have 2 coordinates")) + end + convert(Cartesian, coords) +end + +_flatcoords(coords::LatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) + +_flatcoords(coords::GeocentricLatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) + +_flatcoords(coords::AuthalicLatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) diff --git a/test/transforms.jl b/test/transforms.jl index b0d4eecfd..6961ae71d 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1104,6 +1104,83 @@ @test r == r2 end + @testset "FlatCoords" begin + @test !isaffine(FlatCoords()) + @test !TB.isrevertible(FlatCoords()) + @test !TB.isinvertible(FlatCoords()) + + # ---- + # VEC + # ---- + + f = FlatCoords() + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r == v + + # ------ + # POINT + # ------ + + f = FlatCoords() + g = point(1, 1) + r, c = TB.apply(f, g) + @test r == point(1, 1) + + f = FlatCoords() + g = Point(Polar(T(√2), T(π / 4))) + r, c = TB.apply(f, g) + @test r ≈ point(1, 1) + + f = FlatCoords() + g = Point(LatLon(T(30), T(60))) + r, c = TB.apply(f, g) + @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) + + f = FlatCoords() + g = Point(GeocentricLatLon(T(30), T(60))) + r, c = TB.apply(f, g) + @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) + + f = FlatCoords() + g = Point(AuthalicLatLon(T(30), T(60))) + r, c = TB.apply(f, g) + @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) + + f = FlatCoords() + g = Point(Mercator(T(1), T(1))) + r, c = TB.apply(f, g) + @test r == Point(Cartesian{WGS84Latest}(T(1), T(1))) + + f = FlatCoords() + g = point(1, 1, 1) + # error: points must have 2 coordinates + @test_throws ArgumentError TB.apply(f, g) + + # ---------- + # ROPE/RING + # ---------- + + f = FlatCoords() + g = Rope(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) + r, c = TB.apply(f, g) + @test r ≈ Rope(point(0, 0), point(1, 0), point(0, 1)) + + f = FlatCoords() + g = Ring(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) + r, c = TB.apply(f, g) + @test r ≈ Ring(point(0, 0), point(1, 0), point(0, 1)) + + # --------- + # TRIANGLE + # --------- + + f = FlatCoords() + g = Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) + r, c = TB.apply(f, g) + @test r ≈ Triangle(point(0, 0), point(1, 0), point(0, 1)) + end + @testset "Proj" begin @test !isaffine(Proj(Polar)) @test !TB.isrevertible(Proj(Polar)) From 3ec47a3d029e02772cea3f5e3cb624c200505bc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:01:58 -0300 Subject: [PATCH 133/423] :robot: Format .jl files (#913) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/transforms/flatcoords.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transforms/flatcoords.jl b/src/transforms/flatcoords.jl index 206cb74e6..baf84f601 100644 --- a/src/transforms/flatcoords.jl +++ b/src/transforms/flatcoords.jl @@ -13,7 +13,7 @@ applycoord(::FlatCoords, v::Vec) = v applycoord(::FlatCoords, p::Point) = Point(_flatcoords(coords(p))) -function _flatcoords(coords::CRS) +function _flatcoords(coords::CRS) if CoordRefSystems.ncoords(coords) ≠ 2 throw(ArgumentError("points must have 2 coordinates")) end From 42a66e7957e595b4ae8eefabcefac65e31429c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Jul 2024 10:00:19 -0300 Subject: [PATCH 134/423] Refactor cartesiangrid.jl --- src/mesh/cartesiangrid.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 5a8b3da21..cdb4321b0 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -123,12 +123,12 @@ end CartesianGrid(dims::Int...) = CartesianGrid(dims) -vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = g.origin + Vec((ijk .- g.offset) .* g.spacing) - spacing(g::CartesianGrid) = g.spacing offset(g::CartesianGrid) = g.offset +vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = g.origin + Vec((ijk .- g.offset) .* g.spacing) + function xyz(g::CartesianGrid{Dim}) where {Dim} dims = size(g) spac = spacing(g) From 557c578481f36c9ee7381113e9facaae01ee6671 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:55:42 -0300 Subject: [PATCH 135/423] Fix `point in polyarea` (#909) * Fix 'point in polyarea' * Add 'ascart2' helper function * Apply suggestions * Add tests * Use 'FlatCoords' * Apply suggestions --- src/sideof.jl | 2 +- src/winding.jl | 16 ++++++++++++++-- test/predicates.jl | 13 +++++++++++++ test/sideof.jl | 8 ++++++++ test/winding.jl | 11 +++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/sideof.jl b/src/sideof.jl index 323679521..f9096a434 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -50,7 +50,7 @@ end Determines on which side the `point` is in relation to the `ring`. Possible results are `IN` or `OUT` the `ring`. """ -sideof(point::Point{2}, ring::Ring{2}) = ifelse(isapproxzero(winding(point, ring)), OUT, IN) +sideof(point::Point, ring::Ring) = ifelse(isapproxzero(winding(point, ring)), OUT, IN) # ----- # MESH diff --git a/src/winding.jl b/src/winding.jl index 77cbd8e9b..6c3ea2bfc 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -18,7 +18,9 @@ Generalized winding number of `points` with respect to the geometric `object`. """ function winding end -function winding(points, ring::Ring{2}) +winding(points, ring::Ring) = _winding(_coords(points, ring), points, ring) + +function _winding(::Cartesian{Datum,2}, points, ring) where {Datum} v = vertices(ring) n = nvertices(ring) @@ -30,7 +32,17 @@ function winding(points, ring::Ring{2}) tcollect(w(p) for p in points) end -winding(point::Point{2}, ring::Ring{2}) = winding((point,), ring) |> first +_winding(::CRS, points, ring) = winding(map(FlatCoords(), points), ring |> FlatCoords()) + +function _coords(points, ring) + p = first(points) + if crs(p) !== crs(ring) + throw(ArgumentError("both arguments must have the same CRS")) + end + coords(p) +end + +winding(point::Point, ring::Ring) = winding((point,), ring) |> first # Jacobson et al 2013. function winding(points, mesh::Mesh{3}) diff --git a/test/predicates.jl b/test/predicates.jl index a18a8c300..97e7a9e7e 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -165,6 +165,19 @@ @test point(0.5, 0.5, 0.5) ∈ h @test point(-1, 0, 0) ∉ h @test point(0, 2, 0) ∉ h + + outer = Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))]) + hole1 = Point.([LatLon(T(0.2), T(0.2)), LatLon(T(0.4), T(0.2)), LatLon(T(0.4), T(0.4)), LatLon(T(0.2), T(0.4))]) + hole2 = Point.([LatLon(T(0.6), T(0.2)), LatLon(T(0.8), T(0.2)), LatLon(T(0.8), T(0.4)), LatLon(T(0.6), T(0.4))]) + poly = PolyArea([outer, hole1, hole2]) + @test all(p ∈ poly for p in outer) + @test Point(LatLon(T(0.5), T(0.5))) ∈ poly + @test Point(LatLon(T(0.2), T(0.6))) ∈ poly + @test Point(LatLon(T(1.5), T(0.5))) ∉ poly + @test Point(LatLon(T(-0.5), T(0.5))) ∉ poly + @test Point(LatLon(T(0.25), T(0.25))) ∉ poly + @test Point(LatLon(T(0.75), T(0.25))) ∉ poly + @test Point(LatLon(T(0.75), T(0.75))) ∈ poly end @testset "issubset" begin diff --git a/test/sideof.jl b/test/sideof.jl index b8efb9657..0f4b53307 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -15,6 +15,14 @@ pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, IN] + p1, p2, p3 = Point(LatLon(T(0.5), T(0.5))), Point(LatLon(T(1.5), T(0.5))), Point(LatLon(T(1), T(1))) + c = Ring(Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))])) + @test sideof(p1, c) == IN + @test sideof(p2, c) == OUT + @test sideof(p3, c) == IN + pts = [p1, p2, p3] + @test sideof(pts, c) == [IN, OUT, IN] + points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)]) connec = connect.([(1, 3, 2), (1, 2, 4), (1, 4, 3), (2, 3, 4)], Triangle) mesh = SimpleMesh(points, connec) diff --git a/test/winding.jl b/test/winding.jl index 542ce6ff7..b6e26445b 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -11,6 +11,17 @@ @test winding(p, reverse(c)) ≈ T(-2) @test winding([p, p], c) ≈ T[2, 2] + p = Point(LatLon(T(0.5), T(0.5))) + c = Ring(Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))])) + @test winding(p, c) ≈ T(1) + @test winding(p, reverse(c)) ≈ T(-1) + @test winding([p, p], c) ≈ T[1, 1] + + # error: both arguments must have the same CRS + p = Point(LatLon(T(0.5), T(0.5))) + c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) + @test_throws ArgumentError winding(p, c) + m = boundary(Box(point(0, 0, 0), point(2, 2, 2))) @test all(>(0), winding(vertices(m), m)) @test isapprox(winding(point(1, 1, 1), m), T(1), atol=atol(T)) From 8f412c8eea423b98dcd3e639dbe5d3fa60e6a05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Jul 2024 11:17:12 -0300 Subject: [PATCH 136/423] Rename point --> cart in test suite --- test/boundingboxes.jl | 90 ++--- test/clamping.jl | 18 +- test/clipping.jl | 80 ++-- test/coarsening.jl | 8 +- test/complement.jl | 42 +- test/connectivities.jl | 6 +- test/discretization.jl | 148 +++---- test/distances.jl | 20 +- test/domains.jl | 22 +- test/hulls.jl | 136 +++---- test/intersections.jl | 850 ++++++++++++++++++++--------------------- test/matrices.jl | 10 +- test/merging.jl | 6 +- test/mesh.jl | 242 ++++++------ test/multigeoms.jl | 48 +-- test/neighborsearch.jl | 52 +-- test/orientation.jl | 8 +- test/pointification.jl | 22 +- test/polytopes.jl | 542 +++++++++++++------------- test/predicates.jl | 332 ++++++++-------- test/primitives.jl | 788 +++++++++++++++++++------------------- test/refinement.jl | 14 +- test/runtests.jl | 2 +- test/sampling.jl | 138 +++---- test/sets.jl | 42 +- test/sideof.jl | 42 +- test/simplification.jl | 30 +- test/sorting.jl | 2 +- test/subdomains.jl | 10 +- test/supportfun.jl | 32 +- test/testutils.jl | 4 +- test/trajecs.jl | 8 +- test/transforms.jl | 418 ++++++++++---------- test/utils.jl | 12 +- test/viewing.jl | 52 +-- test/winding.jl | 16 +- 36 files changed, 2146 insertions(+), 2146 deletions(-) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index dd683c1a5..ea393551d 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -1,123 +1,123 @@ @testset "Bounding boxes" begin - p = point(0, 0) + p = cart(0, 0) @test boundingbox(p) == Box(p, p) @test @allocated(boundingbox(p)) < 50 - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) @test boundingbox(b) == b @test @allocated(boundingbox(b)) < 50 - r = Ray(point(0, 0), vector(1, 0)) - @test boundingbox(r) == Box(point(0, 0), point(T(Inf), 0)) + r = Ray(cart(0, 0), vector(1, 0)) + @test boundingbox(r) == Box(cart(0, 0), cart(T(Inf), 0)) @test @allocated(boundingbox(r)) < 50 - r = Ray(point(1, 1), vector(0, 1)) - @test boundingbox(r) == Box(point(1, 1), point(1, T(Inf))) + r = Ray(cart(1, 1), vector(0, 1)) + @test boundingbox(r) == Box(cart(1, 1), cart(1, T(Inf))) @test @allocated(boundingbox(r)) < 50 - r = Ray(point(1, 1), vector(-1, -1)) - @test boundingbox(r) == Box(point(T(-Inf), T(-Inf)), point(1, 1)) + r = Ray(cart(1, 1), vector(-1, -1)) + @test boundingbox(r) == Box(cart(T(-Inf), T(-Inf)), cart(1, 1)) @test @allocated(boundingbox(r)) < 50 - r = Ray(point(-1, 1), vector(1, -1)) - @test boundingbox(r) == Box(point(-1, T(-Inf)), point(T(Inf), 1)) + r = Ray(cart(-1, 1), vector(1, -1)) + @test boundingbox(r) == Box(cart(-1, T(-Inf)), cart(T(Inf), 1)) @test @allocated(boundingbox(r)) < 50 - b = Ball(point(0, 0), T(1)) - @test boundingbox(b) == Box(point(-1, -1), point(1, 1)) + b = Ball(cart(0, 0), T(1)) + @test boundingbox(b) == Box(cart(-1, -1), cart(1, 1)) @test @allocated(boundingbox(b)) < 50 - b = Ball(point(1, 1), T(1)) - @test boundingbox(b) == Box(point(0, 0), point(2, 2)) + b = Ball(cart(1, 1), T(1)) + @test boundingbox(b) == Box(cart(0, 0), cart(2, 2)) @test @allocated(boundingbox(b)) < 50 - s = Sphere(point(0, 0), T(1)) - @test boundingbox(s) == Box(point(-1, -1), point(1, 1)) + s = Sphere(cart(0, 0), T(1)) + @test boundingbox(s) == Box(cart(-1, -1), cart(1, 1)) @test @allocated(boundingbox(s)) < 50 - s = Sphere(point(1, 1), T(1)) - @test boundingbox(s) == Box(point(0, 0), point(2, 2)) + s = Sphere(cart(1, 1), T(1)) + @test boundingbox(s) == Box(cart(0, 0), cart(2, 2)) @test @allocated(boundingbox(s)) < 50 c = Cylinder(T(1)) b = boundingbox(c) - @test b == Box(point(-1, -1, 0), point(1, 1, 1)) + @test b == Box(cart(-1, -1, 0), cart(1, 1, 1)) c = CylinderSurface(T(1)) b = boundingbox(c) - @test b == Box(point(-1, -1, 0), point(1, 1, 1)) + @test b == Box(cart(-1, -1, 0), cart(1, 1, 1)) - c = Cone(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) + c = Cone(Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)), cart(0, 0, 1)) b = boundingbox(c) - @test b == Box(point(-1, -1, 0), point(1, 1, 1)) + @test b == Box(cart(-1, -1, 0), cart(1, 1, 1)) - c = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) + c = ConeSurface(Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)), cart(0, 0, 1)) b = boundingbox(c) - @test b == Box(point(-1, -1, 0), point(1, 1, 1)) + @test b == Box(cart(-1, -1, 0), cart(1, 1, 1)) - b = Box(point(-3, -1), point(0.5, 0.5)) - s = Sphere(point(0, 0), T(2)) + b = Box(cart(-3, -1), cart(0.5, 0.5)) + s = Sphere(cart(0, 0), T(2)) m = Multi([b, s]) d = GeometrySet([b, s]) - @test boundingbox(m) == Box(point(-3, -2), point(2, 2)) - @test boundingbox(d) == Box(point(-3, -2), point(2, 2)) + @test boundingbox(m) == Box(cart(-3, -2), cart(2, 2)) + @test boundingbox(d) == Box(cart(-3, -2), cart(2, 2)) @test @allocated(boundingbox(m)) < 2500 @test @allocated(boundingbox(d)) < 2500 - b1 = Box(point(0, 0), point(1, 1)) - b2 = Box(point(-1, -1), point(0.5, 0.5)) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(-1, -1), cart(0.5, 0.5)) m = Multi([b1, b2]) d = GeometrySet([b1, b2]) - @test boundingbox(m) == Box(point(-1, -1), point(1, 1)) - @test boundingbox(d) == Box(point(-1, -1), point(1, 1)) + @test boundingbox(m) == Box(cart(-1, -1), cart(1, 1)) + @test boundingbox(d) == Box(cart(-1, -1), cart(1, 1)) @test @allocated(boundingbox(m)) < 50 @test @allocated(boundingbox(d)) < 50 d = PointSet(T[0 1 2; 0 2 1]) - @test boundingbox(d) == Box(point(0, 0), point(2, 2)) + @test boundingbox(d) == Box(cart(0, 0), cart(2, 2)) @test @allocated(boundingbox(d)) < 50 d = PointSet(T[1 2; 2 1]) - @test boundingbox(d) == Box(point(1, 1), point(2, 2)) + @test boundingbox(d) == Box(cart(1, 1), cart(2, 2)) @test @allocated(boundingbox(d)) < 50 d = cartgrid(10, 10) - @test boundingbox(d) == Box(point(0, 0), point(10, 10)) + @test boundingbox(d) == Box(cart(0, 0), cart(10, 10)) @test @allocated(boundingbox(d)) < 50 d = cartgrid(100, 200) - @test boundingbox(d) == Box(point(0, 0), point(100, 200)) + @test boundingbox(d) == Box(cart(0, 0), cart(100, 200)) @test @allocated(boundingbox(d)) < 50 d = CartesianGrid((10, 10), T.((1, 1)), T.((1, 1))) - @test boundingbox(d) == Box(point(1, 1), point(11, 11)) + @test boundingbox(d) == Box(cart(1, 1), cart(11, 11)) @test @allocated(boundingbox(d)) < 50 d = PointSet(T[0 1 2; 0 2 1]) v = view(d, 1:2) - @test boundingbox(v) == Box(point(0, 0), point(1, 2)) + @test boundingbox(v) == Box(cart(0, 0), cart(1, 2)) @test @allocated(boundingbox(v)) < 50 d = cartgrid(10, 10) v = view(d, 1:2) - @test boundingbox(v) == Box(point(0, 0), point(2, 1)) + @test boundingbox(v) == Box(cart(0, 0), cart(2, 1)) @test @allocated(boundingbox(v)) < 9000 g = cartgrid(10, 10) d = convert(RectilinearGrid, g) - @test boundingbox(d) == Box(point(0, 0), point(10, 10)) + @test boundingbox(d) == Box(cart(0, 0), cart(10, 10)) @test @allocated(boundingbox(d)) < 50 g = cartgrid(10, 10) d = TransformedGrid(g, Rotate(T(π / 2))) - @test boundingbox(d) ≈ Box(point(-10, 0), point(0, 10)) + @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 g = cartgrid(10, 10) rg = convert(RectilinearGrid, g) d = TransformedGrid(rg, Rotate(T(π / 2))) - @test boundingbox(d) ≈ Box(point(-10, 0), point(0, 10)) + @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 g = cartgrid(10, 10) m = convert(SimpleMesh, g) - @test boundingbox(m) == Box(point(0, 0), point(10, 10)) + @test boundingbox(m) == Box(cart(0, 0), cart(10, 10)) @test @allocated(boundingbox(m)) < 50 - p = ParaboloidSurface(point(1, 2, 3), T(5), T(4)) - @test boundingbox(p) ≈ Box(point(-4, -3, 3), point(6, 7, 73 / 16)) + p = ParaboloidSurface(cart(1, 2, 3), T(5), T(4)) + @test boundingbox(p) ≈ Box(cart(-4, -3, 3), cart(6, 7, 73 / 16)) # datum propagation c = Cartesian{WGS84Latest}(T(-1), T(1)) diff --git a/test/clamping.jl b/test/clamping.jl index 9e573ebdf..c1d603b8c 100644 --- a/test/clamping.jl +++ b/test/clamping.jl @@ -1,13 +1,13 @@ @testset "Clamping" begin box = Box((zero(T), zero(T)), (one(T), one(T))) - @test clamp(point(0.5, 0.5), box) == point(0.5, 0.5) - @test clamp(point(-1, 0.5), box) == point(0, 0.5) - @test clamp(point(0.5, -1), box) == point(0.5, 0) - @test clamp(point(2, 0.5), box) == point(1, 0.5) - @test clamp(point(0.5, 2), box) == point(0.5, 1) - @test clamp(point(2, 2), box) == point(1, 1) - @test clamp(point(-1, -1), box) == point(0, 0) + @test clamp(cart(0.5, 0.5), box) == cart(0.5, 0.5) + @test clamp(cart(-1, 0.5), box) == cart(0, 0.5) + @test clamp(cart(0.5, -1), box) == cart(0.5, 0) + @test clamp(cart(2, 0.5), box) == cart(1, 0.5) + @test clamp(cart(0.5, 2), box) == cart(0.5, 1) + @test clamp(cart(2, 2), box) == cart(1, 1) + @test clamp(cart(-1, -1), box) == cart(0, 0) - points = PointSet(point(0.5, 0.5), point(-1, 0.5), point(0.5, 2)) - @test clamp(points, box) == PointSet(point(0.5, 0.5), point(0, 0.5), point(0.5, 1)) + points = PointSet(cart(0.5, 0.5), cart(-1, 0.5), cart(0.5, 2)) + @test clamp(points, box) == PointSet(cart(0.5, 0.5), cart(0, 0.5), cart(0.5, 1)) end diff --git a/test/clipping.jl b/test/clipping.jl index 83606de87..159364bb1 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -1,79 +1,79 @@ @testset "Clipping" begin @testset "SutherlandHodgman" begin # triangle - poly = Triangle(point(6, 2), point(3, 5), point(0, 2)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) - @test all(vertices(clipped) .≈ [point(5, 3), point(4, 4), point(2, 4), point(0, 2), point(5, 2)]) + @test all(vertices(clipped) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) # octagon poly = - Octagon(point(8, -2), point(8, 5), point(2, 5), point(4, 3), point(6, 3), point(4, 1), point(2, 1), point(2, -2)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test !issimple(clipped) @test all( vertices(clipped) .≈ [ - point(3, 4), - point(4, 3), - point(5, 3), - point(5, 2), - point(4, 1), - point(2, 1), - point(2, 0), - point(5, 0), - point(5, 4) + cart(3, 4), + cart(4, 3), + cart(5, 3), + cart(5, 2), + cart(4, 1), + cart(2, 1), + cart(2, 0), + cart(5, 0), + cart(5, 4) ] ) # inside - poly = Quadrangle(point(1, 0), point(1, 1), point(0, 1), point(0, 0)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(poly)) # outside - poly = Quadrangle(point(7, 6), point(7, 7), point(6, 7), point(6, 6)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test isnothing(clipped) # surrounded - poly = Hexagon(point(0, 2), point(-2, 2), point(-2, 0), point(0, -2), point(2, -2), point(2, 0)) - other = Hexagon(point(1, 0), point(0, 1), point(-1, 1), point(-1, 0), point(0, -1), point(1, -1)) + poly = Hexagon(cart(0, 2), cart(-2, 2), cart(-2, 0), cart(0, -2), cart(2, -2), cart(2, 0)) + other = Hexagon(cart(1, 0), cart(0, 1), cart(-1, 1), cart(-1, 0), cart(0, -1), cart(1, -1)) clipped = clip(poly, other, SutherlandHodgman()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(other)) # PolyArea with box outer = - Ring(point(8, 0), point(4, 8), point(2, 8), point(-2, 0), point(0, 0), point(1, 2), point(5, 2), point(6, 0)) - inner = Ring(point(4, 4), point(2, 4), point(3, 6)) + Ring(cart(8, 0), cart(4, 8), cart(2, 8), cart(-2, 0), cart(0, 0), cart(1, 2), cart(5, 2), cart(6, 0)) + inner = Ring(cart(4, 4), cart(2, 4), cart(3, 6)) poly = PolyArea([outer, inner]) - other = Box(point(0, 1), point(3, 7)) + other = Box(cart(0, 1), cart(3, 7)) clipped = clip(poly, other, SutherlandHodgman()) crings = rings(clipped) @test !issimple(clipped) @test all( vertices(crings[1]) .≈ [ - point(1.5, 7.0), - point(0.0, 4.0), - point(0.0, 1.0), - point(0.5, 1.0), - point(1.0, 2.0), - point(3.0, 2.0), - point(3.0, 7.0) + cart(1.5, 7.0), + cart(0.0, 4.0), + cart(0.0, 1.0), + cart(0.5, 1.0), + cart(1.0, 2.0), + cart(3.0, 2.0), + cart(3.0, 7.0) ] ) - @test all(vertices(crings[2]) .≈ [point(3.0, 4.0), point(2.0, 4.0), point(3.0, 6.0)]) + @test all(vertices(crings[2]) .≈ [cart(3.0, 4.0), cart(2.0, 4.0), cart(3.0, 6.0)]) # PolyArea with outer ring outside and inner ring inside - outer = Ring(point(8, 0), point(2, 6), point(-4, 0)) - inner = Ring(point(1, 3), point(3, 3), point(3, 1), point(1, 1)) + outer = Ring(cart(8, 0), cart(2, 6), cart(-4, 0)) + inner = Ring(cart(1, 3), cart(3, 3), cart(3, 1), cart(1, 1)) poly = PolyArea([outer, inner]) - other = Quadrangle(point(4, 4), point(0, 4), point(0, 0), point(4, 0)) + other = Quadrangle(cart(4, 4), cart(0, 4), cart(0, 0), cart(4, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test !issimple(clipped) crings = rings(clipped) @@ -81,16 +81,16 @@ @test all(vertices(crings[2]) .≈ vertices(inner)) # PolyArea with one inner ring inside `other` and another inner ring outside `other` - outer = Ring(point(6, 4), point(6, 7), point(1, 6), point(1, 1), point(5, 2)) - inner₁ = Ring(point(3, 3), point(3, 4), point(4, 3)) - inner₂ = Ring(point(2, 5), point(2, 6), point(3, 5)) + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) poly = PolyArea([outer, inner₁, inner₂]) - other = PolyArea(Ring(point(6, 1), point(7, 2), point(6, 5), point(0, 2), point(1, 1))) + other = PolyArea(Ring(cart(6, 1), cart(7, 2), cart(6, 5), cart(0, 2), cart(1, 1))) clipped = clip(poly, other, SutherlandHodgman()) crings = rings(clipped) @test !issimple(clipped) @test length(crings) == 2 - @test all(vertices(crings[1]) .≈ [point(6, 4), point(6, 5), point(1, 2.5), point(1, 1), point(5, 2)]) - @test all(vertices(crings[2]) .≈ [point(3.0, 3.0), point(3.0, 3.5), point(10 / 3, 11 / 3), point(4.0, 3.0)]) + @test all(vertices(crings[1]) .≈ [cart(6, 4), cart(6, 5), cart(1, 2.5), cart(1, 1), cart(5, 2)]) + @test all(vertices(crings[2]) .≈ [cart(3.0, 3.0), cart(3.0, 3.5), cart(10 / 3, 11 / 3), cart(4.0, 3.0)]) end end diff --git a/test/coarsening.jl b/test/coarsening.jl index 145241ebc..b9d6072bb 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -1,8 +1,8 @@ @testset "Coarsening" begin @testset "RegularCoarsening" begin # 2D grids - grid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(10, 10)) + grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 10)) @test coarsen(grid, RegularCoarsening(2)) == tgrid rgrid = convert(RectilinearGrid, grid) trgrid = convert(RectilinearGrid, tgrid) @@ -11,8 +11,8 @@ tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid - grid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(point(0.0, 0.0), point(10.0, 10.0), dims=(10, 5)) + grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 5)) @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid # 3D grids diff --git a/test/complement.jl b/test/complement.jl index d8ccc8759..d0da1dd9b 100644 --- a/test/complement.jl +++ b/test/complement.jl @@ -1,33 +1,33 @@ @testset "complement" begin τ = atol(T) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) p = !t r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) - @test r[2] == Ring(point.([(0, 0), (1, 1), (1, 0)])) + @test r[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(cart.([(0, 0), (1, 1), (1, 0)])) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) p = !q r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) - @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test r[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - p = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) n = !p r = rings(n) @test n isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) - @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test r[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - o = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - i1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - i2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + o = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + i1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + i2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) p = PolyArea([o, i1, i2]) m = !p r = rings(m) @@ -35,23 +35,23 @@ @test length(r) == 4 g = parent(m) @test length(g) == 3 - @test rings(g[1])[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) - @test rings(g[1])[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) - @test rings(g[2]) == [Ring(point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]))] - @test rings(g[3]) == [Ring(point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]))] + @test rings(g[1])[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test rings(g[1])[2] == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test rings(g[2]) == [Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]))] + @test rings(g[3]) == [Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]))] - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) p = !b r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(point.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) - @test r[2] == Ring(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test r[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) + @test r[2] == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - b = Ball(point(0, 0), T(1)) + b = Ball(cart(0, 0), T(1)) p = !b r = rings(p) @test p isa PolyArea @test length(r) == 2 - @test r[1] ≈ Ring(point.([(-1 - τ, -1 - τ), (1 + τ, -1 - τ), (1 + τ, 1 + τ), (-1 - τ, 1 + τ)])) + @test r[1] ≈ Ring(cart.([(-1 - τ, -1 - τ), (1 + τ, -1 - τ), (1 + τ, 1 + τ), (-1 - τ, 1 + τ)])) end diff --git a/test/connectivities.jl b/test/connectivities.jl index 7a207987a..bc967d447 100644 --- a/test/connectivities.jl +++ b/test/connectivities.jl @@ -5,7 +5,7 @@ @test paramdim(c) == 2 @test issimplex(c) @test indices(c) == (1, 2, 3) - @test materialize(c, point.([(0, 0), (1, 0), (0, 1)])) == Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test materialize(c, cart.([(0, 0), (1, 0), (0, 1)])) == Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) # tuple from other collections c = connect(Tuple([1, 2, 3]), Triangle) @@ -13,7 +13,7 @@ @test paramdim(c) == 2 @test issimplex(c) @test indices(c) == (1, 2, 3) - @test materialize(c, point.([(0, 0), (1, 0), (0, 1)])) == Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test materialize(c, cart.([(0, 0), (1, 0), (0, 1)])) == Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) # incorrect number of vertices for polytope @test_throws AssertionError connect((1, 2, 3, 4), Triangle) @@ -44,6 +44,6 @@ @test paramdim(c) == 3 @test issimplex(c) @test indices(c) == (1, 2, 3, 4) - points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) + points = cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) @test materialize(c, points) == Tetrahedron(points...) end diff --git a/test/discretization.jl b/test/discretization.jl index da42c469f..ac27400a8 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -1,6 +1,6 @@ @testset "Discretization" begin @testset "FanTriangulation" begin - pts = point.([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)]) + pts = cart.([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)]) tris = [Triangle(pts[1], pts[i], pts[i + 1]) for i in 2:(length(pts) - 1)] hex = Hexagon(pts...) mesh = discretize(hex, FanTriangulation()) @@ -13,14 +13,14 @@ @testset "DehnTriangulation" begin octa = Octagon( - point(0.2, 0.2), - point(0.5, -0.5), - point(0.8, 0.2), - point(1.5, 0.5), - point(0.8, 0.8), - point(0.5, 1.5), - point(0.2, 0.8), - point(-0.5, 0.5) + cart(0.2, 0.2), + cart(0.5, -0.5), + cart(0.8, 0.2), + cart(1.5, 0.5), + cart(0.8, 0.8), + cart(0.5, 1.5), + cart(0.2, 0.8), + cart(-0.5, 0.5) ) mesh = discretize(octa, DehnTriangulation()) @test nvertices(mesh) == 8 @@ -28,14 +28,14 @@ @test eltype(mesh) <: Triangle octa = Octagon( - point(0.2, 0.2, 0.0), - point(0.5, -0.5, 0.0), - point(0.8, 0.2, 0.0), - point(1.5, 0.5, 0.0), - point(0.8, 0.8, 0.0), - point(0.5, 1.5, 0.0), - point(0.2, 0.8, 0.0), - point(-0.5, 0.5, 0.0) + cart(0.2, 0.2, 0.0), + cart(0.5, -0.5, 0.0), + cart(0.8, 0.2, 0.0), + cart(1.5, 0.5, 0.0), + cart(0.8, 0.8, 0.0), + cart(0.5, 1.5, 0.0), + cart(0.2, 0.8, 0.0), + cart(-0.5, 0.5, 0.0) ) mesh = discretize(octa, DehnTriangulation()) @test nvertices(mesh) == 8 @@ -44,20 +44,20 @@ end @testset "HeldTriangulation" begin - 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4, 5] - 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)])) + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4] - 𝒫 = Ring(point.([(0, 0), (1, 0), (1, 1), (1, 2)])) + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [2, 4] - 𝒫 = Ring(point.([(0, 0), (1, 1), (1, 2)])) + 𝒫 = Ring(cart.([(0, 0), (1, 1), (1, 2)])) @test Meshes.earsccw(𝒫) == [] 𝒫 = Ring( - point.([ + cart.([ (0.443339268495331, 0.283757618605357), (0.497822414616971, 0.398142813114205), (0.770343126156527, 0.201815462842808), @@ -76,7 +76,7 @@ ) @test Meshes.earsccw(𝒫) == [1, 3, 5, 6, 8, 10, 12, 14] - points = point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) + points = cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) connec = connect.([(4, 5, 6), (3, 4, 6), (3, 6, 1), (1, 2, 3)], Triangle) target = SimpleMesh(points, connec) poly = PolyArea(points) @@ -87,7 +87,7 @@ # https://github.com/JuliaGeometry/Meshes.jl/issues/675 poly = PolyArea( - point.([ + cart.([ (1.1794224993e7, 1.7289506814e7), (1.1794045018e7, 1.7289446822e7), (1.1793985026e7, 1.7289486817e7), @@ -113,7 +113,7 @@ # https://github.com/JuliaGeometry/Meshes.jl/issues/738 poly = PolyArea( - point.([ + cart.([ (-0.5, 0.3296139), (-0.19128194, -0.5), (-0.37872985, 0.29592824), @@ -129,7 +129,7 @@ @testset "DelaunayTriangulation" begin rng = StableRNG(123) - poly = Pentagon(point(0, 0), point(1, 0), point(1, 1), point(0.5, 2), point(0, 1)) + poly = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 2), cart(0, 1)) mesh = discretize(poly, DelaunayTriangulation(rng)) @test Set(vertices(poly)) == Set(vertices(mesh)) @test nelements(mesh) == length(vertices(mesh)) - 2 @@ -138,21 +138,21 @@ @testset "Miscellaneous triangulations" begin rng = StableRNG(123) for method in [DehnTriangulation(), HeldTriangulation(rng), DelaunayTriangulation(rng)] - triangle = Triangle(point(0, 0), point(1, 0), point(0, 1)) + triangle = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) mesh = discretize(triangle, method) - @test vertices(mesh) == [point(0, 0), point(1, 0), point(0, 1)] + @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(0, 1)] @test nelements(mesh) == 1 @test mesh[1] ≗ triangle - quadrangle = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + quadrangle = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) mesh = discretize(quadrangle, method) elms = collect(elements(mesh)) - @test vertices(mesh) == [point(0, 0), point(1, 0), point(1, 1), point(0, 1)] + @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)] @test eltype(elms) <: Triangle @test length(elms) == 2 - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - t = Triangle(point(1, 0), point(2, 1), point(1, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + t = Triangle(cart(1, 0), cart(2, 1), cart(1, 1)) m = Multi([q, t]) mesh = discretize(m, method) elms = collect(elements(mesh)) @@ -163,9 +163,9 @@ @test eltype(elms) <: Triangle @test length(elms) == 3 - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) bpoly = poly |> Bridge(T(0.01)) mesh = discretizewithin(boundary(bpoly), method) @@ -174,14 +174,14 @@ @test all(t -> area(t) > zero(ℳ)^2, mesh) # 3D chains - chain = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)])) + chain = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)])) mesh = discretizewithin(chain, method) @test vertices(mesh) == vertices(chain) @test eltype(mesh) <: Triangle @test nelements(mesh) == 2 # preserves order of vertices - poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) + poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) mesh = simplexify(poly) @test pointify(mesh) == pointify(poly) end @@ -274,7 +274,7 @@ if T == Float64 poly = PolyArea( - point.([ + cart.([ (-48.03012478813999, -18.323912004531923), (-48.030125176275845, -18.323904748608573), (-48.03017873307118, -18.323925747019675), @@ -291,16 +291,16 @@ end # degenerate triangle - poly = PolyArea(point.([(0, 0), (1, 1), (1, 1)])) + poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) mesh = discretize(poly) @test nvertices(mesh) == 3 @test nelements(mesh) == 1 - @test vertices(mesh) == [point(0, 0), point(0, 0), point(0, 0)] - @test mesh[1] == Triangle(point(0, 0), point(0, 0), point(0, 0)) + @test vertices(mesh) == [cart(0, 0), cart(0, 0), cart(0, 0)] + @test mesh[1] == Triangle(cart(0, 0), cart(0, 0), cart(0, 0)) end @testset "Tetrahedralization" begin - box = Box(point(0, 0, 0), point(1, 1, 1)) + box = Box(cart(0, 0, 0), cart(1, 1, 1)) hexa = Hexahedron(pointify(box)...) bmesh = discretize(box, Tetrahedralization()) hmesh = discretize(hexa, Tetrahedralization()) @@ -310,14 +310,14 @@ end @testset "RegularDiscretization" begin - bezier = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) + bezier = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) mesh = discretize(bezier, RegularDiscretization(10)) @test nvertices(mesh) == 11 @test nelements(mesh) == 10 @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] - box = Box(point(0, 0), point(2, 2)) + box = Box(cart(0, 0), cart(2, 2)) mesh = discretize(box, RegularDiscretization(10)) @test mesh isa CartesianGrid @test nvertices(mesh) == 121 @@ -325,14 +325,14 @@ @test eltype(mesh) <: Quadrangle @test nvertices.(mesh) ⊆ [4] - sphere = Sphere(point(0, 0), T(1)) + sphere = Sphere(cart(0, 0), T(1)) mesh = discretize(sphere, RegularDiscretization(10)) @test nvertices(mesh) == 10 @test nelements(mesh) == 10 @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] - sphere = Sphere(point(0, 0, 0), T(1)) + sphere = Sphere(cart(0, 0, 0), T(1)) mesh = discretize(sphere, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @@ -346,28 +346,28 @@ @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - ball = Ball(point(0, 0), T(1)) + ball = Ball(cart(0, 0), T(1)) mesh = discretize(ball, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 1 @test nelements(mesh) == 10 * 10 + 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - disk = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)) + disk = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)) mesh = discretize(disk, RegularDiscretization(10)) @test nvertices(mesh) == 11 * 10 + 1 @test nelements(mesh) == 10 * 10 + 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - cylsurf = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) + cylsurf = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) mesh = discretize(cylsurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * 11 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - consurf = ConeSurface(Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(1)), point(0, 0, 1)) + consurf = ConeSurface(Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)), cart(0, 0, 1)) mesh = discretize(consurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * 11 + 2 @test nelements(mesh) == 10 * 10 + 2 * 10 @@ -381,7 +381,7 @@ @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] - poly = PolyArea(point.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) + poly = PolyArea(cart.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) mesh = discretize(poly, RegularDiscretization(50)) @test mesh isa SubGrid{2} grid = parent(mesh) @@ -391,13 +391,13 @@ end @testset "Discretize" begin - ball = Ball(point(0, 0), T(1)) + ball = Ball(cart(0, 0), T(1)) mesh = discretize(ball) @test !(eltype(mesh) <: Triangle) @test !(eltype(mesh) <: Quadrangle) @test nelements(mesh) == 2550 - sphere = Sphere(point(0, 0, 0), T(1)) + sphere = Sphere(cart(0, 0, 0), T(1)) mesh = discretize(sphere) @test !(eltype(mesh) <: Triangle) @test !(eltype(mesh) <: Quadrangle) @@ -420,50 +420,50 @@ # simplexify is a helper function that calls an # appropriate discretization method depending on # the geometry type that is given to it - box = Box(point(0), point(1)) + box = Box(cart(0), cart(1)) msh = simplexify(box) @test eltype(msh) <: Segment @test topology(msh) == GridTopology(1) @test nvertices(msh) == 2 @test nelements(msh) == 1 - @test msh[1] == Segment(point(0), point(1)) + @test msh[1] == Segment(cart(0), cart(1)) - seg = Segment(point(0), point(1)) + seg = Segment(cart(0), cart(1)) msh = simplexify(seg) @test eltype(msh) <: Segment @test topology(msh) == GridTopology(1) @test nvertices(msh) == 2 @test nelements(msh) == 1 - @test msh[1] == Segment(point(0), point(1)) + @test msh[1] == Segment(cart(0), cart(1)) - chn = Rope(point.([(0, 0), (1, 0), (1, 1)])) + chn = Rope(cart.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(chn) @test eltype(msh) <: Segment @test nvertices(msh) == 3 @test nelements(msh) == 2 - @test msh[1] == Segment(point(0, 0), point(1, 0)) - @test msh[2] == Segment(point(1, 0), point(1, 1)) - chn = Ring(point.([(0, 0), (1, 0), (1, 1)])) + @test msh[1] == Segment(cart(0, 0), cart(1, 0)) + @test msh[2] == Segment(cart(1, 0), cart(1, 1)) + chn = Ring(cart.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(chn) @test eltype(msh) <: Segment @test nvertices(msh) == 3 @test nelements(msh) == 3 - @test msh[1] == Segment(point(0, 0), point(1, 0)) - @test msh[2] == Segment(point(1, 0), point(1, 1)) - @test msh[3] == Segment(point(1, 1), point(0, 0)) + @test msh[1] == Segment(cart(0, 0), cart(1, 0)) + @test msh[2] == Segment(cart(1, 0), cart(1, 1)) + @test msh[3] == Segment(cart(1, 1), cart(0, 0)) - sph = Sphere(point(0, 0), T(1)) + sph = Sphere(cart(0, 0), T(1)) msh = simplexify(sph) @test eltype(msh) <: Segment @test nvertices(msh) == nelements(msh) - bez = BezierCurve(point.([(0, 0), (1, 0), (1, 1)])) + bez = BezierCurve(cart.([(0, 0), (1, 0), (1, 1)])) msh = simplexify(bez) @test eltype(msh) <: Segment @test nvertices(msh) == nelements(msh) + 1 - box = Box(point(0, 0), point(1, 1)) - ngon = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + box = Box(cart(0, 0), cart(1, 1)) + ngon = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) poly = readpoly(T, joinpath(datadir, "taubin.line")) for geom in [box, ngon, poly] bound = boundary(geom) @@ -473,15 +473,15 @@ end # triangulation of multi geometries - box1 = Box(point(0, 0), point(1, 1)) - box2 = Box(point(1, 1), point(2, 2)) + box1 = Box(cart(0, 0), cart(1, 1)) + box2 = Box(cart(1, 1), cart(2, 2)) multi = Multi([box1, box2]) mesh = simplexify(multi) @test nvertices(mesh) == 8 @test nelements(mesh) == 4 # triangulation of spheres - sphere = Sphere(point(0, 0, 0), T(1)) + sphere = Sphere(cart(0, 0, 0), T(1)) mesh = simplexify(sphere) @test eltype(mesh) <: Triangle xs = to.(vertices(mesh)) @@ -497,7 +497,7 @@ @test all(x -> zero(ℳ) ≤ x[3] ≤ oneunit(ℳ), xs) # triangulation of balls - ball = Ball(point(0, 0), T(1)) + ball = Ball(cart(0, 0), T(1)) mesh = simplexify(ball) @test eltype(mesh) <: Triangle xs = to.(vertices(mesh)) @@ -515,7 +515,7 @@ @test measure(mesh) == measure(grid) # https://github.com/JuliaGeometry/Meshes.jl/issues/499 - quad = Quadrangle(point(0, 1, -1), point(0, 1, 1), point(0, -1, 1), point(0, -1, -1)) + quad = Quadrangle(cart(0, 1, -1), cart(0, 1, 1), cart(0, -1, 1), cart(0, -1, -1)) mesh = simplexify(quad) @test vertices(mesh) == pointify(quad) @@ -529,7 +529,7 @@ end # tetrahedralization - box = Box(point(0, 0, 0), point(1, 1, 1)) + box = Box(cart(0, 0, 0), cart(1, 1, 1)) hex = Hexahedron(pointify(box)...) bmesh = simplexify(box) hmesh = simplexify(hex) diff --git a/test/distances.jl b/test/distances.jl index 54056fbae..45ac83762 100644 --- a/test/distances.jl +++ b/test/distances.jl @@ -1,24 +1,24 @@ @testset "Distances" begin - p = point(0, 1) - l = Line(point(0, 0), point(1, 0)) + p = cart(0, 1) + l = Line(cart(0, 0), cart(1, 0)) @test evaluate(Euclidean(), p, l) == T(1) * u"m" @test evaluate(Euclidean(), l, p) == T(1) * u"m" - p = point(68, 259) - l = Line(point(68, 260), point(69, 261)) + p = cart(68, 259) + l = Line(cart(68, 260), cart(69, 261)) @test evaluate(Euclidean(), p, l) ≤ T(0.8) * u"m" - line1 = Line(point(-1, 0, 0), point(1, 0, 0)) - line2 = Line(point(0, -1, 1), point(0, 1, 1)) # line2 ⟂ line1, z++ - line3 = Line(point(-1, 1, 0), point(1, 1, 0)) # line3 ∥ line1 - line4 = Line(point(-2, 0, 0), point(2, 0, 0)) # line4 colinear with line1 - line5 = Line(point(0, -1, 0), point(0, 1, 0)) # line5 intersects line1 + line1 = Line(cart(-1, 0, 0), cart(1, 0, 0)) + line2 = Line(cart(0, -1, 1), cart(0, 1, 1)) # line2 ⟂ line1, z++ + line3 = Line(cart(-1, 1, 0), cart(1, 1, 0)) # line3 ∥ line1 + line4 = Line(cart(-2, 0, 0), cart(2, 0, 0)) # line4 colinear with line1 + line5 = Line(cart(0, -1, 0), cart(0, 1, 0)) # line5 intersects line1 @test evaluate(Euclidean(), line1, line2) ≈ T(1) * u"m" @test evaluate(Euclidean(), line1, line3) ≈ T(1) * u"m" @test evaluate(Euclidean(), line1, line4) ≈ T(0) * u"m" @test evaluate(Euclidean(), line1, line5) ≈ T(0) * u"m" - p1, p2 = point(1, 0), point(0, 1) + p1, p2 = cart(1, 0), cart(0, 1) @test evaluate(Chebyshev(), p1, p2) == T(1) * u"m" @test evaluate(Euclidean(), p1, p2) == T(√2) * u"m" diff --git a/test/domains.jl b/test/domains.jl index 4ecc1077d..30e048c06 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -1,29 +1,29 @@ @testset "Domain" begin # basic properties - dom = DummyDomain(point(0, 0)) + dom = DummyDomain(cart(0, 0)) @test embeddim(dom) == 2 @test Meshes.crs(dom) <: Cartesian{NoDatum} @test Meshes.lentype(dom) == ℳ @test !isparametrized(dom) # indexable/iterable interface - dom = DummyDomain(point(0, 0)) - @test dom[begin] == Ball(point(1, 1), T(1)) - @test dom[end] == Ball(point(3, 3), T(1)) + dom = DummyDomain(cart(0, 0)) + @test dom[begin] == Ball(cart(1, 1), T(1)) + @test dom[end] == Ball(cart(3, 3), T(1)) @test eltype(dom) <: Ball{2} @test length(dom) == 3 @test keys(dom) == 1:3 - @test collect(dom) == [Ball(point(i, i), T(1)) for i in 1:3] - @test dom[1:2] == [Ball(point(i, i), T(1)) for i in 1:2] + @test collect(dom) == [Ball(cart(i, i), T(1)) for i in 1:3] + @test dom[1:2] == [Ball(cart(i, i), T(1)) for i in 1:2] # coordinates of centroids - dom = DummyDomain(point(1, 1)) + dom = DummyDomain(cart(1, 1)) pts = centroid.(Ref(dom), 1:3) - @test pts == point.([(2, 2), (3, 3), (4, 4)]) + @test pts == cart.([(2, 2), (3, 3), (4, 4)]) # concatenation - dom1 = DummyDomain(point(0, 0)) - dom2 = DummyDomain(point(3, 3)) + dom1 = DummyDomain(cart(0, 0)) + dom2 = DummyDomain(cart(3, 3)) dom3 = PointSet(randpoint2(3)) @test vcat(dom1, dom2) == GeometrySet([collect(dom1); collect(dom2)]) @test vcat(dom2, dom3) == GeometrySet([collect(dom2); collect(dom3)]) @@ -35,7 +35,7 @@ dom = DummyDomain(Point(c)) @test datum(Meshes.crs(centroid(dom))) === WGS84Latest - dom = DummyDomain(point(0, 0)) + dom = DummyDomain(cart(0, 0)) @test sprint(show, dom) == "3 DummyDomain" @test sprint(show, MIME"text/plain"(), dom) == """ 3 DummyDomain diff --git a/test/hulls.jl b/test/hulls.jl index f9c34be91..f69d0be05 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -12,32 +12,32 @@ @test all(pts .∈ Ref(chul)) # corner cases - pts = point.([(0, 0)]) + pts = cart.([(0, 0)]) chul = hull(pts, method) - @test chul == point(0, 0) - pts = point.([(0, 1), (1, 0)]) + @test chul == cart(0, 0) + pts = cart.([(0, 1), (1, 0)]) chul = hull(pts, method) - @test chul == Segment(point(0, 1), point(1, 0)) - pts = point.([(1, 0), (0, 0), (0, 1)]) + @test chul == Segment(cart(0, 1), cart(1, 0)) + pts = cart.([(1, 0), (0, 0), (0, 1)]) chul = hull(pts, method) - @test vertices(chul) == point.([(0, 0), (1, 0), (0, 1)]) + @test vertices(chul) == cart.([(0, 0), (1, 0), (0, 1)]) # original point set is already in hull - pts = point.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) + pts = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) chul = hull(pts, method) verts = vertices(chul) - @test verts == point.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) + @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) # random points in interior do not affect result - p1 = point.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) - p2 = point.([0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10]) + p1 = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) + p2 = cart.([0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10]) pts = [p1; p2] chul = hull(pts, method) verts = vertices(chul) - @test verts == point.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) + @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) pts = - point.([ + cart.([ (0, 5), (1, 5), (1, 4), @@ -75,67 +75,67 @@ if method == GrahamScan() # simplifying rectangular hull / triangular - points = [point(i - 1, j - 1) for i in 1:11 for j in 1:11] + points = [cart(i - 1, j - 1) for i in 1:11 for j in 1:11] chull = hull(points, method) - @test vertices(chull) == [point(0, 0), point(10, 0), point(10, 10), point(0, 10)] + @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] for _ in 1:100 # test presence of interior points doesn't affect the result - push!(points, point(10 * rand(), 10 * rand())) + push!(points, cart(10 * rand(), 10 * rand())) end chull = hull(points, method) - @test vertices(chull) == [point(0, 0), point(10, 0), point(10, 10), point(0, 10)] + @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] - points = [point(-1, 0), point(0, 0), point(1, 0), point(0, 2)] + points = [cart(-1, 0), cart(0, 0), cart(1, 0), cart(0, 2)] chull = hull(points, method) - @test vertices(chull) == [point(-1, 0), point(1, 0), point(0, 2)] + @test vertices(chull) == [cart(-1, 0), cart(1, 0), cart(0, 2)] # degenerate cases - points = [point(0, 0), point(1, 0), point(2, 0)] + points = [cart(0, 0), cart(1, 0), cart(2, 0)] chull = hull(points, method) - @test vertices(chull) == (point(0, 0), point(2, 0)) + @test vertices(chull) == (cart(0, 0), cart(2, 0)) - points = [point(0, 0), point(1, 0), point(2, 0), point(10, 0), point(100, 0)] + points = [cart(0, 0), cart(1, 0), cart(2, 0), cart(10, 0), cart(100, 0)] chull = hull(points, method) - @test vertices(chull) == (point(0, 0), point(100, 0)) + @test vertices(chull) == (cart(0, 0), cart(100, 0)) # partially collinear points = [ - point(2, 0), - point(4, 0), - point(6, 0), - point(10, 0), - point(12, 1), - point(14, 3), - point(14, 6), - point(14, 9), - point(13, 10), - point(11, 11), - point(8, 12), - point(3, 11), - point(0, 8), - point(0, 7), - point(0, 6), - point(0, 5), - point(0, 4), - point(0, 3), - point(0, 2), - point(1, 0) + cart(2, 0), + cart(4, 0), + cart(6, 0), + cart(10, 0), + cart(12, 1), + cart(14, 3), + cart(14, 6), + cart(14, 9), + cart(13, 10), + cart(11, 11), + cart(8, 12), + cart(3, 11), + cart(0, 8), + cart(0, 7), + cart(0, 6), + cart(0, 5), + cart(0, 4), + cart(0, 3), + cart(0, 2), + cart(1, 0) ] chull = hull(points, method) truth = [ - point(0, 2), - point(1, 0), - point(10, 0), - point(12, 1), - point(14, 3), - point(14, 9), - point(13, 10), - point(11, 11), - point(8, 12), - point(3, 11), - point(0, 8) + cart(0, 2), + cart(1, 0), + cart(10, 0), + cart(12, 1), + cart(14, 3), + cart(14, 9), + cart(13, 10), + cart(11, 11), + cart(8, 12), + cart(3, 11), + cart(0, 8) ] @test vertices(chull) == truth - push!(points, point(4, 8), point(2, 6), point(6, 2), point(10, 8), point(8, 8), point(10, 6)) + push!(points, cart(4, 8), cart(2, 6), cart(6, 2), cart(10, 8), cart(8, 8), cart(10, 6)) chull = hull(points, method) @test vertices(chull) == truth end @@ -143,26 +143,26 @@ end @testset "convexhull" begin - @test convexhull(point(0, 0)) == point(0, 0) + @test convexhull(cart(0, 0)) == cart(0, 0) - @test convexhull(Box(point(0, 0), point(1, 1))) == Box(point(0, 0), point(1, 1)) + @test convexhull(Box(cart(0, 0), cart(1, 1))) == Box(cart(0, 0), cart(1, 1)) - @test convexhull(Ball(point(0, 0), T(1))) == Ball(point(0, 0), T(1)) - @test convexhull(Ball(point(1, 1), T(1))) == Ball(point(1, 1), T(1)) + @test convexhull(Ball(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) + @test convexhull(Ball(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) - @test convexhull(Sphere(point(0, 0), T(1))) == Ball(point(0, 0), T(1)) - @test convexhull(Sphere(point(1, 1), T(1))) == Ball(point(1, 1), T(1)) + @test convexhull(Sphere(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) + @test convexhull(Sphere(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) - b1 = Box(point(0, 0), point(1, 1)) - b2 = Box(point(-1, -1), point(0.5, 0.5)) - @test convexhull(Multi([b1, b2])) == PolyArea(point.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(-1, -1), cart(0.5, 0.5)) + @test convexhull(Multi([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) @test convexhull(GeometrySet([b1, b2])) == - PolyArea(point.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) - b1 = Ball(point(0, 0), T(1)) - b2 = Box(point(-1, -1), point(0, 0)) + b1 = Ball(cart(0, 0), T(1)) + b2 = Box(cart(-1, -1), cart(0, 0)) h = convexhull(Multi([b1, b2])) - @test point(-0.8, -0.8) ∈ h - @test point(0.2, 0.2) ∈ h + @test cart(-0.8, -0.8) ∈ h + @test cart(0.2, 0.2) ∈ h end end diff --git a/test/intersections.jl b/test/intersections.jl index bf7683e66..c8f318e1b 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -11,9 +11,9 @@ end @testset "Points" begin - p = point(0, 0) - q = point(-1, -1) - b = Box(point(0, 0), point(1, 1)) + p = cart(0, 0) + q = cart(-1, -1) + b = Box(cart(0, 0), cart(1, 1)) @test p ∩ p == p @test q ∩ q == q @test p ∩ b == b ∩ p == p @@ -23,153 +23,153 @@ @testset "Segments" begin # segments in 2D - s1 = Segment(point(0, 0), point(1, 0)) - s2 = Segment(point(0.5, 0.0), point(2, 0)) - @test s1 ∩ s2 ≈ Segment(point(0.5, 0.0), point(1, 0)) - @test s2 ∩ s1 ≈ Segment(point(0.5, 0.0), point(1, 0)) - - s1 = Segment(point(0, 0), point(1, -1)) - s2 = Segment(point(0.5, -0.5), point(1.5, -1.5)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0.5, -0.5), point(1, -1)) - - s1 = Segment(point(0, 0), point(1, 0)) - s2 = Segment(point(0, 0), point(0, 1)) - @test s1 ∩ s2 ≈ point(0, 0) - @test s2 ∩ s1 ≈ point(0, 0) - - s1 = Segment(point(0, 0), point(1, 0)) - s2 = Segment(point(0, 0), point(-1, 0)) - @test s1 ∩ s2 ≈ point(0, 0) - @test s2 ∩ s1 ≈ point(0, 0) - - s1 = Segment(point(0, 0), point(0, 1)) - s2 = Segment(point(0, 0), point(0, -1)) - @test s1 ∩ s2 ≈ point(0, 0) - @test s2 ∩ s1 ≈ point(0, 0) - - s1 = Segment(point(1, 1), point(1, 2)) - s2 = Segment(point(1, 1), point(1, 0)) - @test s1 ∩ s2 ≈ point(1, 1) - @test s2 ∩ s1 ≈ point(1, 1) - - s1 = Segment(point(1, 1), point(2, 1)) - s2 = Segment(point(1, 0), point(3, 0)) + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0.5, 0.0), cart(2, 0)) + @test s1 ∩ s2 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) + @test s2 ∩ s1 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) + + s1 = Segment(cart(0, 0), cart(1, -1)) + s2 = Segment(cart(0.5, -0.5), cart(1.5, -1.5)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0.5, -0.5), cart(1, -1)) + + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(0, 1)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(-1, 0)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(0, 0), cart(0, 1)) + s2 = Segment(cart(0, 0), cart(0, -1)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(1, 1), cart(1, 2)) + s2 = Segment(cart(1, 1), cart(1, 0)) + @test s1 ∩ s2 ≈ cart(1, 1) + @test s2 ∩ s1 ≈ cart(1, 1) + + s1 = Segment(cart(1, 1), cart(2, 1)) + s2 = Segment(cart(1, 0), cart(3, 0)) @test s1 ∩ s2 === nothing @test s2 ∩ s1 === nothing - s1 = Segment(point(0.181429364026879, 0.546811355144474), point(0.38282226144778, 0.107781953228536)) - s2 = Segment(point(0.412498700935005, 0.212081819871479), point(0.395936725690311, 0.252041094122474)) + s1 = Segment(cart(0.181429364026879, 0.546811355144474), cart(0.38282226144778, 0.107781953228536)) + s2 = Segment(cart(0.412498700935005, 0.212081819871479), cart(0.395936725690311, 0.252041094122474)) @test s1 ∩ s2 === nothing @test s2 ∩ s1 === nothing - s1 = Segment(point(1, 2), point(1, 0)) - s2 = Segment(point(1, 0), point(1, 1)) - @test s1 ∩ s2 ≈ Segment(point(1, 1), point(1, 0)) - @test s2 ∩ s1 ≈ Segment(point(1, 0), point(1, 1)) + s1 = Segment(cart(1, 2), cart(1, 0)) + s2 = Segment(cart(1, 0), cart(1, 1)) + @test s1 ∩ s2 ≈ Segment(cart(1, 1), cart(1, 0)) + @test s2 ∩ s1 ≈ Segment(cart(1, 0), cart(1, 1)) - s1 = Segment(point(0, 0), point(2, 0)) - s2 = Segment(point(-2, 0), point(-1, 0)) - s3 = Segment(point(-1, 0), point(-2, 0)) + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(-2, 0), cart(-1, 0)) + s3 = Segment(cart(-1, 0), cart(-2, 0)) @test s1 ∩ s2 === s2 ∩ s1 === nothing @test s1 ∩ s3 === s3 ∩ s1 === nothing - s1 = Segment(point(-1, 0), point(0, 0)) - s2 = Segment(point(0, 0), point(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(0, 0) + s1 = Segment(cart(-1, 0), cart(0, 0)) + s2 = Segment(cart(0, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0, 0) - s1 = Segment(point(-1, 0), point(1, 0)) - s2 = Segment(point(0, 0), point(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0, 0), point(1, 0)) + s1 = Segment(cart(-1, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) - s1 = Segment(point(0, 0), point(1, 0)) - s2 = Segment(point(0, 0), point(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(0, 0), point(1, 0)) + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) - s1 = Segment(point(0, 0), point(3, 0)) - s2 = Segment(point(1, 0), point(2, 0)) + s1 = Segment(cart(0, 0), cart(3, 0)) + s2 = Segment(cart(1, 0), cart(2, 0)) @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - s1 = Segment(point(0, 0), point(2, 0)) - s2 = Segment(point(1, 0), point(2, 0)) + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(1, 0), cart(2, 0)) @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - s1 = Segment(point(0, 0), point(2, 0)) - s2 = Segment(point(1, 0), point(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(point(1, 0), point(2, 0)) + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(1, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(1, 0), cart(2, 0)) - s1 = Segment(point(0, 0), point(2, 0)) - s2 = Segment(point(2, 0), point(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(2, 0) + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(2, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2, 0) - s1 = Segment(point(0, 0), point(2, 0)) - s2 = Segment(point(3, 0), point(4, 0)) + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(3, 0), cart(4, 0)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - s1 = Segment(point(2, 1), point(1, 2)) - s2 = Segment(point(1, 0), point(1, 1)) + s1 = Segment(cart(2, 1), cart(1, 2)) + s2 = Segment(cart(1, 0), cart(1, 1)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - s1 = Segment(point(1.5, 1.5), point(3.0, 1.5)) - s2 = Segment(point(3.0, 1.0), point(2.0, 2.0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(2.5, 1.5) - - s1 = Segment(point(0.94495744, 0.53224397), point(0.94798386, 0.5344541)) - s2 = Segment(point(0.94798386, 0.5344541), point(0.9472896, 0.5340202)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ point(0.94798386, 0.5344541) - - s₁ = Segment(point(0, 0), point(3, 4)) - s₂ = Segment(point(1, 2), point(3, -2)) - s₃ = Segment(point(2, 0), point(-2, 0)) - s₄ = Segment(point(0, 0), point(1, 2)) - s₅ = Segment(point(1, 2), point(3, 4)) - s₆ = Segment(point(-1, -4 / 3), point(0, 0)) - s₇ = Segment(point(1, 2), point(0, 4)) - s₈ = Segment(point(4, 16 / 3), point(3, 4)) - - s₉ = Segment(point(-1, 5), point(1, 4)) - s₁₀ = Segment(point(1, 4), point(-1, 5)) - s₁₁ = Segment(point(-2, 5.5), point(-0.8, 4.9)) - s₁₂ = Segment(point(-0.8, 4.9), point(-2, 5.5)) - s₁₃ = Segment(point(-0.5, 4.75), point(0.2, 4.4)) - s₁₄ = Segment(point(0.2, 4.4), point(-0.5, 4.75)) - s₁₅ = Segment(point(0.5, 4.25), point(1, 4)) - s₁₆ = Segment(point(1, 4), point(0.5, 4.25)) - s₁₇ = Segment(point(2, 3.5), point(1.5, 3.75)) - s₁₈ = Segment(point(1.5, 3.75), point(2, 3.5)) - - @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ point(1.2, 1.6) # CASE 1: Crossing Segments + s1 = Segment(cart(1.5, 1.5), cart(3.0, 1.5)) + s2 = Segment(cart(3.0, 1.0), cart(2.0, 2.0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2.5, 1.5) + + s1 = Segment(cart(0.94495744, 0.53224397), cart(0.94798386, 0.5344541)) + s2 = Segment(cart(0.94798386, 0.5344541), cart(0.9472896, 0.5340202)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0.94798386, 0.5344541) + + s₁ = Segment(cart(0, 0), cart(3, 4)) + s₂ = Segment(cart(1, 2), cart(3, -2)) + s₃ = Segment(cart(2, 0), cart(-2, 0)) + s₄ = Segment(cart(0, 0), cart(1, 2)) + s₅ = Segment(cart(1, 2), cart(3, 4)) + s₆ = Segment(cart(-1, -4 / 3), cart(0, 0)) + s₇ = Segment(cart(1, 2), cart(0, 4)) + s₈ = Segment(cart(4, 16 / 3), cart(3, 4)) + + s₉ = Segment(cart(-1, 5), cart(1, 4)) + s₁₀ = Segment(cart(1, 4), cart(-1, 5)) + s₁₁ = Segment(cart(-2, 5.5), cart(-0.8, 4.9)) + s₁₂ = Segment(cart(-0.8, 4.9), cart(-2, 5.5)) + s₁₃ = Segment(cart(-0.5, 4.75), cart(0.2, 4.4)) + s₁₄ = Segment(cart(0.2, 4.4), cart(-0.5, 4.75)) + s₁₅ = Segment(cart(0.5, 4.25), cart(1, 4)) + s₁₆ = Segment(cart(1, 4), cart(0.5, 4.25)) + s₁₇ = Segment(cart(2, 3.5), cart(1.5, 3.75)) + s₁₈ = Segment(cart(1.5, 3.75), cart(2, 3.5)) + + @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ cart(1.2, 1.6) # CASE 1: Crossing Segments @test intersection(s₁, s₂) |> type == Crossing @test intersection(s₂, s₁) |> type == Crossing - @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ point(0, 0) # CASE 2: EdgeTouching (s₁(0)) + @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ cart(0, 0) # CASE 2: EdgeTouching (s₁(0)) @test intersection(s₁, s₃) |> type == EdgeTouching @test intersection(s₃, s₁) |> type == EdgeTouching - @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ point(2, 0) # CASE 2: EdgeTouching (s₃(1)) + @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ cart(2, 0) # CASE 2: EdgeTouching (s₃(1)) @test intersection(s₂, s₃) |> type == EdgeTouching @test intersection(s₃, s₂) |> type == EdgeTouching - @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ point(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) + @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) @test intersection(s₁, s₄) |> type == CornerTouching @test intersection(s₄, s₁) |> type == CornerTouching - @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ point(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) + @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) @test intersection(s₂, s₄) |> type == CornerTouching @test intersection(s₄, s₂) |> type == CornerTouching - @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ point(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) + @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) @test intersection(s₂, s₄) |> type == CornerTouching @test intersection(s₄, s₂) |> type == CornerTouching - @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ point(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear + @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear @test intersection(s₁, s₆) |> type == CornerTouching @test intersection(s₆, s₁) |> type == CornerTouching - @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ point(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear + @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear @test intersection(s₂, s₇) |> type == CornerTouching @test intersection(s₇, s₂) |> type == CornerTouching - @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ point(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear + @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear @test intersection(s₁, s₈) |> type == CornerTouching @test intersection(s₈, s₁) |> type == CornerTouching @@ -181,17 +181,17 @@ @test intersection(s₉, s₁₀) |> type == Overlapping @test intersection(s₁₀, s₉) |> type == Overlapping - @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) + @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) @test intersection(s₉, s₁₁) |> type == Overlapping @test intersection(s₁₁, s₉) |> type == Overlapping - @test s₉ ∩ s₁₂ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) - @test s₁₂ ∩ s₉ ≈ Segment(point(-0.8, 4.9), point(-1, 5)) # flipped Points in Segment + @test s₉ ∩ s₁₂ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) + @test s₁₂ ∩ s₉ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # flipped Points in Segment @test intersection(s₉, s₁₂) |> type == Overlapping @test intersection(s₁₂, s₉) |> type == Overlapping - @test s₁₀ ∩ s₁₁ ≈ Segment(point(-0.8, 4.9), point(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) - @test s₁₁ ∩ s₁₀ ≈ Segment(point(-1, 5), point(-0.8, 4.9)) # flipped Points in Segment + @test s₁₀ ∩ s₁₁ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) + @test s₁₁ ∩ s₁₀ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # flipped Points in Segment @test intersection(s₁₀, s₁₁) |> type == Overlapping @test intersection(s₁₁, s₁₀) |> type == Overlapping @@ -251,29 +251,29 @@ @test intersection(s₃, s₁₀) |> type == NotIntersecting # segments in 3D - s1 = Segment(point(0.0, 0.0, 0.0), point(1.0, 0.0, 0.0)) - s2 = Segment(point(0.5, 1.0, 0.0), point(0.5, -1.0, 0.0)) - s3 = Segment(point(0.5, 0.0, 0.0), point(1.5, 0.0, 0.0)) - s4 = Segment(point(0.0, 1.0, 0.0), point(0.0, -2.0, 0.0)) - s5 = Segment(point(-1.0, 1.0, 0.0), point(2.0, -2.0, 0.0)) - s6 = Segment(point(0.0, 0.0, 0.0), point(0.0, 1.0, 0.0)) - s7 = Segment(point(-1.0, 1.0, 0.0), point(-1.0, -1.0, 0.0)) - s8 = Segment(point(-1.0, 1.0, 1.0), point(-1.0, -1.0, 1.0)) - s9 = Segment(point(0.5, 1.0, 1.0), point(0.5, -1.0, 1.0)) - s10 = Segment(point(0.0, 1.0, 0.0), point(1.0, 1.0, 0.0)) - s11 = Segment(point(1.5, 0.0, 0.0), point(2.5, 0.0, 0.0)) - s12 = Segment(point(1.0, 0.0, 0.0), point(2.0, 0.0, 0.0)) + s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) + s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) + s3 = Segment(cart(0.5, 0.0, 0.0), cart(1.5, 0.0, 0.0)) + s4 = Segment(cart(0.0, 1.0, 0.0), cart(0.0, -2.0, 0.0)) + s5 = Segment(cart(-1.0, 1.0, 0.0), cart(2.0, -2.0, 0.0)) + s6 = Segment(cart(0.0, 0.0, 0.0), cart(0.0, 1.0, 0.0)) + s7 = Segment(cart(-1.0, 1.0, 0.0), cart(-1.0, -1.0, 0.0)) + s8 = Segment(cart(-1.0, 1.0, 1.0), cart(-1.0, -1.0, 1.0)) + s9 = Segment(cart(0.5, 1.0, 1.0), cart(0.5, -1.0, 1.0)) + s10 = Segment(cart(0.0, 1.0, 0.0), cart(1.0, 1.0, 0.0)) + s11 = Segment(cart(1.5, 0.0, 0.0), cart(2.5, 0.0, 0.0)) + s12 = Segment(cart(1.0, 0.0, 0.0), cart(2.0, 0.0, 0.0)) @test intersection(s1, s2) |> type == Crossing - @test s1 ∩ s2 ≈ point(0.5, 0.0, 0.0) + @test s1 ∩ s2 ≈ cart(0.5, 0.0, 0.0) @test intersection(s1, s3) |> type == Overlapping - @test s1 ∩ s3 ≈ Segment(point(0.5, 0.0, 0.0), point(1.0, 0.0, 0.0)) + @test s1 ∩ s3 ≈ Segment(cart(0.5, 0.0, 0.0), cart(1.0, 0.0, 0.0)) @test intersection(s1, s4) |> type == EdgeTouching - @test s1 ∩ s4 ≈ point(0.0, 0.0, 0.0) + @test s1 ∩ s4 ≈ cart(0.0, 0.0, 0.0) @test intersection(s1, s5) |> type == EdgeTouching - @test s1 ∩ s5 ≈ point(0.0, 0.0, 0.0) + @test s1 ∩ s5 ≈ cart(0.0, 0.0, 0.0) @test intersection(s1, s6) |> type == CornerTouching - @test s1 ∩ s6 ≈ point(0.0, 0.0, 0.0) + @test s1 ∩ s6 ≈ cart(0.0, 0.0, 0.0) @test intersection(s1, s7) |> type == NotIntersecting @test isnothing(s1 ∩ s7) @test intersection(s1, s8) |> type == NotIntersecting @@ -285,38 +285,38 @@ @test intersection(s1, s11) |> type == NotIntersecting @test isnothing(s1 ∩ s11) @test intersection(s1, s12) |> type == CornerTouching - @test s1 ∩ s12 ≈ point(1.0, 0.0, 0.0) + @test s1 ∩ s12 ≈ cart(1.0, 0.0, 0.0) # precision test - s1 = Segment(point(2.0, 2.0), point(3.0, 1.0)) - s2 = Segment(point(2.12505, 1.87503), point(50000.0, 30000.0)) - s3 = Segment(point(2.125005, 1.875003), point(50000.0, 30000.0)) - s4 = Segment(point(2.125005, 1.875003), point(50002.125005, 30001.875003)) + s1 = Segment(cart(2.0, 2.0), cart(3.0, 1.0)) + s2 = Segment(cart(2.12505, 1.87503), cart(50000.0, 30000.0)) + s3 = Segment(cart(2.125005, 1.875003), cart(50000.0, 30000.0)) + s4 = Segment(cart(2.125005, 1.875003), cart(50002.125005, 30001.875003)) @test s1 ∩ s2 === s2 ∩ s1 === nothing - @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? point(2.125005, 1.875003) : nothing) - @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? point(2.125005, 1.875003) : nothing) + @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) + @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) # type stability tests - s1 = Segment(point(0, 0), point(1, 0)) - s2 = Segment(point(0.5, 0.0), point(2, 0)) + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0.5, 0.0), cart(2, 0)) @inferred someornone(s1, s2) - s1 = Segment(point(0.0, 0.0, 0.0), point(1.0, 0.0, 0.0)) - s2 = Segment(point(0.5, 1.0, 0.0), point(0.5, -1.0, 0.0)) + s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) + s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) @inferred someornone(s1, s2) # rays and segments in 2D - r₁ = Ray(point(1, 0), vector(2, 1)) - s₁ = Segment(point(0, 2), point(2, -1)) # Crossing - s₂ = Segment(point(0, 2), point(1, 0.5)) # NotIntersecting - s₃ = Segment(point(0, 2), point(0.5, -0.5)) # NotIntersecting - s₄ = Segment(point(0.5, 1), point(1.5, -1)) # EdgeTouching - s₅ = Segment(point(1.5, 0.25), point(1.5, 2)) # EdgeTouching - s₆ = Segment(point(1, 0), point(1, -1)) # CornerTouching - s₇ = Segment(point(0.5, -1), point(1, 0)) # CornerTouching + r₁ = Ray(cart(1, 0), vector(2, 1)) + s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing + s₂ = Segment(cart(0, 2), cart(1, 0.5)) # NotIntersecting + s₃ = Segment(cart(0, 2), cart(0.5, -0.5)) # NotIntersecting + s₄ = Segment(cart(0.5, 1), cart(1.5, -1)) # EdgeTouching + s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # EdgeTouching + s₆ = Segment(cart(1, 0), cart(1, -1)) # CornerTouching + s₇ = Segment(cart(0.5, -1), cart(1, 0)) # CornerTouching @test intersection(r₁, s₁) |> type == Crossing #CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ point(1.25, 0.125) + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(1.25, 0.125) @test intersection(r₁, s₂) |> type == NotIntersecting # CASE 5 @test r₁ ∩ s₂ === s₂ ∩ r₁ === nothing @test intersection(r₁, s₃) |> type == NotIntersecting # CASE 5 @@ -324,22 +324,22 @@ @test intersection(r₁, s₄) |> type == EdgeTouching # CASE 2 @test r₁ ∩ s₄ ≈ s₄ ∩ r₁ ≈ r₁(0) @test intersection(r₁, s₅) |> type == EdgeTouching # CASE 2 - @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ point(1.5, 0.25) + @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ cart(1.5, 0.25) @test intersection(r₁, s₆) |> type == CornerTouching # CASE 3 @test r₁ ∩ s₆ ≈ s₆ ∩ r₁ ≈ r₁(0) @test intersection(r₁, s₇) |> type == CornerTouching # CASE 3 @test r₁ ∩ s₇ ≈ s₇ ∩ r₁ ≈ r₁(0) - r₂ = Ray(point(3, 2), vector(1, 1)) - s₈ = Segment(point(4, 3), point(5, 4)) # Overlapping - s₉ = Segment(point(2.5, 1.5), point(3.3, 2.3)) # Overlapping s(1) - s₁₀ = Segment(point(3.6, 2.6), point(2.6, 1.6)) # Overlapping s(0) - s₁₁ = Segment(point(2.2, 1.2), point(3, 2)) # CornerTouching, colinear, s(1) - s₁₂ = Segment(point(3, 2), point(2.4, 1.4)) # CornerTouching, colinear, s(0) - s₁₃ = Segment(point(3, 2), point(3.1, 2.1)) # Overlapping s(0) = r(0) - s₁₄ = Segment(point(3.2, 2.2), point(3, 2)) # Overlapping s(1) = r(0) - s₁₅ = Segment(point(2, 1), point(1.6, 0.6)) # No Intersection, colinear - s₁₆ = Segment(point(3, 1), point(4, 2)) # No Intersection, parallel + r₂ = Ray(cart(3, 2), vector(1, 1)) + s₈ = Segment(cart(4, 3), cart(5, 4)) # Overlapping + s₉ = Segment(cart(2.5, 1.5), cart(3.3, 2.3)) # Overlapping s(1) + s₁₀ = Segment(cart(3.6, 2.6), cart(2.6, 1.6)) # Overlapping s(0) + s₁₁ = Segment(cart(2.2, 1.2), cart(3, 2)) # CornerTouching, colinear, s(1) + s₁₂ = Segment(cart(3, 2), cart(2.4, 1.4)) # CornerTouching, colinear, s(0) + s₁₃ = Segment(cart(3, 2), cart(3.1, 2.1)) # Overlapping s(0) = r(0) + s₁₄ = Segment(cart(3.2, 2.2), cart(3, 2)) # Overlapping s(1) = r(0) + s₁₅ = Segment(cart(2, 1), cart(1.6, 0.6)) # No Intersection, colinear + s₁₆ = Segment(cart(3, 1), cart(4, 2)) # No Intersection, parallel @test intersection(r₂, s₈) |> type == Overlapping # CASE 4 @test r₂ ∩ s₈ === s₈ ∩ r₂ === s₈ @test intersection(r₂, s₉) |> type == Overlapping # CASE 4 @@ -360,42 +360,42 @@ @test r₂ ∩ s₁₆ === s₁₆ ∩ r₂ === nothing # type stability tests - r₁ = Ray(point(0, 0), vector(1, 0)) - s₁ = Segment(point(-1, -1), point(-1, 1)) + r₁ = Ray(cart(0, 0), vector(1, 0)) + s₁ = Segment(cart(-1, -1), cart(-1, 1)) @inferred someornone(r₁, s₁) # 3D test - r₁ = Ray(point(1, 2, 3), vector(1, 2, 3)) - s₁ = Segment(point(1, 3, 5), point(3, 5, 7)) + r₁ = Ray(cart(1, 2, 3), vector(1, 2, 3)) + s₁ = Segment(cart(1, 3, 5), cart(3, 5, 7)) @test intersection(r₁, s₁) |> type === Crossing # CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ point(2, 4, 6) + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(2, 4, 6) - s₂ = Segment(point(0, 1, 2), point(2, 3, 4)) + s₂ = Segment(cart(0, 1, 2), cart(2, 3, 4)) @test intersection(r₁, s₂) |> type === EdgeTouching # CASE 2 @test r₁ ∩ s₂ == s₂ ∩ r₁ == r₁(0) - s₃ = Segment(point(0.23, 1, 2.3), point(1, 2, 3)) + s₃ = Segment(cart(0.23, 1, 2.3), cart(1, 2, 3)) @test intersection(r₁, s₃) |> type === CornerTouching # CASE 3 @test r₁ ∩ s₃ == s₃ ∩ r₁ == r₁(0) - s₄ = Segment(point(0, 0, 0), point(2, 4, 6)) + s₄ = Segment(cart(0, 0, 0), cart(2, 4, 6)) @test intersection(r₁, s₄) |> type === Overlapping # CASE 4 - @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(point(1, 2, 3), point(2, 4, 6)) + @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(cart(1, 2, 3), cart(2, 4, 6)) - s₅ = Segment(point(0, 0, 0), point(0.5, 1, 1.5)) + s₅ = Segment(cart(0, 0, 0), cart(0.5, 1, 1.5)) @test intersection(r₁, s₅) |> type === NotIntersecting # CASE 5 @test r₁ ∩ s₅ === s₅ ∩ r₁ === nothing - l₁ = Line(point(1, 0), point(3, 1)) - s₁ = Segment(point(0, 2), point(2, -1)) # Crossing - s₂ = Segment(point(0.5, 1), point(0, 0)) # NotIntersecting - s₃ = Segment(point(0, 2), point(-2, 1)) # NotIntersecting - s₄ = Segment(point(0.5, -1), point(1, 0)) # Touching - s₅ = Segment(point(1.5, 0.25), point(1.5, 2)) # Touching - s₆ = Segment(point(-3, -2), point(4, 1.5)) # Overlapping + l₁ = Line(cart(1, 0), cart(3, 1)) + s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing + s₂ = Segment(cart(0.5, 1), cart(0, 0)) # NotIntersecting + s₃ = Segment(cart(0, 2), cart(-2, 1)) # NotIntersecting + s₄ = Segment(cart(0.5, -1), cart(1, 0)) # Touching + s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # Touching + s₆ = Segment(cart(-3, -2), cart(4, 1.5)) # Overlapping @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ point(1.25, 0.125) + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125) @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 @@ -412,17 +412,17 @@ @inferred someornone(l₁, s₂) # 3d tests - l₁ = Line(point(1, 0, 1), point(3, 1, 1)) - s₁ = Segment(point(0, 2, 1), point(2, -1, 1)) # Crossing - s₂ = Segment(point(0.5, 1, 1), point(0, 0, 1)) # NotIntersecting - s₃ = Segment(point(0, 2, 1), point(-2, 1, 1)) # NotIntersecting - s₄ = Segment(point(0.5, -1, 1), point(1, 0, 1)) # Touching - s₅ = Segment(point(1.5, 0.25, 1), point(1.5, 2, 1)) # Touching - s₆ = Segment(point(-3, -2, 1), point(4, 1.5, 1)) # Overlapping - s₇ = Segment(point(0, 2, 1), point(2, -1, 1.1)) # NotIntersecting + l₁ = Line(cart(1, 0, 1), cart(3, 1, 1)) + s₁ = Segment(cart(0, 2, 1), cart(2, -1, 1)) # Crossing + s₂ = Segment(cart(0.5, 1, 1), cart(0, 0, 1)) # NotIntersecting + s₃ = Segment(cart(0, 2, 1), cart(-2, 1, 1)) # NotIntersecting + s₄ = Segment(cart(0.5, -1, 1), cart(1, 0, 1)) # Touching + s₅ = Segment(cart(1.5, 0.25, 1), cart(1.5, 2, 1)) # Touching + s₆ = Segment(cart(-3, -2, 1), cart(4, 1.5, 1)) # Overlapping + s₇ = Segment(cart(0, 2, 1), cart(2, -1, 1.1)) # NotIntersecting @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ point(1.25, 0.125, 1) + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125, 1) @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 @@ -437,9 +437,9 @@ @test l₁ ∩ s₇ === s₇ ∩ l₁ === nothing # degenerate segments - A = point(0.0, 0.0) - B = point(0.5, 0.0) - C = point(1.0, 0.0) + A = cart(0.0, 0.0) + B = cart(0.5, 0.0) + C = cart(1.0, 0.0) s₀ = Segment(A, C) s₁ = Segment(A, A) s₂ = Segment(B, B) @@ -471,16 +471,16 @@ @testset "Rays" begin # rays in 2D - r₁ = Ray(point(1, 0), vector(2, 1)) - r₂ = Ray(point(0, 2), vector(2, -3)) - r₃ = Ray(point(0.5, 1), vector(1, -2)) - r₄ = Ray(point(0, 2), vector(1, -3)) - r₅ = Ray(point(4, 1.5), vector(4, 2)) - r₆ = Ray(point(2, 0.5), vector(-0.5, -0.25)) - r₇ = Ray(point(4, 0), vector(0, 1)) + r₁ = Ray(cart(1, 0), vector(2, 1)) + r₂ = Ray(cart(0, 2), vector(2, -3)) + r₃ = Ray(cart(0.5, 1), vector(1, -2)) + r₄ = Ray(cart(0, 2), vector(1, -3)) + r₅ = Ray(cart(4, 1.5), vector(4, 2)) + r₆ = Ray(cart(2, 0.5), vector(-0.5, -0.25)) + r₇ = Ray(cart(4, 0), vector(0, 1)) @test intersection(r₁, r₂) |> type == Crossing #CASE 1 - @test r₁ ∩ r₂ ≈ point(1.25, 0.125) - @test r₁ ∩ r₇ ≈ point(4, 1.5) + @test r₁ ∩ r₂ ≈ cart(1.25, 0.125) + @test r₁ ∩ r₇ ≈ cart(4, 1.5) @test intersection(r₁, r₃) |> type == EdgeTouching #CASE 2 @test r₁ ∩ r₃ ≈ r₁(0) # origin of first ray @test r₅ ∩ r₇ ≈ r₅(0) @@ -499,14 +499,14 @@ @test r₁ ∩ r₄ === r₄ ∩ r₁ === nothing # lines and rays in 2D - l₁ = Line(point(0, 0), point(4, 5)) - r₁ = Ray(point(3, 4), vector(1, -2)) # crossing ray - r₂ = Ray(point(1, 1.25), vector(1, 0.3)) # touching ray - r₃ = Ray(point(-1, -1.25), vector(-1, -1.25)) # overlapping ray - r₄ = Ray(point(1, 3), vector(1, 1.25)) # parallel ray - r₅ = Ray(point(1, 1), vector(1, -1)) # no Intersection - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ point(3.0769230769230766, 3.846153846153846) # CASE 1 + l₁ = Line(cart(0, 0), cart(4, 5)) + r₁ = Ray(cart(3, 4), vector(1, -2)) # crossing ray + r₂ = Ray(cart(1, 1.25), vector(1, 0.3)) # touching ray + r₃ = Ray(cart(-1, -1.25), vector(-1, -1.25)) # overlapping ray + r₄ = Ray(cart(1, 3), vector(1, 1.25)) # parallel ray + r₅ = Ray(cart(1, 1), vector(1, -1)) # no Intersection + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846) # CASE 1 @test intersection(l₁, r₁) |> type === Crossing @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 @@ -527,15 +527,15 @@ # 3D tests # lines and rays in 3D - l₁ = Line(point(0, 0, 0.1), point(4, 5, 0.1)) - r₁ = Ray(point(3, 4, 0.1), vector(1, -2, 0)) # crossing ray - r₂ = Ray(point(1, 1.25, 0.1), vector(1, 0.3, 0)) # touching ray - r₃ = Ray(point(-1, -1.25, 0.1), vector(-1, -1.25, 0)) # overlapping ray - r₄ = Ray(point(1, 3, 0.1), vector(1, 1.25, 0)) # parallel ray - r₅ = Ray(point(1, 1, 0.1), vector(1, -1, 0)) # no Intersection - r₆ = Ray(point(3, 4, 0), vector(1, -2, 1)) # crossing ray - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ point(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 + l₁ = Line(cart(0, 0, 0.1), cart(4, 5, 0.1)) + r₁ = Ray(cart(3, 4, 0.1), vector(1, -2, 0)) # crossing ray + r₂ = Ray(cart(1, 1.25, 0.1), vector(1, 0.3, 0)) # touching ray + r₃ = Ray(cart(-1, -1.25, 0.1), vector(-1, -1.25, 0)) # overlapping ray + r₄ = Ray(cart(1, 3, 0.1), vector(1, 1.25, 0)) # parallel ray + r₅ = Ray(cart(1, 1, 0.1), vector(1, -1, 0)) # no Intersection + r₆ = Ray(cart(3, 4, 0), vector(1, -2, 1)) # crossing ray + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 @test intersection(l₁, r₁) |> type === Crossing @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 @@ -556,59 +556,59 @@ @testset "Lines" begin # lines in 2D - l1 = Line(point(0, 0), point(1, 0)) - l2 = Line(point(-1, -1), point(-1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ point(-1, 0) + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(-1, -1), cart(-1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(-1, 0) - l1 = Line(point(0, 0), point(1, 0)) - l2 = Line(point(0, 1), point(1, 1)) + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(0, 1), cart(1, 1)) @test l1 ∩ l2 === l2 ∩ l1 === nothing - l1 = Line(point(0, 0), point(1, 0)) - l2 = Line(point(1, 0), point(2, 0)) + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(1, 0), cart(2, 0)) @test l1 == l2 @test l1 ∩ l2 == l2 ∩ l1 == l1 # rounding errors - l1 = Line(point(3.0, 1.0), point(2.0, 2.0)) + l1 = Line(cart(3.0, 1.0), cart(2.0, 2.0)) for k in 1:1000 Δ = k * atol(T) - l2 = Line(point(1.5, 1.5 + Δ), point(3.0, 1.5 + Δ)) - p = point(2.5 - Δ, 1.5 + Δ) + l2 = Line(cart(1.5, 1.5 + Δ), cart(3.0, 1.5 + Δ)) + p = cart(2.5 - Δ, 1.5 + Δ) @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ p end # lines in 3D # not in same plane - l1 = Line(point(0, 0, 0), point(1, 0, 0)) - l2 = Line(point(1, 1, 1), point(1, 2, 1)) + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(1, 1, 1), cart(1, 2, 1)) @test l1 ∩ l2 == l2 ∩ l1 === nothing # in same plane but parallel - l1 = Line(point(0, 0, 0), point(1, 0, 0)) - l2 = Line(point(0, 1, 1), point(1, 1, 1)) + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(0, 1, 1), cart(1, 1, 1)) @test l1 ∩ l2 == l2 ∩ l1 === nothing # in same plane and colinear - l1 = Line(point(0, 0, 0), point(1, 0, 0)) - l2 = Line(point(2, 0, 0), point(3, 0, 0)) + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(2, 0, 0), cart(3, 0, 0)) @test l1 ∩ l2 == l2 ∩ l1 == l1 # crossing in one point - l1 = Line(point(1, 2, 3), point(2, 1, 0)) - l2 = Line(point(1, 2, 3), point(1, 1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ point(1, 2, 3) + l1 = Line(cart(1, 2, 3), cart(2, 1, 0)) + l2 = Line(cart(1, 2, 3), cart(1, 1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(1, 2, 3) # type stability tests - l1 = Line(point(0, 0), point(1, 0)) - l2 = Line(point(-1, -1), point(-1, 1)) + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(-1, -1), cart(-1, 1)) @inferred someornone(l1, l2) end @testset "Chains" begin # https://github.com/JuliaGeometry/Meshes.jl/issues/644 - r = Rope(point(0, 0), point(1, 1)) - @test r ∩ r == GeometrySet([Segment(point(0, 0), point(1, 1))]) + r = Rope(cart(0, 0), cart(1, 1)) + @test r ∩ r == GeometrySet([Segment(cart(0, 0), cart(1, 1))]) @inferred someornone(r, r) end @@ -617,153 +617,153 @@ # SEGMENTS # --------- - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting segment and plane - s = Segment(point(0, 0, 0), point(0, 2, 2)) + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) @test intersection(s, p) |> type == Crossing - @test s ∩ p == point(0, 1, 1) + @test s ∩ p == cart(0, 1, 1) # intersecting segment and plane with λ ≈ 0 - s = Segment(point(0, 0, 1), point(0, 2, 2)) + s = Segment(cart(0, 0, 1), cart(0, 2, 2)) @test intersection(s, p) |> type == Touching - @test s ∩ p == point(0, 0, 1) + @test s ∩ p == cart(0, 0, 1) # intersecting segment and plane with λ ≈ 1 - s = Segment(point(0, 0, 2), point(0, 2, 1)) + s = Segment(cart(0, 0, 2), cart(0, 2, 1)) @test intersection(s, p) |> type == Touching - @test s ∩ p == point(0, 2, 1) + @test s ∩ p == cart(0, 2, 1) # segment contained within plane - s = Segment(point(0, 0, 1), point(0, -2, 1)) + s = Segment(cart(0, 0, 1), cart(0, -2, 1)) @test intersection(s, p) |> type == Overlapping @test s ∩ p == s # segment below plane, non-intersecting - s = Segment(point(0, 0, 0), point(0, -2, -2)) + s = Segment(cart(0, 0, 0), cart(0, -2, -2)) @test intersection(s, p) |> type == NotIntersecting @test isnothing(s ∩ p) # segment parallel to plane, offset, non-intersecting - s = Segment(point(0, 0, -1), point(0, -2, -1)) + s = Segment(cart(0, 0, -1), cart(0, -2, -1)) @test intersection(s, p) |> type == NotIntersecting @test isnothing(s ∩ p) # plane as first argument - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - s = Segment(point(0, 0, 0), point(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) @test intersection(p, s) |> type == Crossing - @test s ∩ p == p ∩ s == point(0, 1, 1) + @test s ∩ p == p ∩ s == cart(0, 1, 1) # type stability tests - s = Segment(point(0, 0, 0), point(0, 2, 2)) - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) @inferred someornone(s, p) # ----- # RAYS # ----- - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting ray and plane - r = Ray(point(0, 0, 0), vector(0, 2, 2)) + r = Ray(cart(0, 0, 0), vector(0, 2, 2)) @test intersection(r, p) |> type == Crossing - @test r ∩ p == point(0, 1, 1) + @test r ∩ p == cart(0, 1, 1) # intersecting ray and plane with λ ≈ 0 - r = Ray(point(0, 0, 1), vector(0, 2, 1)) + r = Ray(cart(0, 0, 1), vector(0, 2, 1)) @test intersection(r, p) |> type == Touching - @test r ∩ p == point(0, 0, 1) + @test r ∩ p == cart(0, 0, 1) # intersecting ray and plane with λ ≈ 1 (only case where Ray different to Segment) - r = Ray(point(0, 0, 2), vector(0, 2, -1)) + r = Ray(cart(0, 0, 2), vector(0, 2, -1)) @test intersection(r, p) |> type == Crossing - @test r ∩ p == point(0, 2, 1) + @test r ∩ p == cart(0, 2, 1) # ray contained within plane - r = Ray(point(0, 0, 1), vector(0, -2, 0)) + r = Ray(cart(0, 0, 1), vector(0, -2, 0)) @test intersection(r, p) |> type == Overlapping @test r ∩ p == r # ray below plane, non-intersecting - r = Ray(point(0, 0, 0), vector(0, -2, -2)) + r = Ray(cart(0, 0, 0), vector(0, -2, -2)) @test intersection(r, p) |> type == NotIntersecting @test isnothing(r ∩ p) # ray parallel to plane, offset, non-intersecting - r = Ray(point(0, 0, -1), vector(0, -2, 0)) + r = Ray(cart(0, 0, -1), vector(0, -2, 0)) @test intersection(r, p) |> type == NotIntersecting @test isnothing(r ∩ p) # plane as first argument - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - r = Ray(point(0, 0, 0), vector(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + r = Ray(cart(0, 0, 0), vector(0, 2, 2)) @test intersection(p, r) |> type == Crossing - @test r ∩ p == p ∩ r == point(0, 1, 1) + @test r ∩ p == p ∩ r == cart(0, 1, 1) # ------ # LINES # ------ - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) # intersecting line and plane - l = Line(point(0, 0, 0), point(0, 2, 2)) + l = Line(cart(0, 0, 0), cart(0, 2, 2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == point(0, 1, 1) + @test l ∩ p == cart(0, 1, 1) # intersecting line and plane with λ ≈ 0 - l = Line(point(0, 0, 1), point(0, 2, 2)) + l = Line(cart(0, 0, 1), cart(0, 2, 2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == point(0, 0, 1) + @test l ∩ p == cart(0, 0, 1) # intersecting line and plane with λ ≈ 1 - l = Line(point(0, 0, 2), point(0, 2, 1)) + l = Line(cart(0, 0, 2), cart(0, 2, 1)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == point(0, 2, 1) + @test l ∩ p == cart(0, 2, 1) # line contained within plane - l = Line(point(0, 0, 1), point(0, -2, 1)) + l = Line(cart(0, 0, 1), cart(0, -2, 1)) @test intersection(l, p) |> type == Overlapping @test l ∩ p == l # line below plane, non-intersecting - l = Line(point(0, 0, 0), point(0, -2, -2)) + l = Line(cart(0, 0, 0), cart(0, -2, -2)) @test intersection(l, p) |> type == Crossing - @test l ∩ p == point(0, 1, 1) + @test l ∩ p == cart(0, 1, 1) # line parallel to plane, offset, non-intersecting - l = Line(point(0, 0, -1), point(0, -2, -1)) + l = Line(cart(0, 0, -1), cart(0, -2, -1)) @test intersection(l, p) |> type == NotIntersecting @test isnothing(l ∩ p) # plane as first argument - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - l = Line(point(0, 0, 0), point(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + l = Line(cart(0, 0, 0), cart(0, 2, 2)) @test intersection(p, l) |> type == Crossing - @test l ∩ p == p ∩ l == point(0, 1, 1) + @test l ∩ p == p ∩ l == cart(0, 1, 1) # type stability tests - l = Line(point(0, 0, 0), point(0, 2, 2)) - p = Plane(point(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + l = Line(cart(0, 0, 0), cart(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) @inferred someornone(l, p) # ------ # PLANES # ------ - p1 = Plane(point(0, 0, 0), vector(0, 0, 1)) + p1 = Plane(cart(0, 0, 0), vector(0, 0, 1)) # p1 parallel to p2 - p2 = Plane(point(0, 0, 1), vector(0, 0, 1)) + p2 = Plane(cart(0, 0, 1), vector(0, 0, 1)) @test intersection(p1, p2) |> type == NotIntersecting @test isnothing(p1 ∩ p2) # p1 intersects p2 - p2 = Plane(point(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) + p2 = Plane(cart(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) @test intersection(p1, p2) |> type == Intersecting - @test p1 ∩ p2 == Line(point(1, 0, 0), point(1, 1, 0)) + @test p1 ∩ p2 == Line(cart(1, 0, 0), cart(1, 1, 0)) # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) @@ -774,62 +774,62 @@ end @testset "Boxes" begin - b1 = Box(point(0, 0), point(1, 1)) - b2 = Box(point(0.5, 0.5), point(2, 2)) - b3 = Box(point(2, 2), point(3, 3)) - b4 = Box(point(1, 1), point(2, 2)) - b5 = Box(point(1.0, 0.5), point(2, 2)) - b6 = Box(point(0, 2), point(1, 3)) - b7 = Box(point(0, 1), point(1, 2)) - b8 = Box(point(0, -1), point(1, 0)) - b9 = Box(point(1, 0), point(2, 1)) - b10 = Box(point(-1, 0), point(0, 1)) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(0.5, 0.5), cart(2, 2)) + b3 = Box(cart(2, 2), cart(3, 3)) + b4 = Box(cart(1, 1), cart(2, 2)) + b5 = Box(cart(1.0, 0.5), cart(2, 2)) + b6 = Box(cart(0, 2), cart(1, 3)) + b7 = Box(cart(0, 1), cart(1, 2)) + b8 = Box(cart(0, -1), cart(1, 0)) + b9 = Box(cart(1, 0), cart(2, 1)) + b10 = Box(cart(-1, 0), cart(0, 1)) @test intersection(b1, b2) |> type == Overlapping - @test b1 ∩ b2 == Box(point(0.5, 0.5), point(1, 1)) + @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) @test intersection(b1, b3) |> type == NotIntersecting @test isnothing(b1 ∩ b3) @test intersection(b1, b4) |> type == CornerTouching - @test b1 ∩ b4 == point(1, 1) + @test b1 ∩ b4 == cart(1, 1) @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(point(1.0, 0.5), point(1, 1)) + @test b1 ∩ b5 == Box(cart(1.0, 0.5), cart(1, 1)) @test intersection(b1, b6) |> type == NotIntersecting @test isnothing(b1 ∩ b6) @test intersection(b1, b7) |> type == Touching - @test b1 ∩ b7 == Box(point(0, 1), point(1, 1)) + @test b1 ∩ b7 == Box(cart(0, 1), cart(1, 1)) @test intersection(b1, b8) |> type == Touching - @test b1 ∩ b8 == Box(point(0, 0), point(1, 0)) + @test b1 ∩ b8 == Box(cart(0, 0), cart(1, 0)) @test intersection(b1, b9) |> type == Touching - @test b1 ∩ b9 == Box(point(1, 0), point(1, 1)) + @test b1 ∩ b9 == Box(cart(1, 0), cart(1, 1)) @test intersection(b1, b10) |> type == Touching - @test b1 ∩ b10 == Box(point(0, 0), point(0, 1)) + @test b1 ∩ b10 == Box(cart(0, 0), cart(0, 1)) # more touching examples - b1 = Box(point(0, 0), point(1, 1)) - b2 = Box(point(1.0, 0.5), point(2, 1)) - b3 = Box(point(-1, 0), point(0.0, 0.5)) - b4 = Box(point(0, 1), point(0.5, 2.0)) - b5 = Box(point(0.5, -1.0), point(1, 0)) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(1.0, 0.5), cart(2, 1)) + b3 = Box(cart(-1, 0), cart(0.0, 0.5)) + b4 = Box(cart(0, 1), cart(0.5, 2.0)) + b5 = Box(cart(0.5, -1.0), cart(1, 0)) @test intersection(b1, b2) |> type == Touching - @test b1 ∩ b2 == Box(point(1.0, 0.5), point(1, 1)) + @test b1 ∩ b2 == Box(cart(1.0, 0.5), cart(1, 1)) @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(point(0.0, 0.0), point(0.0, 0.5)) + @test b1 ∩ b3 == Box(cart(0.0, 0.0), cart(0.0, 0.5)) @test intersection(b1, b4) |> type == Touching - @test b1 ∩ b4 == Box(point(0.0, 1.0), point(0.5, 1.0)) + @test b1 ∩ b4 == Box(cart(0.0, 1.0), cart(0.5, 1.0)) @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(point(0.5, 0.0), point(1.0, 0.0)) + @test b1 ∩ b5 == Box(cart(0.5, 0.0), cart(1.0, 0.0)) # tricky examples with degenerate boxes - b1 = Box(point(0, 0, 0), point(2, 2, 0)) - b2 = Box(point(3, 0, 0), point(5, 2, 0)) - b3 = Box(point(1, 0, 0), point(3, 2, 0)) + b1 = Box(cart(0, 0, 0), cart(2, 2, 0)) + b2 = Box(cart(3, 0, 0), cart(5, 2, 0)) + b3 = Box(cart(1, 0, 0), cart(3, 2, 0)) @test intersection(b1, b2) |> type == NotIntersecting @test isnothing(b1 ∩ b2) @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(point(1, 0, 0), point(2, 2, 0)) + @test b1 ∩ b3 == Box(cart(1, 0, 0), cart(2, 2, 0)) # type stability tests - b1 = Box(point(0, 0), point(1, 1)) - b2 = Box(point(0.5, 0.5), point(2, 2)) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(0.5, 0.5), cart(2, 2)) @inferred someornone(b1, b2) # datum propagation @@ -842,27 +842,27 @@ @test datum(Meshes.crs(b1 ∩ b2)) === WGS84Latest # Ray-Box intersection - b = Box(point(0, 0, 0), point(1, 1, 1)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) - r = Ray(point(0, 0, 0), vector(1, 1, 1)) + r = Ray(cart(0, 0, 0), vector(1, 1, 1)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(point(0, 0, 0), point(1, 1, 1)) + @test r ∩ b == Segment(cart(0, 0, 0), cart(1, 1, 1)) - r = Ray(point(-0.5, 0, 0), vector(1.0, 1.0, 1.0)) + r = Ray(cart(-0.5, 0, 0), vector(1.0, 1.0, 1.0)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(point(0.0, 0.5, 0.5), point(0.5, 1.0, 1.0)) + @test r ∩ b == Segment(cart(0.0, 0.5, 0.5), cart(0.5, 1.0, 1.0)) - r = Ray(point(3.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) + r = Ray(cart(3.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == NotIntersecting - r = Ray(point(2.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) + r = Ray(cart(2.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == Touching - @test r ∩ b == point(1.0, 1.0, 0.5) + @test r ∩ b == cart(1.0, 1.0, 0.5) # the ray on a face of the box, got NaN in calculation - r = Ray(point(1.5, 0.0, 0.0), vector(-1.0, 1.0, 0.0)) + r = Ray(cart(1.5, 0.0, 0.0), vector(-1.0, 1.0, 0.0)) @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(point(1.0, 0.5, 0.0), point(0.5, 1.0, 0.0)) + @test r ∩ b == Segment(cart(1.0, 0.5, 0.0), cart(0.5, 1.0, 0.0)) end @testset "Triangles" begin @@ -871,325 +871,325 @@ reverse_segment(s) = Segment(vertices(s)[2], vertices(s)[1]) # intersections with triangle lying in XY plane - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) # intersects through t - s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == point(0.2, 0.2, 0.0) + @test s ∩ t == cart(0.2, 0.2, 0.0) # intersects at a vertex of t - s = Segment(point(0.0, 0.0, 1.0), point(0.0, 0.0, -1.0)) + s = Segment(cart(0.0, 0.0, 1.0), cart(0.0, 0.0, -1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == point(0.0, 0.0, 0.0) + @test s ∩ t == cart(0.0, 0.0, 0.0) # normal to, doesn't intersect with t - s = Segment(point(0.9, 0.9, 1.0), point(0.9, 0.9, -1.0)) + s = Segment(cart(0.9, 0.9, 1.0), cart(0.9, 0.9, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # coplanar, doesn't intersect with t - s = Segment(point(-0.2, -0.2, 0.0), point(1.2, -0.2, 0.0)) + s = Segment(cart(-0.2, -0.2, 0.0), cart(1.2, -0.2, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, above, doesn't intersect with t - s = Segment(point(-0.2, 0.2, 1.0), point(1.2, 0.2, 1.0)) + s = Segment(cart(-0.2, 0.2, 1.0), cart(1.2, 0.2, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, below, doesn't intersect with t - s = Segment(point(-0.2, 0.2, -1.0), point(1.2, 0.2, -1.0)) + s = Segment(cart(-0.2, 0.2, -1.0), cart(1.2, 0.2, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # coplanar, within bounding box of t, no intersection - s = Segment(point(0.7, 0.8, 0.0), point(0.8, 0.7, 0.0)) + s = Segment(cart(0.7, 0.8, 0.0), cart(0.8, 0.7, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment above and to right of t, no intersection - s = Segment(point(1.0, 1.0, 0.0), point(1.0, 1.0, 1.0)) + s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment below t, no intersection - s = Segment(point(0.5, -1.0, 0.0), point(0.5, -1.0, 1.0)) + s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment left of t, no intersection - s = Segment(point(-1.0, 0.5, 0.0), point(-1.0, 0.5, 1.0)) + s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment above and to right of t, no intersection - s = Segment(point(1.0, 1.0, 0.0), point(1.0, 1.0, -1.0)) + s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment below t, no intersection - s = Segment(point(0.5, -1.0, 0.0), point(0.5, -1.0, -1.0)) + s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment left of t, no intersection - s = Segment(point(-1.0, 0.5, 0.0), point(-1.0, 0.5, -1.0)) + s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, -1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) @test intersection(reverse_segment(s), t) |> type == NotIntersecting @test isnothing(reverse_segment(s) ∩ t) # segment above and to right of t, no intersection - s = Segment(point(1.0, 1.0, 1.0), point(1.0, 1.0, 0.0)) + s = Segment(cart(1.0, 1.0, 1.0), cart(1.0, 1.0, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment below t, no intersection - s = Segment(point(0.5, -1.0, 1.0), point(0.5, -1.0, 0.0)) + s = Segment(cart(0.5, -1.0, 1.0), cart(0.5, -1.0, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # segment left of t, no intersection - s = Segment(point(-1.0, 0.5, 1.0), point(-1.0, 0.5, 0.0)) + s = Segment(cart(-1.0, 0.5, 1.0), cart(-1.0, 0.5, 0.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # intersections with an inclined inclined triangle t - t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) # doesn't reach t, no intersection - s = Segment(point(0.5, 0.5, 1.9), point(0.5, 0.5, 1.8)) + s = Segment(cart(0.5, 0.5, 1.9), cart(0.5, 0.5, 1.8)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # parallel, offset from t, no intersection - s = Segment(point(0.0, 0.5, 1.0), point(1.0, 0.5, 1.0)) + s = Segment(cart(0.0, 0.5, 1.0), cart(1.0, 0.5, 1.0)) @test intersection(s, t) |> type == NotIntersecting @test isnothing(s ∩ t) # triangle as first argument - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) - s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) @test intersection(t, s) |> type == Intersecting - @test s ∩ t == t ∩ s == point(0.2, 0.2, 0.0) + @test s ∩ t == t ∩ s == cart(0.2, 0.2, 0.0) # type stability tests - s = Segment(point(0.2, 0.2, 1.0), point(0.2, 0.2, -1.0)) - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) @inferred someornone(s, t) # https://github.com/JuliaGeometry/Meshes.jl/issues/728 - s = Segment(point(0.5, 0.5, 0.0), point(0.5, 0.5, 2.0)) - t = Triangle(point(1.0, 0.0, 0.0), point(0.0, 1.0, 0.0), point(0.0, 0.0, 1.0)) + s = Segment(cart(0.5, 0.5, 0.0), cart(0.5, 0.5, 2.0)) + t = Triangle(cart(1.0, 0.0, 0.0), cart(0.0, 1.0, 0.0), cart(0.0, 0.0, 1.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == point(0.5, 0.5, 0.0) - s = Segment(point(0.5, 0.5, 2.0), point(0.5, 0.5, 0.0)) + @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) + s = Segment(cart(0.5, 0.5, 2.0), cart(0.5, 0.5, 0.0)) @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == point(0.5, 0.5, 0.0) + @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) # Intersection for a triangle and a ray - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) # intersects through t - r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Crossing - @test r ∩ t == point(0.2, 0.2, 0.0) + @test r ∩ t == cart(0.2, 0.2, 0.0) # origin of ray intersects with middle of triangle - r = Ray(point(0.2, 0.2, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.2, 0.2, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Touching - @test r ∩ t == point(0.2, 0.2, 0.0) + @test r ∩ t == cart(0.2, 0.2, 0.0) # Special case: the direction vector is not length enough to cross triangle - r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, -0.00001)) + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -0.00001)) @test intersection(r, t) |> type == Crossing if T == Float64 - @test r ∩ t ≈ point(0.2, 0.2, 0.0) + @test r ∩ t ≈ cart(0.2, 0.2, 0.0) end # Special case: reverse direction vector should not hit the triangle - r = Ray(point(0.2, 0.2, 1.0), vector(0.0, 0.0, 1.0)) + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # intersects at a vertex of t - r = Ray(point(0.0, 0.0, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.0, 0.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == CornerCrossing - @test r ∩ t ≈ point(0.0, 0.0, 0.0) + @test r ∩ t ≈ cart(0.0, 0.0, 0.0) # normal to, doesn't intersect with t - r = Ray(point(0.9, 0.9, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.9, 0.9, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # coplanar, doesn't intersect with t - r = Ray(point(-0.2, -0.2, 0.0), vector(1.0, 0.0, 0.0)) + r = Ray(cart(-0.2, -0.2, 0.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # parallel, above, doesn't intersect with t - r = Ray(point(-0.2, 0.2, 1.0), vector(1.0, 0.0, 0.0)) + r = Ray(cart(-0.2, 0.2, 1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # parallel, below, doesn't intersect with t - r = Ray(point(-0.2, 0.2, -1.0), vector(1.0, 0.0, 0.0)) + r = Ray(cart(-0.2, 0.2, -1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # coplanar, within bounding box of t, no intersection - r = Ray(point(0.7, 0.8, 0.0), vector(1.0, -1.0, 0.0)) + r = Ray(cart(0.7, 0.8, 0.0), vector(1.0, -1.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(point(1.0, 1.0, 0.0), vector(0.0, 0.0, 1.0)) + r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(point(0.5, -1.0, 0.0), vector(0.0, 0.0, 1.0)) + r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(point(-1.0, 0.5, 0.0), vector(0.0, 0.0, 1.0)) + r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, 1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(point(1.0, 1.0, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(point(0.5, -1.0, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(point(-1.0, 0.5, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray above and to right of t, no intersection - r = Ray(point(1.0, 1.0, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(1.0, 1.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray below t, no intersection - r = Ray(point(0.5, -1.0, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.5, -1.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # ray left of t, no intersection - r = Ray(point(-1.0, 0.5, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(-1.0, 0.5, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # intersections with an inclined inclined triangle t - t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) # doesn't reach t, but a ray can hit the triangle - r = Ray(point(0.5, 0.5, 1.9), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.5, 0.5, 1.9), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == Crossing - @test r ∩ t ≈ point(0.5, 0.5, 0.5) + @test r ∩ t ≈ cart(0.5, 0.5, 0.5) # parallel, offset from t, no intersection - r = Ray(point(0.0, 0.5, 1.0), vector(1.0, 0.0, 0.0)) + r = Ray(cart(0.0, 0.5, 1.0), vector(1.0, 0.0, 0.0)) @test intersection(r, t) |> type == NotIntersecting @test isnothing(r ∩ t) # origin of ray intersects with vertex of triangle - r = Ray(point(0.0, 0.0, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.0, 0.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == CornerTouching - @test r ∩ t ≈ point(0.0, 0.0, 0.0) + @test r ∩ t ≈ cart(0.0, 0.0, 0.0) # origin of ray intersects with edge of triangle - r = Ray(point(0.5, 0.0, 0.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.5, 0.0, 0.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == EdgeTouching - @test r ∩ t ≈ point(0.5, 0.0, 0.0) + @test r ∩ t ≈ cart(0.5, 0.0, 0.0) # ray intersects with edge of triangle - r = Ray(point(0.5, 0.0, 1.0), vector(0.0, 0.0, -1.0)) + r = Ray(cart(0.5, 0.0, 1.0), vector(0.0, 0.0, -1.0)) @test intersection(r, t) |> type == EdgeCrossing - @test r ∩ t ≈ point(0.5, 0.0, 0.0) + @test r ∩ t ≈ cart(0.5, 0.0, 0.0) end @testset "Ngons" begin o = Octagon( - point(0.0, 0.0, 1.0), - point(0.5, -0.5, 0.0), - point(1.0, 0.0, 0.0), - point(1.5, 0.5, -0.5), - point(1.0, 1.0, 0.0), - point(0.5, 1.5, 0.0), - point(0.0, 1.0, 0.0), - point(-0.5, 0.5, 0.0) + cart(0.0, 0.0, 1.0), + cart(0.5, -0.5, 0.0), + cart(1.0, 0.0, 0.0), + cart(1.5, 0.5, -0.5), + cart(1.0, 1.0, 0.0), + cart(0.5, 1.5, 0.0), + cart(0.0, 1.0, 0.0), + cart(-0.5, 0.5, 0.0) ) - r = Ray(point(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) + r = Ray(cart(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) @test intersection(r, o) |> type == Intersecting - @test r ∩ o == PointSet([point(0.0, 0.0, 0.0)]) + @test r ∩ o == PointSet([cart(0.0, 0.0, 0.0)]) - r = Ray(point(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) + r = Ray(cart(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) @test intersection(r, o) |> type == NotIntersecting @test isnothing(r ∩ o) end @testset "Polygons" begin # triangle - poly = Triangle(point(6, 2), point(3, 5), point(0, 2)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) @test intersection(poly, other) |> type == Intersecting - @test all(vertices(poly ∩ other) .≈ [point(5, 3), point(4, 4), point(2, 4), point(0, 2), point(5, 2)]) + @test all(vertices(poly ∩ other) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) # octagon poly = - Octagon(point(8, -2), point(8, 5), point(2, 5), point(4, 3), point(6, 3), point(4, 1), point(2, 1), point(2, -2)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) @test intersection(poly, other) |> type == Intersecting @test all( vertices(poly ∩ other) .≈ [ - point(3, 4), - point(4, 3), - point(5, 3), - point(5, 2), - point(4, 1), - point(2, 1), - point(2, 0), - point(5, 0), - point(5, 4) + cart(3, 4), + cart(4, 3), + cart(5, 3), + cart(5, 2), + cart(4, 1), + cart(2, 1), + cart(2, 0), + cart(5, 0), + cart(5, 4) ] ) # inside - poly = Quadrangle(point(1, 0), point(1, 1), point(0, 1), point(0, 0)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) @test intersection(poly, other) |> type == Intersecting @test all(vertices(poly ∩ other) .≈ vertices(poly)) # outside - poly = Quadrangle(point(7, 6), point(7, 7), point(6, 7), point(6, 6)) - other = Quadrangle(point(5, 0), point(5, 4), point(0, 4), point(0, 0)) + poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) @test intersection(poly, other) |> type == NotIntersecting @test isnothing(poly ∩ other) # convex and non-convex polygons - quad = Quadrangle(point(0, 0), point(0.1, 0.0), point(0.1, 0.1), point(0.0, 0.1)) - poly = PolyArea(point(0, 0), point(2, 0), point(1, 1), point(1, 0.5)) + quad = Quadrangle(cart(0, 0), cart(0.1, 0.0), cart(0.1, 0.1), cart(0.0, 0.1)) + poly = PolyArea(cart(0, 0), cart(2, 0), cart(1, 1), cart(1, 0.5)) @test intersection(quad, poly) |> type == Intersecting - @test all(vertices(quad ∩ poly) .≈ [point(0, 0), point(0.1, 0), point(0.1, 0.05)]) + @test all(vertices(quad ∩ poly) .≈ [cart(0, 0), cart(0.1, 0), cart(0.1, 0.05)]) end @testset "Domains" begin grid = cartgrid(4, 4) pset = PointSet(centroid.(grid)) - ball = Ball(point(0, 0), T(1)) + ball = Ball(cart(0, 0), T(1)) @test pset ∩ pset == pset @test pset ∩ grid == grid ∩ pset == pset - @test pset ∩ ball == ball ∩ pset == PointSet(point(0.5, 0.5)) + @test pset ∩ ball == ball ∩ pset == PointSet(cart(0.5, 0.5)) end end diff --git a/test/matrices.jl b/test/matrices.jl index c6e25838b..38edbd31e 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -1,7 +1,7 @@ @testset "Matrices" begin @testset "Laplace" begin # uniform weights for simple mesh - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) L = laplacematrix(mesh, kind=:uniform) @@ -18,7 +18,7 @@ @test size(L) == (5, 5) # cotangent weights only defined for triangle meshes - points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) connec = connect.([(1, 2, 3, 4)], Quadrangle) mesh = SimpleMesh(points, connec) @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) @@ -34,7 +34,7 @@ @testset "Measure" begin # measure matrix of simple mesh - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) M = measurematrix(mesh) @@ -57,7 +57,7 @@ @test size(A) == (101 * 101, 101 * 101) # adjacency of SimpleMesh - points = point.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) + points = cart.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) mesh = SimpleMesh(points, connec, relations=true) A = adjacencymatrix(mesh) @@ -74,7 +74,7 @@ @testset "Miscellaneous" begin # full Laplace-Beltrami operator - sphere = Sphere(point(0, 0, 0)) + sphere = Sphere(cart(0, 0, 0)) mesh = simplexify(sphere) L = laplacematrix(mesh) M = measurematrix(mesh) diff --git a/test/merging.jl b/test/merging.jl index 92642692b..664d81235 100644 --- a/test/merging.jl +++ b/test/merging.jl @@ -1,12 +1,12 @@ @testset "Merging" begin - s = Sphere(point(0, 0, 0), T(1)) + s = Sphere(cart(0, 0, 0), T(1)) c = CylinderSurface(T(1)) m = merge(s, c) @test m isa Multi @test eltype(parent(m)) <: Primitive - s = Sphere(point(0, 0, 0), T(1)) - b = Box(point(0, 0, 0), point(1, 1, 1)) + s = Sphere(cart(0, 0, 0), T(1)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) ms = Multi([s]) mb = Multi([b]) @test merge(ms, b) == merge(ms, mb) == merge(s, mb) diff --git a/test/mesh.jl b/test/mesh.jl index c2a36ebb6..d99e99356 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -5,26 +5,26 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (100,) - @test minimum(grid) == point(0) - @test maximum(grid) == point(100) - @test extrema(grid) == (point(0), point(100)) + @test minimum(grid) == cart(0) + @test maximum(grid) == cart(100) + @test extrema(grid) == (cart(0), cart(100)) @test spacing(grid) == (T(1) * u"m",) @test nelements(grid) == 100 @test eltype(grid) <: Segment{1} @test measure(grid) ≈ T(100) * u"m" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1] == Segment(point(0), point(1)) - @test grid[100] == Segment(point(99), point(100)) + @test grid[1] == Segment(cart(0), cart(1)) + @test grid[100] == Segment(cart(99), cart(100)) grid = cartgrid(200, 100) @test embeddim(grid) == 2 @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) - @test minimum(grid) == point(0, 0) - @test maximum(grid) == point(200, 100) - @test extrema(grid) == (point(0, 0), point(200, 100)) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(200, 100) + @test extrema(grid) == (cart(0, 0), cart(200, 100)) @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 @test eltype(grid) <: Quadrangle{2} @@ -39,9 +39,9 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100, 50) - @test minimum(grid) == point(0, 0, 0) - @test maximum(grid) == point(200, 100, 50) - @test extrema(grid) == (point(0, 0, 0), point(200, 100, 50)) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(200, 100, 50) + @test extrema(grid) == (cart(0, 0, 0), cart(200, 100, 50)) @test spacing(grid) == (T(1) * u"m", T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 * 50 @test eltype(grid) <: Hexahedron{3} @@ -56,8 +56,8 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10, 10) - @test minimum(grid) == point(0, 0, 0) - @test maximum(grid) == point(1, 1, 1) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(1, 1, 1) @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m", T(0.1) * u"m") grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) @@ -65,8 +65,8 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) - @test minimum(grid) == point(-1.0, -1.0) - @test maximum(grid) == point(1.0, 1.0) + @test minimum(grid) == cart(-1.0, -1.0) + @test maximum(grid) == cart(1.0, 1.0) @test spacing(grid) == (T(2 / 200) * u"m", T(2 / 100) * u"m") @test nelements(grid) == 200 * 100 @test eltype(grid) <: Quadrangle{2} @@ -76,21 +76,21 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (20, 10, 5) - @test minimum(grid) == point(0, 0, 0) - @test maximum(grid) == point(100, 50, 25) - @test extrema(grid) == (point(0, 0, 0), point(100, 50, 25)) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(100, 50, 25) + @test extrema(grid) == (cart(0, 0, 0), cart(100, 50, 25)) @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") @test nelements(grid) == 20 * 10 * 5 @test eltype(grid) <: Hexahedron{3} @test vertices(grid[1]) == ( - point(0, 0, 0), - point(5, 0, 0), - point(5, 5, 0), - point(0, 5, 0), - point(0, 0, 5), - point(5, 0, 5), - point(5, 5, 5), - point(0, 5, 5) + cart(0, 0, 0), + cart(5, 0, 0), + cart(5, 5, 0), + cart(0, 5, 0), + cart(0, 0, 5), + cart(5, 0, 5), + cart(5, 5, 5), + cart(0, 5, 5) ) @test all(centroid(grid, i) == centroid(grid[i]) for i in 1:nelements(grid)) @@ -100,8 +100,8 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10) - @test minimum(grid) == point(0.0, 0.0) - @test maximum(grid) == point(10.0, 10.0) + @test minimum(grid) == cart(0.0, 0.0) + @test maximum(grid) == cart(10.0, 10.0) @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 10 * 10 @test eltype(grid) <: Quadrangle{2} @@ -112,39 +112,39 @@ @test size(sub) == (2, 2) @test spacing(sub) == spacing(grid) @test minimum(sub) == minimum(grid) - @test maximum(sub) == point(2, 2) + @test maximum(sub) == cart(2, 2) sub = grid[1:1, 2:3] @test size(sub) == (1, 2) @test spacing(sub) == spacing(grid) - @test minimum(sub) == point(0, 1) - @test maximum(sub) == point(1, 3) + @test minimum(sub) == cart(0, 1) + @test maximum(sub) == cart(1, 3) sub = grid[2:4, 3:7] @test size(sub) == (3, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == point(1, 2) - @test maximum(sub) == point(4, 7) - grid = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) + grid = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) sub = grid[2:4, 3:7] @test size(sub) == (3, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == point(2, 3) - @test maximum(sub) == point(5, 8) + @test minimum(sub) == cart(2, 3) + @test maximum(sub) == cart(5, 8) sub = grid[2, 3:7] @test size(sub) == (1, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == point(2, 3) - @test maximum(sub) == point(3, 8) + @test minimum(sub) == cart(2, 3) + @test maximum(sub) == cart(3, 8) sub = grid[:, 3:7] @test size(sub) == (10, 5) @test spacing(sub) == spacing(grid) - @test minimum(sub) == point(1, 3) - @test maximum(sub) == point(11, 8) + @test minimum(sub) == cart(1, 3) + @test maximum(sub) == cart(11, 8) @test_throws BoundsError grid[3:11, :] # subgrid with comparable vertices of grid - grid = CartesianGrid((10, 10), point(0.0, 0.0), T.((1.2, 1.2))) + grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.2, 1.2))) sub = grid[2:4, 5:7] - @test sub == CartesianGrid((3, 3), point(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) + @test sub == CartesianGrid((3, 3), cart(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) ind = reshape(reshape(1:121, 11, 11)[2:5, 5:8], :) @test vertices(grid)[ind] == vertices(sub) @@ -155,16 +155,16 @@ @test sub1 == sub2 grid = cartgrid(200, 100) - @test centroid(grid, 1) == point(0.5, 0.5) - @test centroid(grid, 2) == point(1.5, 0.5) - @test centroid(grid, 200 * 100) == point(199.5, 99.5) + @test centroid(grid, 1) == cart(0.5, 0.5) + @test centroid(grid, 2) == cart(1.5, 0.5) + @test centroid(grid, 200 * 100) == cart(199.5, 99.5) @test nelements(grid) == 200 * 100 @test eltype(grid) <: Quadrangle{2} - @test grid[1] == Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - @test grid[2] == Quadrangle(point(1, 0), point(2, 0), point(2, 1), point(1, 1)) + @test grid[1] == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test grid[2] == Quadrangle(cart(1, 0), cart(2, 0), cart(2, 1), cart(1, 1)) # expand CartesianGrid with comparable vertices - grid = CartesianGrid((10, 10), point(0.0, 0.0), T.((1.0, 1.0))) + grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.0, 1.0))) left, right = (1, 1), (1, 1) newdim = size(grid) .+ left .+ right newoffset = offset(grid) .+ left @@ -190,8 +190,8 @@ # single vertex access grid = cartgrid(10, 10) - @test vertex(grid, 1) == point(0, 0) - @test vertex(grid, 121) == point(10, 10) + @test vertex(grid, 1) == cart(0, 0) + @test vertex(grid, 121) == cart(10, 10) # xyz g1D = cartgrid(10) @@ -213,7 +213,7 @@ @test Meshes.XYZ(g3D) == (repeat(x, 1, 11, 11), repeat(y, 11, 1, 11), repeat(z, 11, 11, 1)) # units - grid = CartesianGrid((10, 10), point(0, 0), (T(1) * u"m", T(1) * u"m")) + grid = CartesianGrid((10, 10), cart(0, 0), (T(1) * u"m", T(1) * u"m")) o = minimum(grid) s = spacing(grid) @test unit(Meshes.lentype(o)) == u"m" @@ -245,40 +245,40 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) - @test minimum(grid) == point(0, 0) - @test maximum(grid) == point(1, 1) - @test extrema(grid) == (point(0, 0), point(1, 1)) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(1, 1) + @test extrema(grid) == (cart(0, 0), cart(1, 1)) @test nelements(grid) == 25 @test eltype(grid) <: Quadrangle{2} @test measure(grid) ≈ T(1) * u"m^2" - @test centroid(grid, 1) ≈ point(0.1, 0.05) - @test centroid(grid[1]) ≈ point(0.1, 0.05) - @test centroid(grid, 2) ≈ point(0.3, 0.05) - @test centroid(grid[2]) ≈ point(0.3, 0.05) + @test centroid(grid, 1) ≈ cart(0.1, 0.05) + @test centroid(grid[1]) ≈ cart(0.1, 0.05) + @test centroid(grid, 2) ≈ cart(0.3, 0.05) + @test centroid(grid[2]) ≈ cart(0.3, 0.05) @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1] == grid[1] @test grid[5, 5] == grid[25] sub = grid[2:4, 3:5] @test size(sub) == (3, 3) - @test minimum(sub) == point(0.2, 0.3) - @test maximum(sub) == point(0.8, 1.0) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.8, 1.0) sub = grid[2, 3:5] @test size(sub) == (1, 3) - @test minimum(sub) == point(0.2, 0.3) - @test maximum(sub) == point(0.4, 1.0) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.4, 1.0) sub = grid[:, 3:5] @test size(sub) == (5, 3) - @test minimum(sub) == point(0.0, 0.3) - @test maximum(sub) == point(1.0, 1.0) + @test minimum(sub) == cart(0.0, 0.3) + @test maximum(sub) == cart(1.0, 1.0) @test_throws BoundsError grid[2:6, :] @test Meshes.xyz(grid) == (x * u"m", y * u"m") @test Meshes.XYZ(grid) == (repeat(x, 1, 6) * u"m", repeat(y', 6, 1) * u"m") # single vertex access grid = RectilinearGrid(T.(0:10), T.(0:10)) - @test vertex(grid, 1) == point(0, 0) - @test vertex(grid, 121) == point(10, 10) + @test vertex(grid, 1) == cart(0, 0) + @test vertex(grid, 121) == cart(10, 10) # constructor with datum & datum propagation grid = RectilinearGrid{WGS84Latest}(x, y) @@ -371,32 +371,32 @@ @test Meshes.crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) - @test minimum(grid) == point(0, 0) - @test maximum(grid) == point(1, 1) - @test extrema(grid) == (point(0, 0), point(1, 1)) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(1, 1) + @test extrema(grid) == (cart(0, 0), cart(1, 1)) @test nelements(grid) == 25 @test eltype(grid) <: Quadrangle{2} @test measure(grid) ≈ T(1) * u"m^2" - @test centroid(grid, 1) ≈ point(0.1, 0.05) - @test centroid(grid[1]) ≈ point(0.1, 0.05) - @test centroid(grid, 2) ≈ point(0.3, 0.05) - @test centroid(grid[2]) ≈ point(0.3, 0.05) + @test centroid(grid, 1) ≈ cart(0.1, 0.05) + @test centroid(grid[1]) ≈ cart(0.1, 0.05) + @test centroid(grid, 2) ≈ cart(0.3, 0.05) + @test centroid(grid[2]) ≈ cart(0.3, 0.05) @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @test grid[1, 1] == grid[1] @test grid[5, 5] == grid[25] sub = grid[2:4, 3:5] @test size(sub) == (3, 3) - @test minimum(sub) == point(0.2, 0.3) - @test maximum(sub) == point(0.8, 1.0) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.8, 1.0) sub = grid[2, 3:5] @test size(sub) == (1, 3) - @test minimum(sub) == point(0.2, 0.3) - @test maximum(sub) == point(0.4, 1.0) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.4, 1.0) sub = grid[:, 3:5] @test size(sub) == (5, 3) - @test minimum(sub) == point(0.0, 0.3) - @test maximum(sub) == point(1.0, 1.0) + @test minimum(sub) == cart(0.0, 0.3) + @test maximum(sub) == cart(1.0, 1.0) @test_throws BoundsError grid[2:6, :] @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") @@ -499,15 +499,15 @@ end @testset "SimpleMesh" begin - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) triangles = Triangle.([ - (point(0.0, 0.0), point(1.0, 0.0), point(0.5, 0.5)), - (point(1.0, 0.0), point(1.0, 1.0), point(0.5, 0.5)), - (point(1.0, 1.0), point(0.0, 1.0), point(0.5, 0.5)), - (point(0.0, 1.0), point(0.0, 0.0), point(0.5, 0.5)) + (cart(0.0, 0.0), cart(1.0, 0.0), cart(0.5, 0.5)), + (cart(1.0, 0.0), cart(1.0, 1.0), cart(0.5, 0.5)), + (cart(1.0, 1.0), cart(0.0, 1.0), cart(0.5, 0.5)), + (cart(0.0, 1.0), cart(0.0, 0.0), cart(0.5, 0.5)) ]) @test Meshes.crs(mesh) <: Cartesian{NoDatum} @test Meshes.lentype(mesh) == ℳ @@ -521,7 +521,7 @@ @test eltype(mesh) <: Triangle{2} @test measure(mesh) ≈ T(1) * u"m^2" @test area(mesh) ≈ T(1) * u"m^2" - @test extrema(mesh) == (point(0, 0), point(1, 1)) + @test extrema(mesh) == (cart(0, 0), cart(1, 1)) # test constructors coords = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] @@ -542,15 +542,15 @@ @test nvertices(mesh) == 5 @test nelements(mesh) == 4 - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) Δs = connect.([(3, 1, 5), (4, 6, 2)], Triangle) □s = connect.([(1, 2, 6, 5), (5, 6, 4, 3)], Quadrangle) mesh = SimpleMesh(points, [Δs; □s]) elms = [ - Triangle(point(0.0, 1.0), point(0.0, 0.0), point(0.25, 0.5)), - Triangle(point(1.0, 1.0), point(0.75, 0.5), point(1.0, 0.0)), - Quadrangle(point(0.0, 0.0), point(1.0, 0.0), point(0.75, 0.5), point(0.25, 0.5)), - Quadrangle(point(0.25, 0.5), point(0.75, 0.5), point(1.0, 1.0), point(0.0, 1.0)) + Triangle(cart(0.0, 1.0), cart(0.0, 0.0), cart(0.25, 0.5)), + Triangle(cart(1.0, 1.0), cart(0.75, 0.5), cart(1.0, 0.0)), + Quadrangle(cart(0.0, 0.0), cart(1.0, 0.0), cart(0.75, 0.5), cart(0.25, 0.5)), + Quadrangle(cart(0.25, 0.5), cart(0.75, 0.5), cart(1.0, 1.0), cart(0.0, 1.0)) ] @test collect(elements(mesh)) == elms @test nelements(mesh) == 4 @@ -560,7 +560,7 @@ @test eltype(mesh) <: Polygon{2} # test for https://github.com/JuliaGeometry/Meshes.jl/issues/177 - points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) mesh = SimpleMesh(points, connec) topo = topology(mesh) @@ -568,13 +568,13 @@ @test collect(faces(topo, 3)) == [connect((1, 2, 3, 4), Tetrahedron)] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/187 - points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) mesh = SimpleMesh(points[4:-1:1], connec) meshvp = SimpleMesh(view(points, 4:-1:1), connec) @test mesh == meshvp - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) bytes = @allocated faces(mesh, 2) @@ -583,24 +583,24 @@ bytes = @allocated collect(cells) @test bytes < 800 - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) - @test centroid(mesh, 1) == centroid(Triangle(point(0, 0), point(1, 0), point(0.5, 0.5))) - @test centroid(mesh, 2) == centroid(Triangle(point(1, 0), point(1, 1), point(0.5, 0.5))) - @test centroid(mesh, 3) == centroid(Triangle(point(1, 1), point(0, 1), point(0.5, 0.5))) - @test centroid(mesh, 4) == centroid(Triangle(point(0, 1), point(0, 0), point(0.5, 0.5))) + @test centroid(mesh, 1) == centroid(Triangle(cart(0, 0), cart(1, 0), cart(0.5, 0.5))) + @test centroid(mesh, 2) == centroid(Triangle(cart(1, 0), cart(1, 1), cart(0.5, 0.5))) + @test centroid(mesh, 3) == centroid(Triangle(cart(1, 1), cart(0, 1), cart(0.5, 0.5))) + @test centroid(mesh, 4) == centroid(Triangle(cart(0, 1), cart(0, 0), cart(0.5, 0.5))) # merge operation with 2D geometries - mesh₁ = SimpleMesh(point.([(0, 0), (1, 0), (0, 1)]), connect.([(1, 2, 3)])) - mesh₂ = SimpleMesh(point.([(1, 0), (1, 1), (0, 1)]), connect.([(1, 2, 3)])) + mesh₁ = SimpleMesh(cart.([(0, 0), (1, 0), (0, 1)]), connect.([(1, 2, 3)])) + mesh₂ = SimpleMesh(cart.([(1, 0), (1, 1), (0, 1)]), connect.([(1, 2, 3)])) mesh = merge(mesh₁, mesh₂) @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] @test collect(elements(topology(mesh))) == connect.([(1, 2, 3), (4, 5, 6)]) # merge operation with 3D geometries - mesh₁ = SimpleMesh(point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) - mesh₂ = SimpleMesh(point.([(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh₁ = SimpleMesh(cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh₂ = SimpleMesh(cart.([(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) mesh = merge(mesh₁, mesh₂) @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] @test collect(elements(topology(mesh))) == connect.([(1, 2, 3, 4), (5, 6, 7, 8)], Tetrahedron) @@ -615,25 +615,25 @@ @test eltype(mesh) <: Quadrangle # grid interface @test size(mesh) == (10, 10) - @test minimum(mesh) == point(0, 0) - @test maximum(mesh) == point(10, 10) - @test extrema(mesh) == (point(0, 0), point(10, 10)) + @test minimum(mesh) == cart(0, 0) + @test maximum(mesh) == cart(10, 10) + @test extrema(mesh) == (cart(0, 0), cart(10, 10)) @test vertex(mesh, 1) == vertex(mesh, ntuple(i -> 1, embeddim(mesh))) @test vertex(mesh, nvertices(mesh)) == vertex(mesh, size(mesh) .+ 1) @test mesh[1, 1] == mesh[1] @test mesh[10, 10] == mesh[100] sub = mesh[2:4, 3:7] @test size(sub) == (3, 5) - @test minimum(sub) == point(1, 2) - @test maximum(sub) == point(4, 7) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) sub = mesh[2, 3:7] @test size(sub) == (1, 5) - @test minimum(sub) == point(1, 2) - @test maximum(sub) == point(2, 7) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(2, 7) sub = mesh[:, 3:7] @test size(sub) == (10, 5) - @test minimum(sub) == point(0, 2) - @test maximum(sub) == point(10, 7) + @test minimum(sub) == cart(0, 2) + @test maximum(sub) == cart(10, 7) @test_throws BoundsError grid[3:11, :] # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 @@ -652,7 +652,7 @@ @test vertex(mesh, 4) == points[4] @test vertex(mesh, 5) == points[5] - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) @test sprint(show, mesh) == "4 SimpleMesh" @@ -715,25 +715,25 @@ tgrid = TransformedMesh(grid, trans) @test tgrid isa TransformedGrid @test size(tgrid) == (10, 10) - @test minimum(tgrid) == point(0, 0) - @test maximum(tgrid) == point(10, 10) - @test extrema(tgrid) == (point(0, 0), point(10, 10)) + @test minimum(tgrid) == cart(0, 0) + @test maximum(tgrid) == cart(10, 10) + @test extrema(tgrid) == (cart(0, 0), cart(10, 10)) @test vertex(tgrid, 1) == vertex(tgrid, ntuple(i -> 1, embeddim(tgrid))) @test vertex(tgrid, nvertices(tgrid)) == vertex(tgrid, size(tgrid) .+ 1) @test tgrid[1, 1] == tgrid[1] @test tgrid[10, 10] == tgrid[100] sub = tgrid[2:4, 3:7] @test size(sub) == (3, 5) - @test minimum(sub) == point(1, 2) - @test maximum(sub) == point(4, 7) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) sub = tgrid[2, 3:7] @test size(sub) == (1, 5) - @test minimum(sub) == point(1, 2) - @test maximum(sub) == point(2, 7) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(2, 7) sub = tgrid[:, 3:7] @test size(sub) == (10, 5) - @test minimum(sub) == point(0, 2) - @test maximum(sub) == point(10, 7) + @test minimum(sub) == cart(0, 2) + @test maximum(sub) == cart(10, 7) @test sprint(show, tgrid) == "10×10 TransformedGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), tgrid) == """ diff --git a/test/multigeoms.jl b/test/multigeoms.jl index a48d771fd..e97000e13 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -1,7 +1,7 @@ @testset "Multi" begin - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) multi = Multi([poly, poly]) @test multi == multi @@ -15,26 +15,26 @@ @test boundary(multi) == merge(boundary(poly), boundary(poly)) @test rings(multi) == [rings(poly); rings(poly)] - poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) + poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) @test vertices(multi) == [vertices(poly1); vertices(poly2)] @test nvertices(multi) == nvertices(poly1) + nvertices(poly2) @test area(multi) == area(poly1) + area(poly2) @test perimeter(multi) == perimeter(poly1) + perimeter(poly2) - @test centroid(multi) == point(1, 1) - @test point(0.5, 0.5) ∈ multi - @test point(1.5, 1.5) ∈ multi - @test point(1.5, 0.5) ∉ multi - @test point(0.5, 1.5) ∉ multi + @test centroid(multi) == cart(1, 1) + @test cart(0.5, 0.5) ∈ multi + @test cart(1.5, 1.5) ∈ multi + @test cart(1.5, 0.5) ∉ multi + @test cart(0.5, 1.5) ∉ multi @test sprint(show, multi) == "Multi(2×PolyArea)" @test sprint(show, MIME"text/plain"(), multi) == """ MultiPolyArea ├─ PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) └─ PolyArea((x: 1.0 m, y: 1.0 m), ..., (x: 1.0 m, y: 2.0 m))""" - box1 = Box(point(0, 0), point(1, 1)) - box2 = Box(point(1, 1), point(2, 2)) + box1 = Box(cart(0, 0), cart(1, 1)) + box2 = Box(cart(1, 1), cart(2, 2)) mbox = Multi([box1, box2]) mchn = boundary(mbox) noth = boundary(mchn) @@ -47,8 +47,8 @@ ├─ Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)) └─ Box(min: (x: 1.0 m, y: 1.0 m), max: (x: 2.0 m, y: 2.0 m))""" - box1 = Box(point(0, 0), point(1, 1)) - box2 = Box(point(1, 1), point(2, 2)) + box1 = Box(cart(0, 0), cart(1, 1)) + box2 = Box(cart(1, 1), cart(2, 2)) mbox = Multi([box1, box2]) equaltest(mbox) isapproxtest(mbox) @@ -59,8 +59,8 @@ @test parent(multi) == collect(grid) # boundary of multi-3D-geometry - box1 = Box(point(0, 0, 0), point(1, 1, 1)) - box2 = Box(point(1, 1, 1), point(2, 2, 2)) + box1 = Box(cart(0, 0, 0), cart(1, 1, 1)) + box2 = Box(cart(1, 1, 1), cart(2, 2, 2)) mbox = Multi([box1, box2]) mesh = boundary(mbox) @test mesh isa Mesh @@ -68,8 +68,8 @@ @test nelements(mesh) == 12 # unique vertices - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) multi = Multi([poly, quad]) @test unique(multi) == multi @test sprint(show, multi) == "Multi(1×PolyArea, 1×Quadrangle)" @@ -79,12 +79,12 @@ └─ Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))""" # type aliases - p = point(0, 0) - segm = Segment(point(0, 0), point(1, 1)) - rope = Rope(point.([(0, 0), (1, 0), (1, 1)])) - ring = Ring(point.([(0, 0), (1, 0), (1, 1)])) - tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + p = cart(0, 0) + segm = Segment(cart(0, 0), cart(1, 1)) + rope = Rope(cart.([(0, 0), (1, 0), (1, 1)])) + ring = Ring(cart.([(0, 0), (1, 0), (1, 1)])) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test Multi([p, p]) isa MultiPoint @test Multi([segm, segm]) isa MultiSegment @test Multi([rope, rope]) isa MultiRope diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index c01a8fc11..130c722d6 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -3,29 +3,29 @@ 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = BallSearch(𝒟, MetricBall(T(1))) - n = search(point(0, 0), s) + n = search(cart(0, 0), s) @test Set(n) == Set([1, 2, 11]) - n = search(point(9, 0), s) + n = search(cart(9, 0), s) @test Set(n) == Set([9, 10, 20]) - n = search(point(0, 9), s) + n = search(cart(0, 9), s) @test Set(n) == Set([91, 81, 92]) - n = search(point(9, 9), s) + n = search(cart(9, 9), s) @test Set(n) == Set([100, 99, 90]) s = BallSearch(𝒟, MetricBall(T(√2 + eps(T)))) - n = search(point(0, 0), s) + n = search(cart(0, 0), s) @test Set(n) == Set([1, 2, 11, 12]) - n = search(point(9, 0), s) + n = search(cart(9, 0), s) @test Set(n) == Set([9, 10, 19, 20]) - n = search(point(0, 9), s) + n = search(cart(0, 9), s) @test Set(n) == Set([81, 82, 91, 92]) - n = search(point(9, 9), s) + n = search(cart(9, 9), s) @test Set(n) == Set([89, 90, 99, 100]) # non MinkowskiMetric example 𝒟 = CartesianGrid((360, 180), T.((0.0, -90.0)), T.((1.0, 1.0))) s = BallSearch(𝒟, MetricBall(T(150), Haversine(T(6371)))) - n = search(point(0, 0), s) + n = search(cart(0, 0), s) @test Set(n) == Set([32041, 32400, 32401, 32760]) # construct from vector of geometries @@ -36,24 +36,24 @@ @testset "KNearestSearch" begin 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = KNearestSearch(𝒟, 3) - n = search(point(0, 0), s) + n = search(cart(0, 0), s) @test Set(n) == Set([1, 2, 11]) - n = search(point(9, 0), s) + n = search(cart(9, 0), s) @test Set(n) == Set([9, 10, 20]) - n = search(point(0, 9), s) + n = search(cart(0, 9), s) @test Set(n) == Set([91, 81, 92]) - n = search(point(9, 9), s) + n = search(cart(9, 9), s) @test Set(n) == Set([100, 99, 90]) - n, d = searchdists(point(9, 9), s) + n, d = searchdists(cart(9, 9), s) @test Set(n) == Set([100, 99, 90]) @test length(d) == 3 n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, point(9, 9), s) + nn = search!(n, cart(9, 9), s) @test nn == 3 @test Set(n[1:nn]) == Set([100, 99, 90]) n = Vector{Int}(undef, maxneighbors(s)) d = Vector{ℳ}(undef, maxneighbors(s)) - nn = searchdists!(n, d, point(9, 9), s) + nn = searchdists!(n, d, cart(9, 9), s) @test nn == 3 @test Set(n[1:nn]) == Set([100, 99, 90]) @@ -66,43 +66,43 @@ 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) s = KBallSearch(𝒟, 10, MetricBall(T(100))) - n = search(point(5, 5), s) + n = search(cart(5, 5), s) @test length(n) == 10 s = KBallSearch(𝒟, 10, MetricBall(T.((100, 100)))) - n = search(point(5, 5), s) + n = search(cart(5, 5), s) @test length(n) == 10 s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n = search(point(5, 5), s) + n = search(cart(5, 5), s) @test length(n) == 5 @test n[1] == 56 s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n, d = searchdists(point(5, 5), s) + n, d = searchdists(cart(5, 5), s) @test length(n) == 5 @test length(d) == 5 s = KBallSearch(𝒟, 10, MetricBall(T(1))) n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, point(5, 5), s) + nn = search!(n, cart(5, 5), s) @test nn == 5 s = KBallSearch(𝒟, 10, MetricBall(T(1))) n = Vector{Int}(undef, maxneighbors(s)) d = Vector{ℳ}(undef, maxneighbors(s)) - nn = searchdists!(n, d, point(5, 5), s) + nn = searchdists!(n, d, cart(5, 5), s) @test nn == 5 mask = trues(nelements(𝒟)) mask[56] = false - n = search(point(5, 5), s, mask=mask) + n = search(cart(5, 5), s, mask=mask) @test length(n) == 4 - n = search(point(-0.2, -0.2), s) + n = search(cart(-0.2, -0.2), s) @test length(n) == 1 - n = search(point(-10, -10), s) + n = search(cart(-10, -10), s) @test length(n) == 0 - n, d = searchdists(point(5, 5), s, mask=mask) + n, d = searchdists(cart(5, 5), s, mask=mask) @test length(n) == 4 @test length(d) == 4 diff --git a/test/orientation.jl b/test/orientation.jl index 84e6f0a74..fe359c6bd 100644 --- a/test/orientation.jl +++ b/test/orientation.jl @@ -1,12 +1,12 @@ @testset "orientation" begin # test orientation - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test orientation(t) == CCW - t = Triangle(point(0, 0), point(0, 1), point(1, 0)) + t = Triangle(cart(0, 0), cart(0, 1), cart(1, 0)) @test orientation(t) == CW # orientation of 3D rings in X-Y plane - r1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - r2 = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) + r1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + r2 = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test orientation(r1) == orientation(r2) end diff --git a/test/pointification.jl b/test/pointification.jl index 6406da54e..e1ecf0195 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -1,38 +1,38 @@ @testset "Pointification" begin - p = point(0, 0) - @test pointify(p) == [point(0, 0)] + p = cart(0, 0) + @test pointify(p) == [cart(0, 0)] - sphere = Sphere(point(0, 0), T(1)) + sphere = Sphere(cart(0, 0), T(1)) points = pointify(sphere) @test all(∈(sphere), points) - ball = Ball(point(0, 0), T(1)) + ball = Ball(cart(0, 0), T(1)) points = pointify(ball) @test all(∈(boundary(ball)), points) - verts = [point(0, 0), point(1, 1)] + verts = [cart(0, 0), cart(1, 1)] segment = Segment(verts...) points = pointify(segment) @test points == verts - verts = [point(0, 0), point(1, 0), point(1, 1)] + verts = [cart(0, 0), cart(1, 0), cart(1, 1)] triangle = Triangle(verts...) points = pointify(triangle) @test points == verts - verts = [point(0, 0), point(1, 0), point(1, 1), point(0, 1)] + verts = [cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)] quadrangle = Quadrangle(verts...) points = pointify(quadrangle) @test points == verts - tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) - quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) multi = Multi([tri, quad]) points = pointify(multi) @test points == [pointify(tri); pointify(quad)] - tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) - quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) gset = GeometrySet([tri, quad]) points = pointify(gset) @test points == [pointify(tri); pointify(quad)] diff --git a/test/polytopes.jl b/test/polytopes.jl index 5ae1f775b..0618b9197 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -3,41 +3,41 @@ @test paramdim(Segment) == 1 @test nvertices(Segment) == 2 - s = Segment(point(1.0), point(2.0)) + s = Segment(cart(1.0), cart(2.0)) @test Meshes.crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ - @test vertex(s, 1) == point(1.0) - @test vertex(s, 2) == point(2.0) - @test all(point(x) ∈ s for x in 1:0.01:2) - @test all(point(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) + @test vertex(s, 1) == cart(1.0) + @test vertex(s, 2) == cart(2.0) + @test all(cart(x) ∈ s for x in 1:0.01:2) + @test all(cart(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) @test s ≈ s - @test !(s ≈ Segment(point(2.0), point(1.0))) - @test !(s ≈ Segment(point(-1.0), point(2.0))) + @test !(s ≈ Segment(cart(2.0), cart(1.0))) + @test !(s ≈ Segment(cart(-1.0), cart(2.0))) - s = Segment(point(0, 0), point(1, 1)) - @test minimum(s) == point(0, 0) - @test maximum(s) == point(1, 1) - @test extrema(s) == (point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) + @test minimum(s) == cart(0, 0) + @test maximum(s) == cart(1, 1) + @test extrema(s) == (cart(0, 0), cart(1, 1)) @test isapprox(length(s), sqrt(T(2)) * u"m") - @test s(T(0)) == point(0, 0) - @test s(T(1)) == point(1, 1) - @test all(point(x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [point(-0.1, -0.1), point(1.1, 1.1), point(0.5, 0.49), point(1, 2)]) + @test s(T(0)) == cart(0, 0) + @test s(T(1)) == cart(1, 1) + @test all(cart(x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [cart(-0.1, -0.1), cart(1.1, 1.1), cart(0.5, 0.49), cart(1, 2)]) @test_throws DomainError(T(1.2), "s(t) is not defined for t outside [0, 1].") s(T(1.2)) @test_throws DomainError(T(-0.5), "s(t) is not defined for t outside [0, 1].") s(T(-0.5)) @test s ≈ s - @test !(s ≈ Segment(point(1, 1), point(0, 0))) - @test !(s ≈ Segment(point(1, 2), point(0, 0))) + @test !(s ≈ Segment(cart(1, 1), cart(0, 0))) + @test !(s ≈ Segment(cart(1, 2), cart(0, 0))) - s = Segment(point(0, 0, 0), point(1, 1, 1)) - @test all(point(x, x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [point(-0.1, -0.1, -0.1), point(1.1, 1.1, 1.1)]) - @test all(p ∉ s for p in [point(0.5, 0.5, 0.49), point(1, 1, 2)]) + s = Segment(cart(0, 0, 0), cart(1, 1, 1)) + @test all(cart(x, x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [cart(-0.1, -0.1, -0.1), cart(1.1, 1.1, 1.1)]) + @test all(p ∉ s for p in [cart(0.5, 0.5, 0.49), cart(1, 1, 2)]) @test s ≈ s - @test !(s ≈ Segment(point(1, 1, 1), point(0, 0, 0))) - @test !(s ≈ Segment(point(1, 1, 1), point(0, 1, 0))) + @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 0, 0))) + @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 1, 0))) - s = Segment(point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) equaltest(s) isapproxtest(s) @@ -49,10 +49,10 @@ @test !(s ≈ Segment(Point(2, 2, 2, 2), Point(1, 1, 1, 1))) @test !(s ≈ Segment(Point(1, 1, 2, 1), Point(0, 0, 0, 0))) - s = Segment(point(0, 0, 0), point(1, 1, 1)) - @test boundary(s) == Multi([point(0, 0, 0), point(1, 1, 1)]) + s = Segment(cart(0, 0, 0), cart(1, 1, 1)) + @test boundary(s) == Multi([cart(0, 0, 0), cart(1, 1, 1)]) @test perimeter(s) == zero(T) * u"m" - @test center(s) == point(0.5, 0.5, 0.5) + @test center(s) == cart(0.5, 0.5, 0.5) @test Meshes.lentype(center(s)) == ℳ # unitful coordinates @@ -80,7 +80,7 @@ s = Segment(Point(c1), Point(c2)) @test datum(Meshes.crs(s(T(0)))) === WGS84Latest - s = Segment(point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), s) == """ @@ -96,105 +96,105 @@ end @testset "Ropes/Rings" begin - c1 = Rope(point.([(1, 1), (2, 2)])) - c2 = Rope(point(1, 1), point(2, 2)) + c1 = Rope(cart.([(1, 1), (2, 2)])) + c2 = Rope(cart(1, 1), cart(2, 2)) c3 = Rope(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 - c1 = Ring(point.([(1, 1), (2, 2)])) - c2 = Ring(point(1, 1), point(2, 2)) + c1 = Ring(cart.([(1, 1), (2, 2)])) + c2 = Ring(cart(1, 1), cart(2, 2)) c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) @test c1 == c2 == c3 - c = Rope(point(0, 0), point(1, 0), point(0, 1)) + c = Rope(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(c) isapproxtest(c) - c = Ring(point(0, 0), point(1, 0), point(0, 1)) + c = Ring(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(c) isapproxtest(c) # circular equality - c1 = Ring(point.([(1, 1), (2, 2), (3, 3)])) - c2 = Ring(point.([(2, 2), (3, 3), (1, 1)])) - c3 = Ring(point.([(3, 3), (1, 1), (2, 2)])) + c1 = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + c2 = Ring(cart.([(2, 2), (3, 3), (1, 1)])) + c3 = Ring(cart.([(3, 3), (1, 1), (2, 2)])) @test c1 ≗ c2 ≗ c3 - c = Rope(point.([(1, 1), (2, 2)])) + c = Rope(cart.([(1, 1), (2, 2)])) @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ - @test vertex(c, 1) == point(1, 1) - @test vertex(c, 2) == point(2, 2) - c = Ring(point.([(1, 1), (2, 2)])) + @test vertex(c, 1) == cart(1, 1) + @test vertex(c, 2) == cart(2, 2) + c = Ring(cart.([(1, 1), (2, 2)])) @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ - @test vertex(c, 0) == point(2, 2) - @test vertex(c, 1) == point(1, 1) - @test vertex(c, 2) == point(2, 2) - @test vertex(c, 3) == point(1, 1) - @test vertex(c, 4) == point(2, 2) - - c = Rope(point.([(1, 1), (2, 2), (3, 3)])) - @test collect(segments(c)) == [Segment(point(1, 1), point(2, 2)), Segment(point(2, 2), point(3, 3))] - c = Ring(point.([(1, 1), (2, 2), (3, 3)])) + @test vertex(c, 0) == cart(2, 2) + @test vertex(c, 1) == cart(1, 1) + @test vertex(c, 2) == cart(2, 2) + @test vertex(c, 3) == cart(1, 1) + @test vertex(c, 4) == cart(2, 2) + + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test collect(segments(c)) == [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3))] + c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) @test collect(segments(c)) == - [Segment(point(1, 1), point(2, 2)), Segment(point(2, 2), point(3, 3)), Segment(point(3, 3), point(1, 1))] + [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3)), Segment(cart(3, 3), cart(1, 1))] - c = Rope(point.([(1, 1), (2, 2), (2, 2), (3, 3)])) - @test unique(c) == Rope(point.([(1, 1), (2, 2), (3, 3)])) - @test c == Rope(point.([(1, 1), (2, 2), (2, 2), (3, 3)])) + c = Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) + @test unique(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test c == Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) unique!(c) - @test c == Rope(point.([(1, 1), (2, 2), (3, 3)])) + @test c == Rope(cart.([(1, 1), (2, 2), (3, 3)])) - c = Rope(point.([(1, 1), (2, 2), (3, 3)])) - @test close(c) == Ring(point.([(1, 1), (2, 2), (3, 3)])) - c = Ring(point.([(1, 1), (2, 2), (3, 3)])) - @test open(c) == Rope(point.([(1, 1), (2, 2), (3, 3)])) + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test close(c) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) + c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + @test open(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) - c = Rope(point.([(1, 1), (2, 2), (3, 3)])) + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) reverse!(c) - @test c == Rope(point.([(3, 3), (2, 2), (1, 1)])) - c = Rope(point.([(1, 1), (2, 2), (3, 3)])) - @test reverse(c) == Rope(point.([(3, 3), (2, 2), (1, 1)])) + @test c == Rope(cart.([(3, 3), (2, 2), (1, 1)])) + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test reverse(c) == Rope(cart.([(3, 3), (2, 2), (1, 1)])) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test angles(c) ≈ [-π / 2, -π / 2, -π / 2, -π / 2] - c = Rope(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test angles(c) ≈ [-π / 2, -π / 2] - c = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) @test angles(c) ≈ [-atan(2), -π / 2, +π / 2, -π / 2, -π / 2, -(π - atan(2))] @test innerangles(c) ≈ [atan(2), π / 2, 3π / 2, π / 2, π / 2, π - atan(2)] - c1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + c1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) c2 = Ring(vertices(c1)) @test c1 == c2 - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test centroid(c) == point(0.5, 0.5) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(c) == cart(0.5, 0.5) - c = Rope(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test boundary(c) == Multi(point.([(0, 0), (0, 1)])) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test boundary(c) == Multi(cart.([(0, 0), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test isnothing(boundary(c)) # should not repeat the first vertex manually - @test_throws ArgumentError Ring(point.([(0, 0), (0, 0)])) - @test_throws ArgumentError Ring(point.([(0, 0), (1, 0), (1, 1), (0, 0)])) + @test_throws ArgumentError Ring(cart.([(0, 0), (0, 0)])) + @test_throws ArgumentError Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) # degenerate rings with 1 or 2 vertices are allowed - r = Ring(point.([(0, 0)])) + r = Ring(cart.([(0, 0)])) @test isclosed(r) @test nvertices(r) == 1 - @test collect(segments(r)) == [Segment(point(0, 0), point(0, 0))] - r = Ring(point.([(0, 0), (1, 1)])) + @test collect(segments(r)) == [Segment(cart(0, 0), cart(0, 0))] + r = Ring(cart.([(0, 0), (1, 1)])) @test isclosed(r) @test nvertices(r) == 2 - @test collect(segments(r)) == [Segment(point(0, 0), point(1, 1)), Segment(point(1, 1), point(0, 0))] + @test collect(segments(r)) == [Segment(cart(0, 0), cart(1, 1)), Segment(cart(1, 1), cart(0, 0))] - p1 = point(1, 1) - p2 = point(3, 1) - p3 = point(1, 0) - p4 = point(3, 0) - pts = point.([(0, 0), (2, 2), (4, 0)]) + p1 = cart(1, 1) + p2 = cart(3, 1) + p3 = cart(1, 0) + p4 = cart(3, 0) + pts = cart.([(0, 0), (2, 2), (4, 0)]) r = Ring(pts) @test p1 ∈ r @test p2 ∈ r @@ -208,7 +208,7 @@ # approximately equal vertices pts = - point.( + cart.( [ (-48.04448403189499, -18.326530800015174) (-48.044478457836675, -18.326503670869467) @@ -264,14 +264,14 @@ @test Meshes.lentype(r) === Meshes.Met{Float64} # issimple benchmark - r = Sphere(point(0, 0), T(1)) |> pointify |> Ring + r = Sphere(cart(0, 0), T(1)) |> pointify |> Ring @test issimple(r) @test @elapsed(issimple(r)) < 0.02 @test @allocated(issimple(r)) < 950000 # innerangles in 3D is obtained via projection - r1 = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - r2 = Ring(point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) + r1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + r2 = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test innerangles(r1) ≈ innerangles(r2) # datum propagation @@ -280,8 +280,8 @@ r = Ring(points) @test datum(Meshes.crs(centroid(r))) === WGS84Latest - ri = Ring(point.([(1, 1), (2, 2), (3, 3)])) - ro = Rope(point.([(1, 1), (2, 2), (3, 3)])) + ri = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + ro = Rope(cart.([(1, 1), (2, 2), (3, 3)])) @test sprint(show, ri) == "Ring((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" @test sprint(show, ro) == "Rope((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" if T === Float32 @@ -310,7 +310,7 @@ end @testset "Ngons" begin - pts = (point(0, 0), point(1, 0), point(0, 1)) + pts = (cart(0, 0), cart(1, 0), cart(0, 1)) tups = (T.((0, 0)), T.((1, 0)), T.((0, 1))) @test paramdim(Ngon) == 2 @test vertices(Ngon(pts)) == pts @@ -337,52 +337,52 @@ end # error: the number of vertices must be greater than or equal to 3 - @test_throws ArgumentError Ngon(point(0, 0), point(1, 1)) - @test_throws ArgumentError Ngon{2}(point(0, 0), point(1, 1)) + @test_throws ArgumentError Ngon(cart(0, 0), cart(1, 1)) + @test_throws ArgumentError Ngon{2}(cart(0, 0), cart(1, 1)) # --------- # TRIANGLE # --------- # Triangle in 2D space - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test Meshes.crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ - @test vertex(t, 1) == point(0, 0) - @test vertex(t, 2) == point(1, 0) - @test vertex(t, 3) == point(0, 1) + @test vertex(t, 1) == cart(0, 0) + @test vertex(t, 2) == cart(1, 0) + @test vertex(t, 3) == cart(0, 1) @test signarea(t) == T(0.5) * u"m^2" @test area(t) == T(0.5) * u"m^2" - t = Triangle(point(0, 0), point(0, 1), point(1, 0)) + t = Triangle(cart(0, 0), cart(0, 1), cart(1, 0)) @test signarea(t) == T(-0.5) * u"m^2" @test area(t) == T(0.5) * u"m^2" - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) - for p in point.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + for p in cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) @test p ∈ t end - for p in point.([(-1, 0), (0, -1), (0.5, 1.0)]) + for p in cart.([(-1, 0), (0, -1), (0.5, 1.0)]) @test p ∉ t end - t = Triangle(point(0.4, 0.4), point(0.6, 0.4), point(0.8, 0.4)) - @test point(0.2, 0.4) ∉ t - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) - @test t(T(0.0), T(0.0)) == point(0, 0) - @test t(T(1.0), T(0.0)) == point(1, 0) - @test t(T(0.0), T(1.0)) == point(0, 1) - @test t(T(0.5), T(0.5)) == point(0.5, 0.5) + t = Triangle(cart(0.4, 0.4), cart(0.6, 0.4), cart(0.8, 0.4)) + @test cart(0.2, 0.4) ∉ t + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test t(T(0.0), T(0.0)) == cart(0, 0) + @test t(T(1.0), T(0.0)) == cart(1, 0) + @test t(T(0.0), T(1.0)) == cart(0, 1) + @test t(T(0.5), T(0.5)) == cart(0.5, 0.5) @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) @test !hasholes(t) @test unique(t) == t @test boundary(t) == first(rings(t)) - @test rings(t) == [Ring(point(0, 0), point(1, 0), point(0, 1))] + @test rings(t) == [Ring(cart(0, 0), cart(1, 0), cart(0, 1))] @test convexhull(t) == t - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(t) isapproxtest(t) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" # https://github.com/JuliaGeometry/Meshes.jl/issues/333 @@ -397,38 +397,38 @@ @test t1 ≗ t2 ≗ t3 # point at edge of triangle - @test point(3, 1) ∈ Triangle(point(1, 1), point(5, 1), point(3, 3)) + @test cart(3, 1) ∈ Triangle(cart(1, 1), cart(5, 1), cart(3, 3)) # test angles - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45], atol=8 * eps(T))) @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45], atol=8 * eps(T))) # Triangle in 3D space - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) @test area(t) == T(0.5) * u"m^2" - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) @test area(t) > T(0.7) * u"m^2" - for p in point.([(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)]) + for p in cart.([(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)]) @test p ∈ t end - for p in point.([(-1, 0, 0), (1, 2, 0), (0, 1, 2)]) + for p in cart.([(-1, 0, 0), (1, 2, 0), (0, 1, 2)]) @test p ∉ t end - t = Triangle(point(0, 0, 0), point(0, 1, 0), point(0, 0, 1)) - @test t(T(0.0), T(0.0)) == point(0, 0, 0) - @test t(T(1.0), T(0.0)) == point(0, 1, 0) - @test t(T(0.0), T(1.0)) == point(0, 0, 1) - @test t(T(0.5), T(0.5)) == point(0, 0.5, 0.5) + t = Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test t(T(0.0), T(0.0)) == cart(0, 0, 0) + @test t(T(1.0), T(0.0)) == cart(0, 1, 0) + @test t(T(0.0), T(1.0)) == cart(0, 0, 1) + @test t(T(0.5), T(0.5)) == cart(0, 0.5, 0.5) @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) @test isapprox(normal(t), vector(1, 0, 0)) @test isapprox(norm(normal(t)), oneunit(ℳ)) - t = Triangle(point(0, 0, 0), point(2, 0, 0), point(0, 2, 2)) + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) @test isapprox(normal(t), vector(0, -0.7071067811865475, 0.7071067811865475)) @test isapprox(norm(normal(t)), oneunit(ℳ)) - t = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) # datum propagation @@ -438,7 +438,7 @@ t = Triangle(Point(c1), Point(c2), Point(c3)) @test datum(Meshes.crs(t(T(0), T(0)))) === WGS84Latest - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ @@ -459,52 +459,52 @@ # ----------- # Quadrangle in 2D space - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test Meshes.crs(q) <: Cartesian{NoDatum} @test Meshes.lentype(q) == ℳ - @test vertex(q, 1) == point(0, 0) - @test vertex(q, 2) == point(1, 0) - @test vertex(q, 3) == point(1, 1) - @test vertex(q, 4) == point(0, 1) + @test vertex(q, 1) == cart(0, 0) + @test vertex(q, 2) == cart(1, 0) + @test vertex(q, 3) == cart(1, 1) + @test vertex(q, 4) == cart(0, 1) @test area(q) == T(1) * u"m^2" - q = Quadrangle(point(0, 0), point(1, 0), point(1.5, 1.0), point(0.5, 1.0)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) @test area(q) == T(1) * u"m^2" - q = Quadrangle(point(0, 0), point(1, 0), point(1.5, 1.0), point(0.5, 1.0)) - for p in point.([(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)]) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) + for p in cart.([(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)]) @test p ∈ q end - for p in point.([(0, 1), (1.5, 0.0)]) + for p in cart.([(0, 1), (1.5, 0.0)]) @test p ∉ q end - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test !hasholes(q) @test unique(q) == q @test boundary(q) == first(rings(q)) - @test rings(q) == [Ring(point(0, 0), point(1, 0), point(1, 1), point(0, 1))] - @test q(T(0), T(0)) == point(0, 0) - @test q(T(1), T(0)) == point(1, 0) - @test q(T(1), T(1)) == point(1, 1) - @test q(T(0), T(1)) == point(0, 1) + @test rings(q) == [Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1))] + @test q(T(0), T(0)) == cart(0, 0) + @test q(T(1), T(0)) == cart(1, 0) + @test q(T(1), T(1)) == cart(1, 1) + @test q(T(0), T(1)) == cart(0, 1) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) equaltest(q) isapproxtest(q) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test perimeter(q) ≈ T(4) * u"m" # Quadrangle in 3D space - q = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) + q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) @test area(q) == T(1) * u"m^2" - q = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 1)) + q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 1)) @test area(q) > T(1) * u"m^2" - @test q(T(0), T(0)) == point(0, 0, 0) - @test q(T(1), T(0)) == point(1, 0, 0) - @test q(T(1), T(1)) == point(1, 1, 0) - @test q(T(0), T(1)) == point(0, 1, 1) + @test q(T(0), T(0)) == cart(0, 0, 0) + @test q(T(1), T(0)) == cart(1, 0, 0) + @test q(T(1), T(1)) == cart(1, 1, 0) + @test q(T(0), T(1)) == cart(0, 1, 1) # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0)) @@ -514,7 +514,7 @@ q = Quadrangle(Point(c1), Point(c2), Point(c3), Point(c4)) @test datum(Meshes.crs(q(T(0), T(0)))) === WGS84Latest - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), q) == """ @@ -537,38 +537,38 @@ @test paramdim(PolyArea) == 2 # equality and approximate equality - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test poly == poly @test poly ≈ poly @test Meshes.crs(poly) <: Cartesian{NoDatum} @test Meshes.lentype(poly) == ℳ - p = PolyArea(point(0, 0), point(1, 0), point(0, 1)) + p = PolyArea(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(p) isapproxtest(p) # outer chain with 2 vertices is fixed by default - poly = PolyArea(point.([(0, 0), (1, 0)])) - @test rings(poly) == [Ring(point.([(0, 0), (0.5, 0.0), (1, 0)]))] + poly = PolyArea(cart.([(0, 0), (1, 0)])) + @test rings(poly) == [Ring(cart.([(0, 0), (0.5, 0.0), (1, 0)]))] # inner chain with 2 vertices is removed by default - poly = PolyArea([point.([(0, 0), (1, 0), (1, 1), (0, 1)]), point.([(1, 2), (2, 3)])]) - @test rings(poly) == [Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)]))] + poly = PolyArea([cart.([(0, 0), (1, 0), (1, 1), (0, 1)]), cart.([(1, 2), (2, 3)])]) + @test rings(poly) == [Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)]))] # orientation of chains is fixed by default - poly = PolyArea(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) - @test vertices(poly) == CircularVector(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly = PolyArea(point.([(0, 0), (0, 1), (1, 1), (1, 0)]), fix=false) - @test vertices(poly) == CircularVector(point.([(0, 0), (0, 1), (1, 1), (1, 0)])) + poly = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + @test vertices(poly) == CircularVector(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)]), fix=false) + @test vertices(poly) == CircularVector(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) # test accessor methods - poly = PolyArea(point.([(1, 2), (2, 3)]), fix=false) - @test vertices(poly) == CircularVector(point.([(1, 2), (2, 3)])) - poly = PolyArea([point.([(1, 2), (2, 3)]), point.([(1.1, 2.54), (1.4, 1.5)])], fix=false) - @test vertices(poly) == CircularVector(point.([(1, 2), (2, 3), (1.1, 2.54), (1.4, 1.5)])) + poly = PolyArea(cart.([(1, 2), (2, 3)]), fix=false) + @test vertices(poly) == CircularVector(cart.([(1, 2), (2, 3)])) + poly = PolyArea([cart.([(1, 2), (2, 3)]), cart.([(1.1, 2.54), (1.4, 1.5)])], fix=false) + @test vertices(poly) == CircularVector(cart.([(1, 2), (2, 3), (1.1, 2.54), (1.4, 1.5)])) # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 @@ -636,14 +636,14 @@ end # test uniqueness - points = point.([(1, 1), (2, 2), (2, 2), (3, 3)]) + points = cart.([(1, 1), (2, 2), (2, 2), (3, 3)]) poly = PolyArea(points) unique!(poly) - @test first(rings(poly)) == Ring(point.([(1, 1), (2, 2), (3, 3)])) + @test first(rings(poly)) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) # approximately equal vertices poly = PolyArea( - point.( + cart.( [ (-48.04448403189499, -18.326530800015174) (-48.044478457836675, -18.326503670869467) @@ -685,32 +685,32 @@ @test !hasholes(upoly) # centroid - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test centroid(poly) == point(0.5, 0.5) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(poly) == cart(0.5, 0.5) # single vertex access - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test vertex(poly, 1) == point(0, 0) - @test vertex(poly, 4) == point(0, 1) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test vertex(poly, 1) == cart(0, 0) + @test vertex(poly, 4) == cart(0, 1) # point in polygonal area - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test all(p ∈ poly for p in outer) - @test point(0.5, 0.5) ∈ poly - @test point(0.2, 0.6) ∈ poly - @test point(1.5, 0.5) ∉ poly - @test point(-0.5, 0.5) ∉ poly - @test point(0.25, 0.25) ∉ poly - @test point(0.75, 0.25) ∉ poly - @test point(0.75, 0.75) ∈ poly + @test cart(0.5, 0.5) ∈ poly + @test cart(0.2, 0.6) ∈ poly + @test cart(1.5, 0.5) ∉ poly + @test cart(-0.5, 0.5) ∉ poly + @test cart(0.25, 0.25) ∉ poly + @test cart(0.75, 0.25) ∉ poly + @test cart(0.75, 0.75) ∈ poly # area - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test area(poly) ≈ T(0.92) * u"m^2" @@ -723,9 +723,9 @@ @test embeddim(p) == 3 @test Meshes.lentype(p) === Meshes.Met{Float64} - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) @test sprint(show, poly1) == "PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" @@ -743,21 +743,21 @@ └─ Ring((x: 0.6 m, y: 0.2 m), ..., (x: 0.8 m, y: 0.2 m))""" # should not repeat the first vertex manually - @test_throws ArgumentError PolyArea(point.([(0, 0), (0, 0)])) - @test_throws ArgumentError PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 0)])) + @test_throws ArgumentError PolyArea(cart.([(0, 0), (0, 0)])) + @test_throws ArgumentError PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) end @testset "Polyhedra" begin @test paramdim(Tetrahedron) == 3 @test nvertices(Tetrahedron) == 4 - t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test Meshes.crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ - @test vertex(t, 1) == point(0, 0, 0) - @test vertex(t, 2) == point(1, 0, 0) - @test vertex(t, 3) == point(0, 1, 0) - @test vertex(t, 4) == point(0, 0, 1) + @test vertex(t, 1) == cart(0, 0, 0) + @test vertex(t, 2) == cart(1, 0, 0) + @test vertex(t, 3) == cart(0, 1, 0) + @test vertex(t, 4) == cart(0, 0, 1) @test measure(t) == T(1 / 6) * u"m^3" m = boundary(t) n = normal.(m) @@ -768,13 +768,13 @@ @test n[2] == vector(0, -1, 0) @test n[3] == vector(-1, 0, 0) @test all(>(T(0) * u"m"), n[4]) - @test t(T(0), T(0), T(0)) ≈ point(0, 0, 0) - @test t(T(1), T(0), T(0)) ≈ point(1, 0, 0) - @test t(T(0), T(1), T(0)) ≈ point(0, 1, 0) - @test t(T(0), T(0), T(1)) ≈ point(0, 0, 1) + @test t(T(0), T(0), T(0)) ≈ cart(0, 0, 0) + @test t(T(1), T(0), T(0)) ≈ cart(1, 0, 0) + @test t(T(0), T(1), T(0)) ≈ cart(0, 1, 0) + @test t(T(0), T(0), T(1)) ≈ cart(0, 0, 1) @test_throws DomainError((T(1), T(1), T(1)), "invalid barycentric coordinates for tetrahedron.") t(T(1), T(1), T(1)) - t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) equaltest(t) isapproxtest(t) @@ -791,7 +791,7 @@ t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) @test datum(Meshes.crs(t(T(0), T(0), T(0)))) === WGS84Latest - t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ @@ -813,61 +813,61 @@ @test nvertices(Hexahedron) == 8 h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) @test Meshes.crs(h) <: Cartesian{NoDatum} @test Meshes.lentype(h) == ℳ - @test vertex(h, 1) == point(0, 0, 0) - @test vertex(h, 8) == point(0, 1, 1) - @test h(T(0), T(0), T(0)) == point(0, 0, 0) - @test h(T(0), T(0), T(1)) == point(0, 0, 1) - @test h(T(0), T(1), T(0)) == point(0, 1, 0) - @test h(T(0), T(1), T(1)) == point(0, 1, 1) - @test h(T(1), T(0), T(0)) == point(1, 0, 0) - @test h(T(1), T(0), T(1)) == point(1, 0, 1) - @test h(T(1), T(1), T(0)) == point(1, 1, 0) - @test h(T(1), T(1), T(1)) == point(1, 1, 1) + @test vertex(h, 1) == cart(0, 0, 0) + @test vertex(h, 8) == cart(0, 1, 1) + @test h(T(0), T(0), T(0)) == cart(0, 0, 0) + @test h(T(0), T(0), T(1)) == cart(0, 0, 1) + @test h(T(0), T(1), T(0)) == cart(0, 1, 0) + @test h(T(0), T(1), T(1)) == cart(0, 1, 1) + @test h(T(1), T(0), T(0)) == cart(1, 0, 0) + @test h(T(1), T(0), T(1)) == cart(1, 0, 1) + @test h(T(1), T(1), T(0)) == cart(1, 1, 0) + @test h(T(1), T(1), T(1)) == cart(1, 1, 1) h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) equaltest(h) isapproxtest(h) h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) @test volume(h) ≈ T(1 * 1 * 1) * u"m^3" h = Hexahedron( - point(0, 0, 0), - point(2, 0, 0), - point(2, 2, 0), - point(0, 2, 0), - point(0, 0, 2), - point(2, 0, 2), - point(2, 2, 2), - point(0, 2, 2) + cart(0, 0, 0), + cart(2, 0, 0), + cart(2, 2, 0), + cart(0, 2, 0), + cart(0, 0, 2), + cart(2, 0, 2), + cart(2, 2, 2), + cart(0, 2, 2) ) @test volume(h) ≈ T(2 * 2 * 2) * u"m^3" @@ -875,26 +875,26 @@ # here we build a hexahedron which is a frustum of a prism with # bottom area S₁= 4, top area S₂= 1, height H = 2 h = Hexahedron( - point(0, 0, 0), - point(2, 0, 0), - point(2, 2, 0), - point(0, 2, 0), - point(0, 0, 2), - point(1, 0, 2), - point(1, 1, 2), - point(0, 1, 2) + cart(0, 0, 0), + cart(2, 0, 0), + cart(2, 2, 0), + cart(0, 2, 0), + cart(0, 0, 2), + cart(1, 0, 2), + cart(1, 1, 2), + cart(0, 1, 2) ) @test volume(h) ≈ T(1 / 3 * 2 * (1 + 4 + sqrt(1 * 4))) * u"m^3" h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) m = boundary(h) @test m isa Mesh @@ -919,14 +919,14 @@ @test datum(Meshes.crs(h(T(0), T(0), T(0)))) === WGS84Latest h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) @test sprint(show, h) == "Hexahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" if T === Float32 @@ -956,7 +956,7 @@ @test paramdim(Pyramid) == 3 @test nvertices(Pyramid) == 5 - p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) @test Meshes.crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test volume(p) ≈ T(1 / 3) * u"m^3" @@ -969,7 +969,7 @@ @test m[4] isa Triangle @test m[5] isa Triangle - p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) equaltest(p) isapproxtest(p) @@ -978,7 +978,7 @@ @test embeddim(p) == 3 @test Meshes.lentype(p) === Meshes.Met{Float64} - p = Pyramid(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0), point(0, 0, 1)) + p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), p) == """ diff --git a/test/predicates.jl b/test/predicates.jl index 97e7a9e7e..f6dc3f42b 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -1,98 +1,98 @@ @testset "Predicates" begin @testset "issimplex" begin @test issimplex(Segment) - @test issimplex(Segment(point(0, 0), point(1, 0))) + @test issimplex(Segment(cart(0, 0), cart(1, 0))) @test issimplex(Triangle) - @test issimplex(Triangle(point(0, 0), point(1, 0), point(0, 1))) + @test issimplex(Triangle(cart(0, 0), cart(1, 0), cart(0, 1))) @test issimplex(Tetrahedron) - @test issimplex(Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1))) + @test issimplex(Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1))) end @testset "isconvex" begin # primitives - r = Ray(point(0, 0), vector(1, 1)) + r = Ray(cart(0, 0), vector(1, 1)) @test isconvex(r) - l = Line(point(0, 0), point(1, 1)) + l = Line(cart(0, 0), cart(1, 1)) @test isconvex(l) - p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) @test isconvex(p) - b = Box(point(0), point(1)) + b = Box(cart(0), cart(1)) @test isconvex(b) - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) @test isconvex(b) - b = Box(point(0, 0, 0), point(1, 1, 1)) - b = Ball(point(1, 2, 3), T(5)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + b = Ball(cart(1, 2, 3), T(5)) @test isconvex(b) @test isconvex(b) - s = Sphere(point(0, 0), T(1)) + s = Sphere(cart(0, 0), T(1)) @test !isconvex(s) - s = Sphere(point(0, 0, 0), T(1)) + s = Sphere(cart(0, 0, 0), T(1)) @test !isconvex(s) - d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) @test isconvex(d) - c = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + c = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) @test !isconvex(c) - b = BezierCurve(point.([(0, 0), (1, 0), (2, 0)])) + b = BezierCurve(cart.([(0, 0), (1, 0), (2, 0)])) @test isconvex(b) - b = BezierCurve(point.([(0, 0), (1, 1), (2, 2)])) + b = BezierCurve(cart.([(0, 0), (1, 1), (2, 2)])) @test isconvex(b) - b = BezierCurve(point.([(0, 0)])) + b = BezierCurve(cart.([(0, 0)])) @test isconvex(b) - b = BezierCurve(point.([(0, 0), (1, 0)])) + b = BezierCurve(cart.([(0, 0), (1, 0)])) @test isconvex(b) - b = BezierCurve(point.([(0, 0), (5, 3), (-10, 3), (17, 20)])) + b = BezierCurve(cart.([(0, 0), (5, 3), (-10, 3), (17, 20)])) @test !isconvex(b) - b = BezierCurve(point.([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)])) + b = BezierCurve(cart.([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)])) @test isconvex(b) - P = typeof(point(0, 0)) + P = typeof(cart(0, 0)) b = BezierCurve(P[]) @test isconvex(b) - c = Cylinder(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) + c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) @test isconvex(c) c = CylinderSurface(T(2)) @test !isconvex(c) - d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) - a = point(0, 0, 1) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + a = cart(0, 0, 1) c = Cone(d, a) @test isconvex(c) - d = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) - a = point(0, 0, 1) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + a = cart(0, 0, 1) c = ConeSurface(d, a) @test !isconvex(c) t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) @test !isconvex(t) # polytopes - s = Segment(point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) @test isconvex(s) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test isconvex(t) - q1 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - q2 = Quadrangle(point(0.8, 0.8), point(1, 0), point(1, 1), point(0, 1)) - q3 = Quadrangle(point(0, 0), point(0.2, 0.8), point(1, 1), point(0, 1)) - q4 = Quadrangle(point(0, 0), point(1, 0), point(0.2, 0.2), point(0, 1)) - q5 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0.8, 0.2)) + q1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + q2 = Quadrangle(cart(0.8, 0.8), cart(1, 0), cart(1, 1), cart(0, 1)) + q3 = Quadrangle(cart(0, 0), cart(0.2, 0.8), cart(1, 1), cart(0, 1)) + q4 = Quadrangle(cart(0, 0), cart(1, 0), cart(0.2, 0.2), cart(0, 1)) + q5 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.8, 0.2)) @test isconvex(q1) @test !isconvex(q2) @test !isconvex(q3) @test !isconvex(q4) @test !isconvex(q5) - q1 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) - q2 = Quadrangle(point(0.8, 0.8, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) - q3 = Quadrangle(point(0, 0, 0), point(0.2, 0.8, 0), point(1, 1, 0), point(0, 1, 0)) - q4 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(0.2, 0.2, 0), point(0, 1, 0)) - q5 = Quadrangle(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0.8, 0.2, 0)) + q1 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + q2 = Quadrangle(cart(0.8, 0.8, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + q3 = Quadrangle(cart(0, 0, 0), cart(0.2, 0.8, 0), cart(1, 1, 0), cart(0, 1, 0)) + q4 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(0.2, 0.2, 0), cart(0, 1, 0)) + q5 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0.8, 0.2, 0)) @test isconvex(q1) @test !isconvex(q2) @test !isconvex(q3) @test !isconvex(q4) @test !isconvex(q5) - t = Tetrahedron(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0), point(0, 0, 1)) + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test isconvex(t) - outer = point.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) - inner = point.([(5, 7), (10, 12), (15, 7)]) + outer = cart.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) + inner = cart.([(5, 7), (10, 12), (15, 7)]) pent = Pentagon(outer...) tri = Triangle(inner...) poly = PolyArea([outer, inner]) @@ -101,14 +101,14 @@ @test isconvex(tri) @test !isconvex(poly) @test isconvex(multi) - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) @test isconvex(poly1) @test !isconvex(poly2) - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) @test !isconvex(poly) end @@ -161,10 +161,10 @@ @testset "in" begin h = first(cartgrid(10, 10, 10)) - @test point(0, 0, 0) ∈ h - @test point(0.5, 0.5, 0.5) ∈ h - @test point(-1, 0, 0) ∉ h - @test point(0, 2, 0) ∉ h + @test cart(0, 0, 0) ∈ h + @test cart(0.5, 0.5, 0.5) ∈ h + @test cart(-1, 0, 0) ∉ h + @test cart(0, 2, 0) ∉ h outer = Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))]) hole1 = Point.([LatLon(T(0.2), T(0.2)), LatLon(T(0.4), T(0.2)), LatLon(T(0.4), T(0.4)), LatLon(T(0.2), T(0.4))]) @@ -181,11 +181,11 @@ end @testset "issubset" begin - p = point(0.5, 0.5) - box = Box(point(0, 0), point(1, 1)) - ball = Ball(point(0, 0)) - tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) - quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + p = cart(0.5, 0.5) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test p ⊆ box @test p ⊆ ball @test p ⊆ tri @@ -193,31 +193,31 @@ @test p ⊆ p @test quad ⊆ quad - s1 = Segment(point(0, 0), point(1, 1)) - s2 = Segment(point(0.5, 0.5), point(1, 1)) - s3 = Segment(point(0, 0), point(0.5, 0.5)) + s1 = Segment(cart(0, 0), cart(1, 1)) + s2 = Segment(cart(0.5, 0.5), cart(1, 1)) + s3 = Segment(cart(0, 0), cart(0.5, 0.5)) @test s2 ⊆ s1 @test s3 ⊆ s1 @test s1 ⊆ s1 - seg = Segment(point(0, 0), point(1, 1)) - box = Box(point(0, 0), point(1, 1)) - ball = Ball(point(0, 0)) + seg = Segment(cart(0, 0), cart(1, 1)) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) @test seg ⊆ box @test !(seg ⊆ ball) - t1 = Triangle(point(0, 0), point(1, 0), point(1, 1)) - t2 = Triangle(point(0, 0), point(1, 0), point(0.8, 0.8)) - t3 = Triangle(point(0, 0), point(1, 0), point(1.1, 1.1)) + t1 = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + t2 = Triangle(cart(0, 0), cart(1, 0), cart(0.8, 0.8)) + t3 = Triangle(cart(0, 0), cart(1, 0), cart(1.1, 1.1)) @test t2 ⊆ t1 @test !(t3 ⊆ t1) - tri = Triangle(point(0, 0), point(1, 0), point(1, 1)) - box = Box(point(0, 0), point(1, 1)) - ball = Ball(point(0, 0)) - quad = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - pent = Pentagon(point(0, 0), point(1, 0), point(1, 1), point(0.5, 1.5), point(0, 1)) - poly = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + pent = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 1.5), cart(0, 1)) + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test tri ⊆ quad @test !(quad ⊆ tri) @test tri ⊆ box @@ -233,18 +233,18 @@ @test quad ⊆ poly @test poly ⊆ quad - quad1 = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) - quad2 = Quadrangle(point(0, 0), point(1.1, 0), point(1, 1), point(0, 1)) - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + quad1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + quad2 = Quadrangle(cart(0, 0), cart(1.1, 0), cart(1, 1), cart(0, 1)) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) multi = Multi([poly]) @test quad1 ⊆ poly @test !(quad2 ⊆ poly) @test quad1 ⊆ multi @test !(quad2 ⊆ multi) - p1 = point(-1.0, 0.0) - p2 = point(0.0, 0.0) - p3 = point(1.0, 0.0) + p1 = cart(-1.0, 0.0) + p2 = cart(0.0, 0.0) + p3 = cart(1.0, 0.0) l1 = Line(p1, p3) l2 = Line(p2, p3) @test l1 ⊆ l2 @@ -252,8 +252,8 @@ @test l1 ⊆ l1 @test l2 ⊆ l2 - pts1 = point.([(5, 7), (10, 12), (15, 7)]) - pts2 = point.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) + pts1 = cart.([(5, 7), (10, 12), (15, 7)]) + pts2 = cart.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) pent = Pentagon(pts2...) tri = Triangle(pts1...) poly1 = PolyArea(pts2) @@ -267,11 +267,11 @@ @test pent ⊈ poly2 @test pent ⊆ multi - poly1 = PolyArea(point.([(4, 12), (11, 11), (16, 8), (16, 1), (13, -2), (2, -2), (-3, 4), (-2, 8)])) - poly2 = PolyArea(point.([(3, 0), (1, 2), (3, 4), (1, 6), (4, 7), (10, 7), (11, 4), (9, 0)])) - poly3 = PolyArea(point.([(3, 2), (4, 4), (3, 8), (12, 8), (14, 4), (12, 1)])) - poly4 = PolyArea(point.([(8, 2), (5, 4), (5, 6), (9, 6), (10, 4)])) - poly5 = PolyArea(point.([(3, 9), (6, 11), (10, 10), (10, 9)])) + poly1 = PolyArea(cart.([(4, 12), (11, 11), (16, 8), (16, 1), (13, -2), (2, -2), (-3, 4), (-2, 8)])) + poly2 = PolyArea(cart.([(3, 0), (1, 2), (3, 4), (1, 6), (4, 7), (10, 7), (11, 4), (9, 0)])) + poly3 = PolyArea(cart.([(3, 2), (4, 4), (3, 8), (12, 8), (14, 4), (12, 1)])) + poly4 = PolyArea(cart.([(8, 2), (5, 4), (5, 6), (9, 6), (10, 4)])) + poly5 = PolyArea(cart.([(3, 9), (6, 11), (10, 10), (10, 9)])) @test poly2 ⊆ poly1 @test poly3 ⊆ poly1 @test poly4 ⊆ poly1 @@ -283,22 +283,22 @@ end @testset "intersects" begin - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) - q = Quadrangle(point(1, 1), point(2, 1), point(2, 2), point(1, 2)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + q = Quadrangle(cart(1, 1), cart(2, 1), cart(2, 2), cart(1, 2)) @test intersects(t, t) @test intersects(q, q) @test !intersects(t, q) @test !intersects(q, t) - t = Triangle(point(1, 0), point(2, 0), point(1, 1)) - q = Quadrangle(point(1.3, 0.5), point(2.3, 0.5), point(2.3, 1.5), point(1.3, 1.5)) + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) @test intersects(t, t) @test intersects(q, q) @test intersects(t, q) @test intersects(q, t) - t = Triangle(point(1, 0), point(2, 0), point(1, 1)) - q = Quadrangle(point(1.3, 0.5), point(2.3, 0.5), point(2.3, 1.5), point(1.3, 1.5)) + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) m = Multi([t, q]) @test intersects(m, t) @test intersects(t, m) @@ -306,52 +306,52 @@ @test intersects(q, m) @test intersects(m, m) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) - b = Ball(point(0, 0), T(1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + b = Ball(cart(0, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test intersects(t, b) @test intersects(b, t) - t = Triangle(point(1, 0), point(2, 0), point(1, 1)) - b = Ball(point(0, 0), T(1)) + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + b = Ball(cart(0, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test intersects(t, b) @test intersects(b, t) - t = Triangle(point(1, 0), point(2, 0), point(1, 1)) - b = Ball(point(-0.01, 0), T(1)) + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + b = Ball(cart(-0.01, 0), T(1)) @test intersects(t, t) @test intersects(b, b) @test !intersects(t, b) @test !intersects(b, t) # https://github.com/JuliaGeometry/Meshes.jl/issues/250 - t1 = Triangle(point(0, 0, 0), point(2, 0, 0), point(1, 2, 0)) - t2 = Triangle(point(1, 0, 0), point(3, 0, 0), point(2, 2, 0)) - t3 = Triangle(point(3, 0, 0), point(5, 0, 0), point(4, 2, 0)) + t1 = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) + t2 = Triangle(cart(1, 0, 0), cart(3, 0, 0), cart(2, 2, 0)) + t3 = Triangle(cart(3, 0, 0), cart(5, 0, 0), cart(4, 2, 0)) @test intersects(t1, t2) @test intersects(t2, t3) @test !intersects(t1, t3) # https://github.com/JuliaGeometry/Meshes.jl/issues/639 - r = Ray(point(0.41169768366272996, 0.8990554132423699), vector(0.47249211625247445, 0.2523149692768657)) - b = Box(point(1.0, 1.0), point(5.0, 2.0)) + r = Ray(cart(0.41169768366272996, 0.8990554132423699), vector(0.47249211625247445, 0.2523149692768657)) + b = Box(cart(1.0, 1.0), cart(5.0, 2.0)) @test intersects(r, b) @test intersects(b, r) - t = Triangle(point(0, 0, 0), point(2, 0, 0), point(1, 2, 0)) - r1 = Ray(point(1, 1, 1), vector(0, 0, -1)) - r2 = Ray(point(1, 1, 1), vector(0, 0, 1)) + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) + r1 = Ray(cart(1, 1, 1), vector(0, 0, -1)) + r2 = Ray(cart(1, 1, 1), vector(0, 0, 1)) @test intersects(r1, t) @test intersects(t, r1) @test !intersects(r2, t) @test !intersects(t, r2) - r = Ray(point(0, 0), vector(1, 0)) - s1 = Sphere(point(3, 0), T(1)) - s2 = Sphere(point(0, 3), T(1)) + r = Ray(cart(0, 0), vector(1, 0)) + s1 = Sphere(cart(3, 0), T(1)) + s2 = Sphere(cart(0, 3), T(1)) @test intersects(r, s1) @test !intersects(r, s2) @@ -375,41 +375,41 @@ @test !intersects(t(r), t(s2)) end - r = Ray(point(0, 0), vector(1, 0)) - s = Sphere(point(floatmax(Float32) / 2, 0), 1) + r = Ray(cart(0, 0), vector(1, 0)) + s = Sphere(cart(floatmax(Float32) / 2, 0), 1) @test intersects(r, s) - r = Ray(point(0, 0, 0), vector(1, 0, 0)) - s1 = Sphere(point(5, 0, 1 - eps(T(1))), T(1)) - s2 = Sphere(point(5, 0, 1 + eps(T(1))), T(1)) + r = Ray(cart(0, 0, 0), vector(1, 0, 0)) + s1 = Sphere(cart(5, 0, 1 - eps(T(1))), T(1)) + s2 = Sphere(cart(5, 0, 1 + eps(T(1))), T(1)) @test intersects(r, s1) @test !intersects(r, s2) # https://github.com/JuliaGeometry/Meshes.jl/issues/635 - q1 = Quadrangle(point(4.0, 4.0, 0.0), point(3.0, 3.0, 2.0), point(3.0, 1.0, 2.0), point(4.0, 0.0, 0.0)) - q2 = Quadrangle(point(3.6, 3.0, 1.0), point(5.6, 3.0, 1.0), point(5.6, 1.0, 1.0), point(3.6, 1.0, 1.0)) - q3 = Quadrangle(point(3.6, 1.0, 1.0), point(5.6, 1.0, 1.0), point(5.6, -1.0, 1.0), point(3.6, -1.0, 1.0)) - q4 = Quadrangle(point(2.1, 1.0, 1.0), point(4.1, 1.0, 1.0), point(4.1, -1.0, 1.0), point(2.1, -1.0, 1.0)) + q1 = Quadrangle(cart(4.0, 4.0, 0.0), cart(3.0, 3.0, 2.0), cart(3.0, 1.0, 2.0), cart(4.0, 0.0, 0.0)) + q2 = Quadrangle(cart(3.6, 3.0, 1.0), cart(5.6, 3.0, 1.0), cart(5.6, 1.0, 1.0), cart(3.6, 1.0, 1.0)) + q3 = Quadrangle(cart(3.6, 1.0, 1.0), cart(5.6, 1.0, 1.0), cart(5.6, -1.0, 1.0), cart(3.6, -1.0, 1.0)) + q4 = Quadrangle(cart(2.1, 1.0, 1.0), cart(4.1, 1.0, 1.0), cart(4.1, -1.0, 1.0), cart(2.1, -1.0, 1.0)) @test !intersects(q1, q2) @test !intersects(q1, q3) @test intersects(q1, q1) @test intersects(q1, q4) - h1 = Tetrahedron(point(1, 1, 0), point(4, 4, 0), point(2.5, 2.5, 1.5), point(1, 3, 2)) - h2 = Tetrahedron(point(-1.0, 2.0, 1.0), point(2.0, 1.0, 1.0), point(-1.0, 4.0, 0.0), point(0.5, 2.5, 1.5)) - h3 = Tetrahedron(point(-1.3, 2.0, 1.0), point(1.7, 1.0, 1.0), point(-1.3, 4.0, 0.0), point(0.2, 2.5, 1.5)) + h1 = Tetrahedron(cart(1, 1, 0), cart(4, 4, 0), cart(2.5, 2.5, 1.5), cart(1, 3, 2)) + h2 = Tetrahedron(cart(-1.0, 2.0, 1.0), cart(2.0, 1.0, 1.0), cart(-1.0, 4.0, 0.0), cart(0.5, 2.5, 1.5)) + h3 = Tetrahedron(cart(-1.3, 2.0, 1.0), cart(1.7, 1.0, 1.0), cart(-1.3, 4.0, 0.0), cart(0.2, 2.5, 1.5)) @test intersects(h1, h2) @test !intersects(h1, h3) - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly1 = PolyArea(outer) poly2 = PolyArea([outer, hole1, hole2]) - ball1 = Ball(point(0.5, 0.5), T(0.05)) - ball2 = Ball(point(0.3, 0.3), T(0.05)) - ball3 = Ball(point(0.7, 0.3), T(0.05)) - ball4 = Ball(point(0.3, 0.3), T(0.15)) + ball1 = Ball(cart(0.5, 0.5), T(0.05)) + ball2 = Ball(cart(0.3, 0.3), T(0.05)) + ball3 = Ball(cart(0.7, 0.3), T(0.05)) + ball4 = Ball(cart(0.3, 0.3), T(0.15)) @test intersects(poly1, poly1) @test intersects(poly2, poly2) @test intersects(poly1, poly2) @@ -429,21 +429,21 @@ @test intersects(mesh1, mesh2) @test intersects(mesh2, mesh1) - p = point(0.5, 0.5) - ball = Ball(point(0, 0), T(1)) + p = cart(0.5, 0.5) + ball = Ball(cart(0, 0), T(1)) @test intersects(p, ball) @test intersects(ball, p) @test intersects(p, p) @test !intersects(p, p + vector(1, 1)) - poly = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - box = Box(point(0, 0), point(2, 2)) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + box = Box(cart(0, 0), cart(2, 2)) @test intersects(poly, box) - b1 = Box(point(0, 0), point(2, 2)) - b2 = Box(point(2, 0), point(4, 2)) - p1 = point(1, 1) - p2 = point(3, 1) + b1 = Box(cart(0, 0), cart(2, 2)) + b2 = Box(cart(2, 0), cart(4, 2)) + p1 = cart(1, 1) + p2 = cart(3, 1) m = Multi([b1, b2]) @test intersects(p1, b1) @test !intersects(p2, b1) @@ -454,18 +454,18 @@ @test intersects(m, p2) @test intersects(p2, m) - s1 = Segment(point(0, 0), point(4, 4)) - s2 = Segment(point(4, 0), point(0, 4)) - s3 = Segment(point(2, 0), point(4, 2)) + s1 = Segment(cart(0, 0), cart(4, 4)) + s2 = Segment(cart(4, 0), cart(0, 4)) + s3 = Segment(cart(2, 0), cart(4, 2)) @test intersects(s1, s2) @test intersects(s2, s3) @test !intersects(s1, s3) - s1 = Segment(point(4, 0), point(0, 4)) - s2 = Segment(point(4, 0), point(8, 4)) - s3 = Segment(point(0, 8), point(8, 8)) - r1 = Rope(point.([(0, 0), (4, 4), (8, 0)])) - r2 = Ring(point.([(0, 2), (4, 6), (8, 2)])) + s1 = Segment(cart(4, 0), cart(0, 4)) + s2 = Segment(cart(4, 0), cart(8, 4)) + s3 = Segment(cart(0, 8), cart(8, 8)) + r1 = Rope(cart.([(0, 0), (4, 4), (8, 0)])) + r2 = Ring(cart.([(0, 2), (4, 6), (8, 2)])) @test intersects(s1, r1) @test intersects(s2, r1) @test !intersects(s3, r1) @@ -474,30 +474,30 @@ @test !intersects(s3, r2) @test intersects(r1, r2) - r1 = Rope(point.([(0, 0), (2, 2), (4, 0)])) - r2 = Rope(point.([(3, 0), (5, 2), (7, 0)])) - r3 = Rope(point.([(6, 0), (8, 2), (10, 0)])) + r1 = Rope(cart.([(0, 0), (2, 2), (4, 0)])) + r2 = Rope(cart.([(3, 0), (5, 2), (7, 0)])) + r3 = Rope(cart.([(6, 0), (8, 2), (10, 0)])) @test intersects(r1, r2) @test intersects(r2, r3) @test !intersects(r1, r3) - r1 = Ring(point.([(0, 0), (2, 2), (4, 0)])) - r2 = Ring(point.([(3, 0), (5, 2), (7, 0)])) - r3 = Ring(point.([(6, 0), (8, 2), (10, 0)])) + r1 = Ring(cart.([(0, 0), (2, 2), (4, 0)])) + r2 = Ring(cart.([(3, 0), (5, 2), (7, 0)])) + r3 = Ring(cart.([(6, 0), (8, 2), (10, 0)])) @test intersects(r1, r2) @test intersects(r2, r3) @test !intersects(r1, r3) - t = Triangle(point(3, 1), point(7, 5), point(11, 1)) - q = Quadrangle(point(2, 0), point(2, 7), point(12, 7), point(12, 0)) - b = Box(point(2, 0), point(12, 7)) - s1 = Segment(point(5, 2), point(9, 2)) - s2 = Segment(point(0, 3), point(5, 3)) - s3 = Segment(point(4, 4), point(10, 4)) - s4 = Segment(point(1, 6), point(13, 6)) - s5 = Segment(point(0, 9), point(14, 9)) - r1 = Ring(point.([(1, 2), (7, 8), (13, 2)])) - r2 = Rope(point.([(1, 2), (7, 8), (13, 2)])) + t = Triangle(cart(3, 1), cart(7, 5), cart(11, 1)) + q = Quadrangle(cart(2, 0), cart(2, 7), cart(12, 7), cart(12, 0)) + b = Box(cart(2, 0), cart(12, 7)) + s1 = Segment(cart(5, 2), cart(9, 2)) + s2 = Segment(cart(0, 3), cart(5, 3)) + s3 = Segment(cart(4, 4), cart(10, 4)) + s4 = Segment(cart(1, 6), cart(13, 6)) + s5 = Segment(cart(0, 9), cart(14, 9)) + r1 = Ring(cart.([(1, 2), (7, 8), (13, 2)])) + r2 = Rope(cart.([(1, 2), (7, 8), (13, 2)])) @test intersects(s1, t) @test intersects(s2, t) @test intersects(s3, t) @@ -521,25 +521,25 @@ @test intersects(r2, b) # performance test - b1 = Box(point(0, 0), point(3, 3)) - b2 = Box(point(2, 2), point(5, 5)) + b1 = Box(cart(0, 0), cart(3, 3)) + b2 = Box(cart(2, 2), cart(5, 5)) @test intersects(b1, b2) @test intersects(b2, b1) @test @elapsed(intersects(b1, b2)) < 5e-5 @test @allocated(intersects(b1, b2)) < 100 # partial application - points = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) poly = PolyArea(points) - box = Box(point(0, 0), point(2, 2)) + box = Box(cart(0, 0), cart(2, 2)) @test intersects(box)(poly) @test all(intersects(box), points) # method ambiguities - p = point(3, 1) - ring = Ring(point.([(0, 0), (2, 2), (4, 0)])) - rope = Rope(point.([(2, 0), (4, 2), (6, 0)])) - seg = Segment(point(0, 1), point(6, 1)) + p = cart(3, 1) + ring = Ring(cart.([(0, 0), (2, 2), (4, 0)])) + rope = Rope(cart.([(2, 0), (4, 2), (6, 0)])) + seg = Segment(cart(0, 1), cart(6, 1)) multi = Multi([ring]) @test intersects(p, ring) @test intersects(p, rope) @@ -551,10 +551,10 @@ end @testset "iscollinear" begin - @test iscollinear(point(0, 0), point(1, 1), point(2, 2)) + @test iscollinear(cart(0, 0), cart(1, 1), cart(2, 2)) end @testset "iscoplanar" begin - @test iscoplanar(point(0, 0, 0), point(1, 0, 0), point(1, 1, 0), point(0, 1, 0)) + @test iscoplanar(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) end end diff --git a/test/primitives.jl b/test/primitives.jl index 277ad70fa..c4074d80f 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -3,7 +3,7 @@ @test embeddim(Point(1)) == 1 @test embeddim(Point(1, 2)) == 2 @test embeddim(Point(1, 2, 3)) == 3 - @test Meshes.crs(point(1, 1)) <: Cartesian{NoDatum} + @test Meshes.crs(cart(1, 1)) <: Cartesian{NoDatum} @test Meshes.crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} @test Meshes.crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} @@ -12,38 +12,38 @@ @test Meshes.lentype(Point((T(1), T(1)))) == ℳ @test Meshes.lentype(Point(T(1), T(1))) == ℳ - equaltest(point(1)) - equaltest(point(1, 2)) - equaltest(point(1, 2, 3)) - isapproxtest(point(1)) - isapproxtest(point(1, 2)) - isapproxtest(point(1, 2, 3)) + equaltest(cart(1)) + equaltest(cart(1, 2)) + equaltest(cart(1, 2, 3)) + isapproxtest(cart(1)) + isapproxtest(cart(1, 2)) + isapproxtest(cart(1, 2, 3)) - @test to(point(1)) == vector(1) - @test to(point(1, 2)) == vector(1, 2) - @test to(point(1, 2, 3)) == vector(1, 2, 3) + @test to(cart(1)) == vector(1) + @test to(cart(1, 2)) == vector(1, 2) + @test to(cart(1, 2, 3)) == vector(1, 2, 3) @test to(Point(Polar(T(√2), T(π / 4)))) ≈ vector(1, 1) @test to(Point(Cylindrical(T(√2), T(π / 4), T(1)))) ≈ vector(1, 1, 1) - @test point(1) - point(1) == vector(0) - @test point(1, 2) - point(1, 1) == vector(0, 1) - @test point(1, 2, 3) - point(1, 1, 1) == vector(0, 1, 2) - @test_throws MethodError point(1, 2) - point(1, 2, 3) - - @test point(1) + vector(0) == point(1) - @test point(2) + vector(2) == point(4) - @test point(1, 2) + vector(0, 0) == point(1, 2) - @test point(2, 3) + vector(2, 1) == point(4, 4) - @test point(1, 2, 3) + vector(0, 0, 0) == point(1, 2, 3) - @test point(2, 3, 4) + vector(2, 1, 0) == point(4, 4, 4) - @test_throws MethodError point(1, 2) + vector(1, 2, 3) - - @test point(1) - vector(0) == point(1) - @test point(2) - vector(2) == point(0) - @test point(1, 2) - vector(0, 0) == point(1, 2) - @test point(2, 3) - vector(2, 1) == point(0, 2) - @test point(1, 2, 3) - vector(0, 0, 0) == point(1, 2, 3) - @test point(2, 3, 4) - vector(2, 1, 0) == point(0, 2, 4) + @test cart(1) - cart(1) == vector(0) + @test cart(1, 2) - cart(1, 1) == vector(0, 1) + @test cart(1, 2, 3) - cart(1, 1, 1) == vector(0, 1, 2) + @test_throws MethodError cart(1, 2) - cart(1, 2, 3) + + @test cart(1) + vector(0) == cart(1) + @test cart(2) + vector(2) == cart(4) + @test cart(1, 2) + vector(0, 0) == cart(1, 2) + @test cart(2, 3) + vector(2, 1) == cart(4, 4) + @test cart(1, 2, 3) + vector(0, 0, 0) == cart(1, 2, 3) + @test cart(2, 3, 4) + vector(2, 1, 0) == cart(4, 4, 4) + @test_throws MethodError cart(1, 2) + vector(1, 2, 3) + + @test cart(1) - vector(0) == cart(1) + @test cart(2) - vector(2) == cart(0) + @test cart(1, 2) - vector(0, 0) == cart(1, 2) + @test cart(2, 3) - vector(2, 1) == cart(0, 2) + @test cart(1, 2, 3) - vector(0, 0, 0) == cart(1, 2, 3) + @test cart(2, 3, 4) - vector(2, 1, 0) == cart(0, 2, 4) @test embeddim(rand(Point{1})) == 1 @test embeddim(rand(Point{2})) == 2 @@ -52,9 +52,9 @@ @test Meshes.lentype(rand(Point{2})) == Meshes.Met{Float64} @test Meshes.lentype(rand(Point{3})) == Meshes.Met{Float64} - @test point(1) ≈ point(1 + eps(T)) - @test point(1, 2) ≈ point(1 + eps(T), T(2)) - @test point(1, 2, 3) ≈ point(1 + eps(T), T(2), T(3)) + @test cart(1) ≈ cart(1 + eps(T)) + @test cart(1, 2) ≈ cart(1 + eps(T), T(2)) + @test cart(1, 2, 3) ≈ cart(1 + eps(T), T(2), T(3)) @test embeddim(Point((1,))) == 1 @test Meshes.lentype(Point((1,))) == Meshes.Met{Float64} @@ -98,7 +98,7 @@ @test Unitful.numtype(Meshes.lentype(p)) === Float32 # conversions - P = typeof(point(1, 1)) + P = typeof(cart(1, 1)) p1 = Point(1.0, 1.0) p2 = convert(P, p1) @test p2 isa P @@ -107,22 +107,22 @@ @test p2 isa P # generalized inequality - @test point(1, 1) ⪯ point(1, 1) - @test !(point(1, 1) ≺ point(1, 1)) - @test point(1, 2) ⪯ point(3, 4) - @test point(1, 2) ≺ point(3, 4) - @test point(1, 1) ⪰ point(1, 1) - @test !(point(1, 1) ≻ point(1, 1)) - @test point(3, 4) ⪰ point(1, 2) - @test point(3, 4) ≻ point(1, 2) + @test cart(1, 1) ⪯ cart(1, 1) + @test !(cart(1, 1) ≺ cart(1, 1)) + @test cart(1, 2) ⪯ cart(3, 4) + @test cart(1, 2) ≺ cart(3, 4) + @test cart(1, 1) ⪰ cart(1, 1) + @test !(cart(1, 1) ≻ cart(1, 1)) + @test cart(3, 4) ⪰ cart(1, 2) + @test cart(3, 4) ≻ cart(1, 2) # center and centroid - @test Meshes.center(point(1, 1)) == point(1, 1) - @test centroid(point(1, 1)) == point(1, 1) + @test Meshes.center(cart(1, 1)) == cart(1, 1) + @test centroid(cart(1, 1)) == cart(1, 1) # measure of points is zero - @test measure(point(1, 2)) == zero(ℳ) - @test measure(point(1, 2, 3)) == zero(ℳ) + @test measure(cart(1, 2)) == zero(ℳ) + @test measure(cart(1, 2, 3)) == zero(ℳ) # boundary of points is nothing @test isnothing(boundary(rand(Point{1}))) @@ -130,36 +130,36 @@ @test isnothing(boundary(rand(Point{3}))) # check broadcasting works as expected - @test point(2, 2) .- [point(2, 3), point(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] - @test point(2, 2, 2) .- [point(2, 3, 1), point(3, 1, 4)] == [vector(0.0, -1.0, 1.0), vector(-1.0, 1.0, -2.0)] + @test cart(2, 2) .- [cart(2, 3), cart(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] + @test cart(2, 2, 2) .- [cart(2, 3, 1), cart(3, 1, 4)] == [vector(0.0, -1.0, 1.0), vector(-1.0, 1.0, -2.0)] # angles between 2D points - @test ∠(point(0, 1), point(0, 0), point(1, 0)) ≈ T(-π / 2) - @test ∠(point(1, 0), point(0, 0), point(0, 1)) ≈ T(π / 2) - @test ∠(point(-1, 0), point(0, 0), point(0, 1)) ≈ T(-π / 2) - @test ∠(point(0, 1), point(0, 0), point(-1, 0)) ≈ T(π / 2) - @test ∠(point(0, -1), point(0, 0), point(1, 0)) ≈ T(π / 2) - @test ∠(point(1, 0), point(0, 0), point(0, -1)) ≈ T(-π / 2) - @test ∠(point(0, -1), point(0, 0), point(-1, 0)) ≈ T(-π / 2) - @test ∠(point(-1, 0), point(0, 0), point(0, -1)) ≈ T(π / 2) + @test ∠(cart(0, 1), cart(0, 0), cart(1, 0)) ≈ T(-π / 2) + @test ∠(cart(1, 0), cart(0, 0), cart(0, 1)) ≈ T(π / 2) + @test ∠(cart(-1, 0), cart(0, 0), cart(0, 1)) ≈ T(-π / 2) + @test ∠(cart(0, 1), cart(0, 0), cart(-1, 0)) ≈ T(π / 2) + @test ∠(cart(0, -1), cart(0, 0), cart(1, 0)) ≈ T(π / 2) + @test ∠(cart(1, 0), cart(0, 0), cart(0, -1)) ≈ T(-π / 2) + @test ∠(cart(0, -1), cart(0, 0), cart(-1, 0)) ≈ T(-π / 2) + @test ∠(cart(-1, 0), cart(0, 0), cart(0, -1)) ≈ T(π / 2) # angles between 3D points - @test ∠(point(1, 0, 0), point(0, 0, 0), point(0, 1, 0)) ≈ T(π / 2) - @test ∠(point(1, 0, 0), point(0, 0, 0), point(0, 0, 1)) ≈ T(π / 2) - @test ∠(point(0, 1, 0), point(0, 0, 0), point(1, 0, 0)) ≈ T(π / 2) - @test ∠(point(0, 1, 0), point(0, 0, 0), point(0, 0, 1)) ≈ T(π / 2) - @test ∠(point(0, 0, 1), point(0, 0, 0), point(1, 0, 0)) ≈ T(π / 2) - @test ∠(point(0, 0, 1), point(0, 0, 0), point(0, 1, 0)) ≈ T(π / 2) + @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) + @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) + @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) + @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) + @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) + @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) # a point pertains to itself - p = point(0, 0) - q = point(1, 1) + p = cart(0, 0) + q = cart(1, 1) @test p ∈ p @test q ∈ q @test p ∉ q @test q ∉ p - p = point(0, 0, 0) - q = point(1, 1, 1) + p = cart(0, 0, 0) + q = cart(1, 1, 1) @test p ∈ p @test q ∈ q @test p ∉ q @@ -170,7 +170,7 @@ @test datum(Meshes.crs(Point(c) + vector(1, 1))) === WGS84Latest @test datum(Meshes.crs(Point(c) - vector(1, 1))) === WGS84Latest - p = point(0, 1) + p = cart(0, 1) @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" if T === Float32 @test sprint(show, p) == "Point(x: 0.0f0 m, y: 1.0f0 m)" @@ -188,48 +188,48 @@ end @testset "Ray" begin - r = Ray(point(0, 0), vector(1, 1)) + r = Ray(cart(0, 0), vector(1, 1)) @test paramdim(r) == 1 @test Meshes.crs(r) <: Cartesian{NoDatum} @test Meshes.lentype(r) == ℳ @test measure(r) == typemax(ℳ) @test length(r) == typemax(ℳ) - @test boundary(r) == point(0, 0) + @test boundary(r) == cart(0, 0) @test perimeter(r) == zero(ℳ) - r = Ray(point(0, 0), vector(1, 1)) + r = Ray(cart(0, 0), vector(1, 1)) equaltest(r) isapproxtest(r) - r = Ray(point(0, 0), vector(1, 1)) - @test r(T(0.0)) == point(0, 0) - @test r(T(1.0)) == point(1, 1) - @test r(T(Inf)) == point(Inf, Inf) + r = Ray(cart(0, 0), vector(1, 1)) + @test r(T(0.0)) == cart(0, 0) + @test r(T(1.0)) == cart(1, 1) + @test r(T(Inf)) == cart(Inf, Inf) @test r(T(1.0)) - r(T(0.0)) == vector(1, 1) @test_throws DomainError(T(-1), "r(t) is not defined for t < 0.") r(T(-1)) - p₁ = point(3, 3, 3) - p₂ = point(-3, -3, -3) - p₃ = point(1, 0, 0) - r = Ray(point(0, 0, 0), vector(1, 1, 1)) + p₁ = cart(3, 3, 3) + p₂ = cart(-3, -3, -3) + p₃ = cart(1, 0, 0) + r = Ray(cart(0, 0, 0), vector(1, 1, 1)) @test p₁ ∈ r @test p₂ ∉ r @test p₃ ∉ r - r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(point(1, 1, 1), vector(1, 2, 1)) + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 1, 1), vector(1, 2, 1)) @test r1 != r2 - r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(point(1, 0, 0), vector(-1, 0, 0)) + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 0, 0), vector(-1, 0, 0)) @test r1 != r2 - r1 = Ray(point(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(point(1, 0, 0), vector(1, 0, 0)) + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 0, 0), vector(1, 0, 0)) @test r1 != r2 - r1 = Ray(point(0, 0, 0), vector(2, 0, 0)) - r2 = Ray(point(0, 0, 0), vector(1, 0, 0)) + r1 = Ray(cart(0, 0, 0), vector(2, 0, 0)) + r2 = Ray(cart(0, 0, 0), vector(1, 0, 0)) @test r1 == r2 r2 = rand(Ray{2}) @@ -239,7 +239,7 @@ @test embeddim(r2) == 2 @test embeddim(r3) == 3 - r = Ray(point(0, 0), vector(1, 1)) + r = Ray(cart(0, 0), vector(1, 1)) @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), r) == """ @@ -255,7 +255,7 @@ end @testset "Line" begin - l = Line(point(0, 0), point(1, 1)) + l = Line(cart(0, 0), cart(1, 1)) @test paramdim(l) == 1 @test Meshes.crs(l) <: Cartesian{NoDatum} @test Meshes.lentype(l) == ℳ @@ -264,12 +264,12 @@ @test isnothing(boundary(l)) @test perimeter(l) == zero(ℳ) - l = Line(point(0, 0), point(1, 1)) + l = Line(cart(0, 0), cart(1, 1)) equaltest(l) isapproxtest(l) - l = Line(point(0, 0), point(1, 1)) - @test (l(0), l(1)) == (point(0, 0), point(1, 1)) + l = Line(cart(0, 0), cart(1, 1)) + @test (l(0), l(1)) == (cart(0, 0), cart(1, 1)) l2 = rand(Line{2}) l3 = rand(Line{3}) @@ -278,7 +278,7 @@ @test embeddim(l2) == 2 @test embeddim(l3) == 3 - l = Line(point(0, 0), point(1, 1)) + l = Line(cart(0, 0), cart(1, 1)) @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), l) == """ @@ -294,48 +294,48 @@ end @testset "Plane" begin - p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - @test p(T(1), T(0)) == point(1, 0, 0) + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test p(T(1), T(0)) == cart(1, 0, 0) @test paramdim(p) == 2 @test embeddim(p) == 3 @test Meshes.crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test measure(p) == typemax(ℳ)^2 @test area(p) == typemax(ℳ)^2 - @test p(T(0), T(0)) == point(0, 0, 0) + @test p(T(0), T(0)) == cart(0, 0, 0) @test normal(p) == Vec(0, 0, 1) @test isnothing(boundary(p)) @test perimeter(p) == zero(ℳ) - p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) equaltest(p) isapproxtest(p) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) - @test p(T(1), T(0)) == point(1, 0, 0) - @test p(T(0), T(1)) == point(0, 1, 0) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test p(T(1), T(0)) == cart(1, 0, 0) + @test p(T(0), T(1)) == cart(0, 1, 0) - p₁ = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - p₂ = Plane(point(0, 0, 0), vector(0, 1, 0), vector(1, 0, 0)) + p₁ = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p₂ = Plane(cart(0, 0, 0), vector(0, 1, 0), vector(1, 0, 0)) @test p₁ ≈ p₂ - p₁ = Plane(point(0, 0, 0), vector(1, 1, 0)) - p₂ = Plane(point(0, 0, 0), -vector(1, 1, 0)) + p₁ = Plane(cart(0, 0, 0), vector(1, 1, 0)) + p₂ = Plane(cart(0, 0, 0), -vector(1, 1, 0)) @test p₁ ≈ p₂ # https://github.com/JuliaGeometry/Meshes.jl/issues/624 - p₁ = Plane(point(0, 0, 0), vector(0, 0, 1)) - p₂ = Plane(point(0, 0, 10), vector(0, 0, 1)) + p₁ = Plane(cart(0, 0, 0), vector(0, 0, 1)) + p₂ = Plane(cart(0, 0, 10), vector(0, 0, 1)) @test !(p₁ ≈ p₂) # normal to plane has norm one regardless of basis - p = Plane(point(0, 0, 0), vector(2, 0, 0), vector(0, 3, 0)) + p = Plane(cart(0, 0, 0), vector(2, 0, 0), vector(0, 3, 0)) n = normal(p) @test isapprox(norm(n), oneunit(ℳ), atol=atol(ℳ)) # plane passing through three points - p₁ = point(0, 0, 0) - p₂ = point(1, 2, 3) - p₃ = point(3, 2, 1) + p₁ = cart(0, 0, 0) + p₂ = cart(1, 2, 3) + p₃ = cart(3, 2, 1) p = Plane(p₁, p₂, p₃) @test p₁ ∈ p @test p₂ ∈ p @@ -345,7 +345,7 @@ @test p isa Plane @test embeddim(p) == 3 - p = Plane(point(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) @test sprint(show, p) == "Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, 0.0 m, 0.0 m), v: (0.0 m, 1.0 m, 0.0 m))" if T === Float32 @@ -364,33 +364,33 @@ end @testset "BezierCurve" begin - b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) @test embeddim(b) == 2 @test paramdim(b) == 1 @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - b = BezierCurve(point(0, 0), point(1, 1)) + b = BezierCurve(cart(0, 0), cart(1, 1)) equaltest(b) isapproxtest(b) - b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) for method in [DeCasteljau(), Horner()] - @test b(T(0), method) == point(0, 0) - @test b(T(1), method) == point(1, 0) - @test b(T(0.5), method) == point(0.5, 0.5) - @test b(T(0.5), method) == point(0.5, 0.5) + @test b(T(0), method) == cart(0, 0) + @test b(T(1), method) == cart(1, 0) + @test b(T(0.5), method) == cart(0.5, 0.5) + @test b(T(0.5), method) == cart(0.5, 0.5) @test_throws DomainError(T(-0.1), "b(t) is not defined for t outside [0, 1].") b(T(-0.1), method) @test_throws DomainError(T(1.2), "b(t) is not defined for t outside [0, 1].") b(T(1.2), method) end - @test boundary(b) == Multi([point(0, 0), point(1, 0)]) - b = BezierCurve(point(0, 0), point(1, 1)) - @test boundary(b) == Multi([point(0, 0), point(1, 1)]) + @test boundary(b) == Multi([cart(0, 0), cart(1, 0)]) + b = BezierCurve(cart(0, 0), cart(1, 1)) + @test boundary(b) == Multi([cart(0, 0), cart(1, 1)]) @test perimeter(b) == zero(ℳ) rng = StableRNG(123) - b = BezierCurve(point.(randn(rng, 100), randn(rng, 100))) + b = BezierCurve(cart.(randn(rng, 100), randn(rng, 100))) t1 = @timed b(T(0.2)) t2 = @timed b(T(0.2), Horner()) @test t1.time < 5e-4 @@ -411,7 +411,7 @@ b = BezierCurve(Point(c1), Point(c2), Point(c3)) @test datum(Meshes.crs(b(T(0), Horner()))) === WGS84Latest - b = BezierCurve(point(0, 0), point(0.5, 1), point(1, 0)) + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" if T === Float32 @test sprint(show, MIME("text/plain"), b) == """ @@ -425,94 +425,94 @@ end @testset "Box" begin - b = Box(point(0), point(1)) + b = Box(cart(0), cart(1)) @test embeddim(b) == 1 @test paramdim(b) == 1 @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - @test minimum(b) == point(0) - @test maximum(b) == point(1) - @test extrema(b) == (point(0), point(1)) + @test minimum(b) == cart(0) + @test maximum(b) == cart(1) + @test extrema(b) == (cart(0), cart(1)) - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) @test embeddim(b) == 2 @test paramdim(b) == 2 @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - @test minimum(b) == point(0, 0) - @test maximum(b) == point(1, 1) - @test extrema(b) == (point(0, 0), point(1, 1)) + @test minimum(b) == cart(0, 0) + @test maximum(b) == cart(1, 1) + @test extrema(b) == (cart(0, 0), cart(1, 1)) - b = Box(point(0, 0, 0), point(1, 1, 1)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) @test embeddim(b) == 3 @test paramdim(b) == 3 @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - @test minimum(b) == point(0, 0, 0) - @test maximum(b) == point(1, 1, 1) - @test extrema(b) == (point(0, 0, 0), point(1, 1, 1)) + @test minimum(b) == cart(0, 0, 0) + @test maximum(b) == cart(1, 1, 1) + @test extrema(b) == (cart(0, 0, 0), cart(1, 1, 1)) - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) equaltest(b) isapproxtest(b) - b = Box(point(0), point(1)) - @test boundary(b) == Multi([point(0), point(1)]) + b = Box(cart(0), cart(1)) + @test boundary(b) == Multi([cart(0), cart(1)]) @test measure(b) == T(1) * u"m" - @test point(0) ∈ b - @test point(1) ∈ b - @test point(0.5) ∈ b - @test point(-0.5) ∉ b - @test point(1.5) ∉ b + @test cart(0) ∈ b + @test cart(1) ∈ b + @test cart(0.5) ∈ b + @test cart(-0.5) ∉ b + @test cart(1.5) ∉ b - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) @test measure(b) == area(b) == T(1) * u"m^2" - @test point(1, 1) ∈ b + @test cart(1, 1) ∈ b @test perimeter(b) ≈ T(4) * u"m" - b = Box(point(1, 1), point(2, 2)) + b = Box(cart(1, 1), cart(2, 2)) @test sides(b) == (T(1) * u"m", T(1) * u"m") - @test Meshes.center(b) == point(1.5, 1.5) + @test Meshes.center(b) == cart(1.5, 1.5) @test diagonal(b) == √T(2) * u"m" - b = Box(point(1, 2), point(3, 4)) - v = point.([(1, 2), (3, 2), (3, 4), (1, 4)]) + b = Box(cart(1, 2), cart(3, 4)) + v = cart.([(1, 2), (3, 2), (3, 4), (1, 4)]) @test boundary(b) == Ring(v) - b = Box(point(1, 2, 3), point(4, 5, 6)) - v = point.([(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)]) + b = Box(cart(1, 2, 3), cart(4, 5, 6)) + v = cart.([(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)]) c = connect.([(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)]) @test boundary(b) == SimpleMesh(v, c) - b = Box(point(0, 0), point(1, 1)) - @test boundary(b) == Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + b = Box(cart(0, 0), cart(1, 1)) + @test boundary(b) == Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - b = Box(point(0, 0, 0), point(1, 1, 1)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) m = boundary(b) @test m isa Mesh @test nvertices(m) == 8 @test nelements(m) == 6 # subsetting with boxes - b1 = Box(point(0, 0), point(0.5, 0.5)) - b2 = Box(point(0.1, 0.1), point(0.5, 0.5)) - b3 = Box(point(0, 0), point(1, 1)) + b1 = Box(cart(0, 0), cart(0.5, 0.5)) + b2 = Box(cart(0.1, 0.1), cart(0.5, 0.5)) + b3 = Box(cart(0, 0), cart(1, 1)) @test b1 ⊆ b3 @test b2 ⊆ b3 @test !(b1 ⊆ b2) @test !(b3 ⊆ b1) @test !(b3 ⊆ b1) - b = Box(point(0, 0), point(10, 20)) - @test b(T(0.0), T(0.0)) == point(0, 0) - @test b(T(0.5), T(0.0)) == point(5, 0) - @test b(T(1.0), T(0.0)) == point(10, 0) - @test b(T(0.0), T(0.5)) == point(0, 10) - @test b(T(0.0), T(1.0)) == point(0, 20) + b = Box(cart(0, 0), cart(10, 20)) + @test b(T(0.0), T(0.0)) == cart(0, 0) + @test b(T(0.5), T(0.0)) == cart(5, 0) + @test b(T(1.0), T(0.0)) == cart(10, 0) + @test b(T(0.0), T(0.5)) == cart(0, 10) + @test b(T(0.0), T(1.0)) == cart(0, 20) - b = Box(point(0, 0, 0), point(10, 20, 30)) - @test b(T(0.0), T(0.0), T(0.0)) == point(0, 0, 0) - @test b(T(1.0), T(1.0), T(1.0)) == point(10, 20, 30) + b = Box(cart(0, 0, 0), cart(10, 20, 30)) + @test b(T(0.0), T(0.0), T(0.0)) == cart(0, 0, 0) + @test b(T(1.0), T(1.0), T(1.0)) == cart(10, 20, 30) b1 = rand(Box{1}) b2 = rand(Box{2}) @@ -524,27 +524,27 @@ @test embeddim(b2) == 2 @test embeddim(b3) == 3 - @test_throws AssertionError Box(point(1), point(0)) - @test_throws AssertionError Box(point(1, 1), point(0, 0)) - @test_throws AssertionError Box(point(1, 1, 1), point(0, 0, 0)) + @test_throws AssertionError Box(cart(1), cart(0)) + @test_throws AssertionError Box(cart(1, 1), cart(0, 0)) + @test_throws AssertionError Box(cart(1, 1, 1), cart(0, 0, 0)) - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) q = convert(Quadrangle, b) @test q isa Quadrangle - @test q == Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + @test q == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - b = Box(point(0, 0, 0), point(1, 1, 1)) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) h = convert(Hexahedron, b) @test h isa Hexahedron @test h == Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) # datum propagation @@ -553,7 +553,7 @@ b = Box(Point(c1), Point(c2)) @test datum(Meshes.crs(center(b))) === WGS84Latest - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" if T === Float32 @test sprint(show, MIME("text/plain"), b) == """ @@ -569,61 +569,61 @@ end @testset "Ball" begin - b = Ball(point(1, 2, 3), T(5)) + b = Ball(cart(1, 2, 3), T(5)) @test embeddim(b) == 3 @test paramdim(b) == 3 @test Meshes.crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - @test Meshes.center(b) == point(1, 2, 3) + @test Meshes.center(b) == cart(1, 2, 3) @test radius(b) == T(5) * u"m" - b = Ball(point(0, 0), T(1)) + b = Ball(cart(0, 0), T(1)) equaltest(b) isapproxtest(b) - b = Ball(point(1, 2, 3), 4) + b = Ball(cart(1, 2, 3), 4) @test Meshes.lentype(b) == ℳ - b1 = Ball(point(0, 0), T(1)) - b2 = Ball(point(0, 0)) + b1 = Ball(cart(0, 0), T(1)) + b2 = Ball(cart(0, 0)) b3 = Ball(T.((0, 0))) @test b1 == b2 == b3 - b = Ball(point(0, 0), T(2)) + b = Ball(cart(0, 0), T(2)) @test measure(b) ≈ T(π) * (T(2)^2) * u"m^2" - b = Ball(point(0, 0, 0), T(2)) + b = Ball(cart(0, 0, 0), T(2)) @test measure(b) ≈ T(4 / 3) * T(π) * (T(2)^3) * u"m^3" @test_throws ArgumentError length(b) @test_throws ArgumentError area(b) - b = Ball(point(0, 0), T(2)) - @test point(1, 0) ∈ b - @test point(0, 1) ∈ b - @test point(2, 0) ∈ b - @test point(0, 2) ∈ b - @test point(3, 5) ∉ b + b = Ball(cart(0, 0), T(2)) + @test cart(1, 0) ∈ b + @test cart(0, 1) ∈ b + @test cart(2, 0) ∈ b + @test cart(0, 2) ∈ b + @test cart(3, 5) ∉ b @test perimeter(b) ≈ T(4π) * u"m" - b = Ball(point(0, 0, 0), T(2)) - @test point(1, 0, 0) ∈ b - @test point(0, 0, 1) ∈ b - @test point(2, 0, 0) ∈ b - @test point(0, 0, 2) ∈ b - @test point(3, 5, 2) ∉ b + b = Ball(cart(0, 0, 0), T(2)) + @test cart(1, 0, 0) ∈ b + @test cart(0, 0, 1) ∈ b + @test cart(2, 0, 0) ∈ b + @test cart(0, 0, 2) ∈ b + @test cart(3, 5, 2) ∉ b - b = Ball(point(0, 0), T(2)) - @test b(T(0), T(0)) ≈ point(0, 0) - @test b(T(1), T(0)) ≈ point(2, 0) + b = Ball(cart(0, 0), T(2)) + @test b(T(0), T(0)) ≈ cart(0, 0) + @test b(T(1), T(0)) ≈ cart(2, 0) - b = Ball(point(7, 7), T(1.5)) + b = Ball(cart(7, 7), T(1.5)) ps = b.(1, rand(T, 100)) all(∈(b), ps) - b = Ball(point(0, 0, 0), T(2)) - @test b(T(0), T(0), T(0)) ≈ point(0, 0, 0) - @test b(T(1), T(0), T(0)) ≈ point(0, 0, 2) + b = Ball(cart(0, 0, 0), T(2)) + @test b(T(0), T(0), T(0)) ≈ cart(0, 0, 0) + @test b(T(1), T(0), T(0)) ≈ cart(0, 0, 2) - b = Ball(point(7, 7, 7), T(1.5)) + b = Ball(cart(7, 7, 7), T(1.5)) ps = b.(1, rand(T, 100), rand(T, 100)) all(∈(b), ps) @@ -637,7 +637,7 @@ @test embeddim(b2) == 2 @test embeddim(b3) == 3 - b = Ball(point(0, 0), T(1)) + b = Ball(cart(0, 0), T(1)) @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), b) == """ @@ -653,88 +653,88 @@ end @testset "Sphere" begin - s = Sphere(point(0, 0, 0), T(1)) + s = Sphere(cart(0, 0, 0), T(1)) @test embeddim(s) == 3 @test paramdim(s) == 2 @test Meshes.crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ - @test Meshes.center(s) == point(0, 0, 0) + @test Meshes.center(s) == cart(0, 0, 0) @test radius(s) == T(1) * u"m" - @test extrema(s) == (point(-1, -1, -1), point(1, 1, 1)) + @test extrema(s) == (cart(-1, -1, -1), cart(1, 1, 1)) @test isnothing(boundary(s)) @test perimeter(s) == zero(ℳ) - s = Sphere(point(0, 0), T(1)) + s = Sphere(cart(0, 0), T(1)) equaltest(s) isapproxtest(s) - s = Sphere(point(1, 2, 3), 4) + s = Sphere(cart(1, 2, 3), 4) @test Meshes.lentype(s) == ℳ - s = Sphere(point(0, 0), T(1)) + s = Sphere(cart(0, 0), T(1)) @test embeddim(s) == 2 @test paramdim(s) == 1 @test Meshes.lentype(s) == ℳ - @test Meshes.center(s) == point(0, 0) + @test Meshes.center(s) == cart(0, 0) @test radius(s) == T(1) * u"m" - @test extrema(s) == (point(-1, -1), point(1, 1)) + @test extrema(s) == (cart(-1, -1), cart(1, 1)) @test isnothing(boundary(s)) - s1 = Sphere(point(0, 0), T(1)) - s2 = Sphere(point(0, 0)) + s1 = Sphere(cart(0, 0), T(1)) + s2 = Sphere(cart(0, 0)) s3 = Sphere(T.((0, 0))) @test s1 == s2 == s3 - s = Sphere(point(0, 0), T(2)) + s = Sphere(cart(0, 0), T(2)) @test measure(s) ≈ T(2π) * 2 * u"m" @test length(s) ≈ T(2π) * 2 * u"m" - @test extrema(s) == (point(-2, -2), point(2, 2)) - s = Sphere(point(0, 0, 0), T(2)) + @test extrema(s) == (cart(-2, -2), cart(2, 2)) + s = Sphere(cart(0, 0, 0), T(2)) @test measure(s) ≈ T(4π) * (2^2) * u"m^2" @test area(s) ≈ T(4π) * (2^2) * u"m^2" - s = Sphere(point(0, 0), T(2)) - @test point(1, 0) ∉ s - @test point(0, 1) ∉ s - @test point(2, 0) ∈ s - @test point(0, 2) ∈ s - @test point(3, 5) ∉ s + s = Sphere(cart(0, 0), T(2)) + @test cart(1, 0) ∉ s + @test cart(0, 1) ∉ s + @test cart(2, 0) ∈ s + @test cart(0, 2) ∈ s + @test cart(3, 5) ∉ s - s = Sphere(point(0, 0, 0), T(2)) - @test point(1, 0, 0) ∉ s - @test point(0, 0, 1) ∉ s - @test point(2, 0, 0) ∈ s - @test point(0, 0, 2) ∈ s - @test point(3, 5, 2) ∉ s + s = Sphere(cart(0, 0, 0), T(2)) + @test cart(1, 0, 0) ∉ s + @test cart(0, 0, 1) ∉ s + @test cart(2, 0, 0) ∈ s + @test cart(0, 0, 2) ∈ s + @test cart(3, 5, 2) ∉ s # 2D sphere passing through 3 points - s = Sphere(point(0, 0), point(0.5, 0), point(1, 1)) - @test Meshes.center(s) == point(0.25, 0.75) + s = Sphere(cart(0, 0), cart(0.5, 0), cart(1, 1)) + @test Meshes.center(s) == cart(0.25, 0.75) @test radius(s) == T(0.7905694150420949) * u"m" - s = Sphere(point(0, 0), point(1, 0), point(0, 1)) - @test Meshes.center(s) == point(0.5, 0.5) + s = Sphere(cart(0, 0), cart(1, 0), cart(0, 1)) + @test Meshes.center(s) == cart(0.5, 0.5) @test radius(s) == T(0.7071067811865476) * u"m" - s = Sphere(point(0, 0), point(1, 0), point(1, 1)) - @test Meshes.center(s) == point(0.5, 0.5) + s = Sphere(cart(0, 0), cart(1, 0), cart(1, 1)) + @test Meshes.center(s) == cart(0.5, 0.5) @test radius(s) == T(0.7071067811865476) * u"m" # 3D sphere passing through 4 points - s = Sphere(point(0, 0, 0), point(5, 0, 1), point(1, 1, 1), point(3, 2, 1)) - @test point(0, 0, 0) ∈ s - @test point(5, 0, 1) ∈ s - @test point(1, 1, 1) ∈ s - @test point(3, 2, 1) ∈ s + s = Sphere(cart(0, 0, 0), cart(5, 0, 1), cart(1, 1, 1), cart(3, 2, 1)) + @test cart(0, 0, 0) ∈ s + @test cart(5, 0, 1) ∈ s + @test cart(1, 1, 1) ∈ s + @test cart(3, 2, 1) ∈ s O = Meshes.center(s) r = radius(s) - @test isapprox(r, norm(point(0, 0, 0) - O)) + @test isapprox(r, norm(cart(0, 0, 0) - O)) - s = Sphere(point(0, 0), T(2)) - @test s(T(0)) ≈ point(2, 0) - @test s(T(0.5)) ≈ point(-2, 0) + s = Sphere(cart(0, 0), T(2)) + @test s(T(0)) ≈ cart(2, 0) + @test s(T(0.5)) ≈ cart(-2, 0) - s = Sphere(point(0, 0, 0), T(2)) - @test s(T(0), T(0)) ≈ point(0, 0, 2) - @test s(T(0.5), T(0.5)) ≈ point(-2, 0, 0) + s = Sphere(cart(0, 0, 0), T(2)) + @test s(T(0), T(0)) ≈ cart(0, 0, 2) + @test s(T(0.5), T(0.5)) ≈ cart(-2, 0, 0) s1 = rand(Sphere{1}) s2 = rand(Sphere{2}) @@ -746,7 +746,7 @@ @test embeddim(s2) == 2 @test embeddim(s3) == 3 - s = Sphere(point(0, 0, 0), T(1)) + s = Sphere(cart(0, 0, 0), T(1)) @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), s) == """ @@ -768,7 +768,7 @@ @test Meshes.crs(e) <: Cartesian{NoDatum} @test Meshes.lentype(e) == ℳ @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") - @test center(e) == point(0, 0, 0) + @test center(e) == cart(0, 0, 0) @test isnothing(boundary(e)) @test perimeter(e) == zero(ℳ) @@ -795,23 +795,23 @@ end @testset "Disk" begin - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @test embeddim(d) == 3 @test paramdim(d) == 2 @test Meshes.crs(d) <: Cartesian{NoDatum} @test Meshes.lentype(d) == ℳ @test plane(d) == p - @test Meshes.center(d) == point(0, 0, 0) + @test Meshes.center(d) == cart(0, 0, 0) @test radius(d) == T(2) * u"m" @test normal(d) == vector(0, 0, 1) @test measure(d) == T(π) * T(2)^2 * u"m^2" @test area(d) == measure(d) - @test point(0, 0, 0) ∈ d - @test point(0, 0, 1) ∉ d + @test cart(0, 0, 0) ∈ d + @test cart(0, 0, 1) ∉ d @test boundary(d) == Circle(p, T(2)) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) equaltest(d) isapproxtest(d) @@ -820,7 +820,7 @@ @test d isa Disk @test embeddim(d) == 3 - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @test sprint(show, d) == "Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" @@ -838,44 +838,44 @@ end @testset "Circle" begin - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @test embeddim(c) == 3 @test paramdim(c) == 1 @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test plane(c) == p - @test Meshes.center(c) == point(0, 0, 0) + @test Meshes.center(c) == cart(0, 0, 0) @test radius(c) == T(2) * u"m" @test measure(c) == 2 * T(π) * T(2) * u"m" @test length(c) == measure(c) - @test point(2, 0, 0) ∈ c - @test point(0, 2, 0) ∈ c - @test point(0, 0, 0) ∉ c + @test cart(2, 0, 0) ∈ c + @test cart(0, 2, 0) ∈ c + @test cart(0, 0, 0) ∉ c @test isnothing(boundary(c)) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) equaltest(c) isapproxtest(c) # 3D circumcircle - p1 = point(0, 4, 0) - p2 = point(0, -4, 0) - p3 = point(0, 0, 4) + p1 = cart(0, 4, 0) + p2 = cart(0, -4, 0) + p3 = cart(0, 0, 4) c = Circle(p1, p2, p3) @test p1 ∈ c @test p2 ∈ c @test p3 ∈ c # circle parametrization - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) - @test c(T(0)) ≈ point(2, 0, 0) - @test c(T(0.25)) ≈ point(0, 2, 0) - @test c(T(0.5)) ≈ point(-2, 0, 0) - @test c(T(0.75)) ≈ point(0, -2, 0) - @test c(T(1)) ≈ point(2, 0, 0) + @test c(T(0)) ≈ cart(2, 0, 0) + @test c(T(0.25)) ≈ cart(0, 2, 0) + @test c(T(0.5)) ≈ cart(-2, 0, 0) + @test c(T(0.75)) ≈ cart(0, -2, 0) + @test c(T(1)) ≈ cart(2, 0, 0) c = rand(Circle) @test c isa Circle @@ -888,7 +888,7 @@ c = Circle(Point(c1), Point(c2), Point(c3)) @test datum(Meshes.crs(c)) === WGS84Latest - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @test sprint(show, c) == "Circle(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" @@ -906,21 +906,21 @@ end @testset "Cylinder" begin - c = Cylinder(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) + c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) @test embeddim(c) == 3 @test paramdim(c) == 3 @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(5) * u"m" - @test bottom(c) == Plane(point(1, 2, 3), vector(0, 0, 1)) - @test top(c) == Plane(point(4, 5, 6), vector(0, 0, 1)) - @test axis(c) == Line(point(1, 2, 3), point(4, 5, 6)) + @test bottom(c) == Plane(cart(1, 2, 3), vector(0, 0, 1)) + @test top(c) == Plane(cart(4, 5, 6), vector(0, 0, 1)) + @test axis(c) == Line(cart(1, 2, 3), cart(4, 5, 6)) @test !isright(c) @test measure(c) == volume(c) ≈ T(5)^2 * pi * T(3) * sqrt(T(3)) * u"m^3" - @test point(1, 2, 3) ∈ c - @test point(4, 5, 6) ∈ c - @test point(0.99, 1.99, 2.99) ∉ c - @test point(4.01, 5.01, 6.01) ∉ c + @test cart(1, 2, 3) ∈ c + @test cart(4, 5, 6) ∈ c + @test cart(0.99, 1.99, 2.99) ∉ c + @test cart(4.01, 5.01, 6.01) ∉ c @test !Meshes.hasintersectingplanes(c) @test c(0, 0, 0) ≈ bottom(c)(0, 0) @test c(0, 0, 1) ≈ top(c)(0, 0) @@ -931,11 +931,11 @@ equaltest(c) isapproxtest(c) - c = Cylinder(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) + c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) - c1 = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) - c2 = Cylinder(point(0, 0, 0), point(0, 0, 1)) + c1 = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) + c2 = Cylinder(cart(0, 0, 0), cart(0, 0, 1)) c3 = Cylinder(T(1)) @test c1 == c2 == c3 @test c1 ≈ c2 ≈ c3 @@ -945,30 +945,30 @@ c = Cylinder(1) @test Meshes.lentype(c) == Meshes.Met{Float64} - c = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) + c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) @test radius(c) == T(1) * u"m" - @test bottom(c) == Plane(point(0, 0, 0), vector(0, 0, 1)) - @test top(c) == Plane(point(0, 0, 1), vector(0, 0, 1)) - @test center(c) == point(0.0, 0.0, 0.5) - @test centroid(c) == point(0.0, 0.0, 0.5) - @test axis(c) == Line(point(0, 0, 0), point(0, 0, 1)) + @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test center(c) == cart(0.0, 0.0, 0.5) + @test centroid(c) == cart(0.0, 0.0, 0.5) + @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) @test isright(c) - @test boundary(c) == CylinderSurface(point(0, 0, 0), point(0, 0, 1), T(1)) + @test boundary(c) == CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) @test measure(c) == volume(c) ≈ T(π) * u"m^3" - @test point(0, 0, 0) ∈ c - @test point(0, 0, 1) ∈ c - @test point(1, 0, 0) ∈ c - @test point(0, 1, 0) ∈ c - @test point(cosd(60), sind(60), 0.5) ∈ c - @test point(0, 0, -0.001) ∉ c - @test point(0, 0, 1.001) ∉ c - @test point(1, 1, 1) ∉ c + @test cart(0, 0, 0) ∈ c + @test cart(0, 0, 1) ∈ c + @test cart(1, 0, 0) ∈ c + @test cart(0, 1, 0) ∈ c + @test cart(cosd(60), sind(60), 0.5) ∈ c + @test cart(0, 0, -0.001) ∉ c + @test cart(0, 0, 1.001) ∉ c + @test cart(1, 1, 1) ∉ c c = rand(Cylinder) @test c isa Cylinder @test embeddim(c) == 3 - c = Cylinder(point(0, 0, 0), point(0, 0, 1), T(1)) + c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) @test sprint(show, c) == "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" if T === Float32 @@ -993,11 +993,11 @@ @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(2) * u"m" - @test bottom(c) == Plane(point(0, 0, 0), vector(0, 0, 1)) - @test top(c) == Plane(point(0, 0, 1), vector(0, 0, 1)) - @test center(c) == point(0.0, 0.0, 0.5) - @test centroid(c) == point(0.0, 0.0, 0.5) - @test axis(c) == Line(point(0, 0, 0), point(0, 0, 1)) + @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test center(c) == cart(0.0, 0.0, 0.5) + @test centroid(c) == cart(0.0, 0.0, 0.5) + @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) @test isright(c) @test isnothing(boundary(c)) @test measure(c) == area(c) ≈ (2 * T(2)^2 * pi + 2 * T(2) * pi) * u"m^2" @@ -1007,23 +1007,23 @@ equaltest(c) isapproxtest(c) - c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(1, 0, 1)), T(5)) + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) @test Meshes.hasintersectingplanes(c) - c1 = CylinderSurface(point(0, 0, 0), point(0, 0, 1), T(1)) - c2 = CylinderSurface(point(0, 0, 0), point(0, 0, 1)) + c1 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) + c2 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1)) c3 = CylinderSurface(T(1)) @test c1 == c2 == c3 @test c1 ≈ c2 ≈ c3 - c = CylinderSurface(Plane(point(1, 2, 3), vector(0, 0, 1)), Plane(point(4, 5, 6), vector(0, 0, 1)), T(5)) + c = CylinderSurface(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) @test measure(c) == area(c) ≈ (2 * T(5)^2 * pi + 2 * T(5) * pi * sqrt(3 * T(3)^2)) * u"m^2" c = CylinderSurface(T(1)) - @test c(T(0), T(0)) ≈ point(1, 0, 0) - @test c(T(0.5), T(0)) ≈ point(-1, 0, 0) - @test c(T(0), T(1)) ≈ point(1, 0, 1) - @test c(T(0.5), T(1)) ≈ point(-1, 0, 1) + @test c(T(0), T(0)) ≈ cart(1, 0, 0) + @test c(T(0.5), T(0)) ≈ cart(-1, 0, 0) + @test c(T(0), T(1)) ≈ cart(1, 0, 1) + @test c(T(0.5), T(1)) ≈ cart(-1, 0, 1) c = CylinderSurface(1.0) @test Meshes.lentype(c) == Meshes.Met{Float64} @@ -1061,24 +1061,24 @@ end @testset "ParaboloidSurface" begin - p = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) @test embeddim(p) == 3 @test paramdim(p) == 2 @test Meshes.crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test focallength(p) == T(2) * u"m" @test radius(p) == T(1) * u"m" - @test axis(p) == Line(point(0, 0, 0), point(0, 0, T(2))) + @test axis(p) == Line(cart(0, 0, 0), cart(0, 0, T(2))) @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" - @test centroid(p) == point(0, 0, 1 / 16) + @test centroid(p) == cart(0, 0, 1 / 16) - p = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) equaltest(p) isapproxtest(p) - p1 = ParaboloidSurface(point(1, 2, 3), T(1), T(1)) - p2 = ParaboloidSurface(point(1, 2, 3), T(1)) - p3 = ParaboloidSurface(point(1, 2, 3)) + p1 = ParaboloidSurface(cart(1, 2, 3), T(1), T(1)) + p2 = ParaboloidSurface(cart(1, 2, 3), T(1)) + p3 = ParaboloidSurface(cart(1, 2, 3)) @test p1 == p2 == p3 @test p1 ≈ p2 ≈ p3 @@ -1093,10 +1093,10 @@ @test radius(p) == 4.0 * u"m" @test focallength(p) == 5.0 * u"m" - p = ParaboloidSurface(point(1, 5, 2), T(3), T(4)) + p = ParaboloidSurface(cart(1, 5, 2), T(3), T(4)) @test measure(p) == area(p) ≈ T(128π / 3 * (73√73 / 512 - 1)) * u"m^2" - @test p(T(0), T(0)) ≈ point(1, 5, 2) - @test p(T(1), T(0)) ≈ point(4, 5, 2 + 3^2 / (4 * 4)) + @test p(T(0), T(0)) ≈ cart(1, 5, 2) + @test p(T(1), T(0)) ≈ cart(4, 5, 2 + 3^2 / (4 * 4)) @test_throws DomainError p(T(-0.1), T(0)) @test_throws DomainError p(T(1.1), T(0)) @@ -1116,7 +1116,7 @@ @test p isa ParaboloidSurface @test embeddim(p) == 3 - p = ParaboloidSurface(point(0, 0, 0), T(1), T(1)) + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(1)) @test sprint(show, p) == "ParaboloidSurface(apex: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m, focallength: 1.0 m)" if T === Float32 @@ -1135,9 +1135,9 @@ end @testset "Cone" begin - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 @@ -1145,7 +1145,7 @@ @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) a = T.((0, 0, 1)) c = Cone(d, a) @@ -1154,9 +1154,9 @@ @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) c = Cone(d, a) equaltest(c) isapproxtest(c) @@ -1165,9 +1165,9 @@ @test c isa Cone @test embeddim(c) == 3 - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) c = Cone(d, a) @test sprint(show, c) == "Cone(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -1186,36 +1186,36 @@ # cone: apex at (5,4,3); base center at (5,1,3) # halfangle: 30° -> radius: sqrt(3) # axis of the cone is parallel to y axis - p = Plane(point(5, 1, 3), vector(0, 1, 0)) + p = Plane(cart(5, 1, 3), vector(0, 1, 0)) d = Disk(p, sqrt(T(3))) - a = point(5, 4, 3) + a = cart(5, 4, 3) c = Cone(d, a) @test rad2deg(Meshes.halfangle(c)) ≈ T(30) @test Meshes.height(c) ≈ T(3) * u"m" - @test point(5, 1, 3) ∈ c - @test point(5, 4, 3) ∈ c - @test point(5, 1, 3 - sqrt(3)) ∈ c - @test point(5, 1, 3 + sqrt(3)) ∈ c - @test point(5 - sqrt(3), 1, 3) ∈ c - @test point(5 + sqrt(3), 1, 3) ∈ c - @test point(5, 2.5, 3) ∈ c - @test point(5 + sqrt(3) / 2, 2.5, 3) ∈ c - @test point(5 - sqrt(3) / 2, 2.5, 3) ∈ c - - @test point(5, 0.9, 3) ∉ c - @test point(5, 4.1, 3) ∉ c - @test point(5, 1, 1) ∉ c - @test point(5 + sqrt(3) + 0.01, 1, 3) ∉ c - @test point(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c - @test point(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c + @test cart(5, 1, 3) ∈ c + @test cart(5, 4, 3) ∈ c + @test cart(5, 1, 3 - sqrt(3)) ∈ c + @test cart(5, 1, 3 + sqrt(3)) ∈ c + @test cart(5 - sqrt(3), 1, 3) ∈ c + @test cart(5 + sqrt(3), 1, 3) ∈ c + @test cart(5, 2.5, 3) ∈ c + @test cart(5 + sqrt(3) / 2, 2.5, 3) ∈ c + @test cart(5 - sqrt(3) / 2, 2.5, 3) ∈ c + + @test cart(5, 0.9, 3) ∉ c + @test cart(5, 4.1, 3) ∉ c + @test cart(5, 1, 1) ∉ c + @test cart(5 + sqrt(3) + 0.01, 1, 3) ∉ c + @test cart(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c + @test cart(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c end @testset "ConeSurface" begin - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) s = ConeSurface(d, a) @test embeddim(s) == 3 @test paramdim(s) == 2 @@ -1223,7 +1223,7 @@ @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) a = T.((0, 0, 1)) c = ConeSurface(d, a) @@ -1232,9 +1232,9 @@ @test Meshes.crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) c = ConeSurface(d, a) equaltest(c) isapproxtest(c) @@ -1243,9 +1243,9 @@ @test c isa ConeSurface @test embeddim(c) == 3 - p = Plane(point(0, 0, 0), vector(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = point(0, 0, 1) + a = cart(0, 0, 1) s = ConeSurface(d, a) @test sprint(show, s) == "ConeSurface(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -1263,9 +1263,9 @@ end @testset "Frustum" begin - pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = Frustum(db, dt) @test embeddim(f) == 3 @@ -1275,9 +1275,9 @@ @test_throws AssertionError Frustum(db, db) - pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = Frustum(db, dt) equaltest(f) @@ -1287,36 +1287,36 @@ @test f isa Frustum f = Frustum(db, dt) - @test point(0, 0, 0) ∈ f - @test point(0, 0, 10) ∈ f - @test point(1, 0, 0) ∈ f - @test point(2, 0, 10) ∈ f - @test point(1, 0, 5) ∈ f + @test cart(0, 0, 0) ∈ f + @test cart(0, 0, 10) ∈ f + @test cart(1, 0, 0) ∈ f + @test cart(2, 0, 10) ∈ f + @test cart(1, 0, 5) ∈ f - @test point(1, 1, 0) ∉ f - @test point(2, 2, 10) ∉ f - @test point(0, 0, -0.01) ∉ f - @test point(0, 0, 10.01) ∉ f + @test cart(1, 1, 0) ∉ f + @test cart(2, 2, 10) ∉ f + @test cart(0, 0, -0.01) ∉ f + @test cart(0, 0, 10.01) ∉ f # reverse order, when top is larger than bottom # the frustum is the same geometry f = Frustum(dt, db) - @test point(0, 0, 0) ∈ f - @test point(0, 0, 10) ∈ f - @test point(1, 0, 0) ∈ f - @test point(2, 0, 10) ∈ f - @test point(1, 0, 5) ∈ f - - @test point(1, 1, 0) ∉ f - @test point(2, 2, 10) ∉ f - @test point(0, 0, -0.01) ∉ f - @test point(0, 0, 10.01) ∉ f + @test cart(0, 0, 0) ∈ f + @test cart(0, 0, 10) ∈ f + @test cart(1, 0, 0) ∈ f + @test cart(2, 0, 10) ∈ f + @test cart(1, 0, 5) ∈ f + + @test cart(1, 1, 0) ∉ f + @test cart(2, 2, 10) ∉ f + @test cart(0, 0, -0.01) ∉ f + @test cart(0, 0, 10.01) ∉ f end @testset "FrustumSurface" begin - pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = FrustumSurface(db, dt) @test embeddim(f) == 3 @@ -1327,9 +1327,9 @@ @test_throws AssertionError FrustumSurface(db, db) - pb = Plane(point(0, 0, 0), vector(0, 0, 1)) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) db = Disk(pb, T(1)) - pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) dt = Disk(pt, T(2)) f = FrustumSurface(db, dt) equaltest(f) @@ -1341,27 +1341,27 @@ @testset "Torus" begin t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) - @test point(1, 1, -1) ∈ t - @test point(1, 1, 1) ∉ t + @test cart(1, 1, -1) ∈ t + @test cart(1, 1, 1) ∉ t @test paramdim(t) == 2 @test Meshes.crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ - @test Meshes.center(t) == point(1, 1, 1) + @test Meshes.center(t) == cart(1, 1, 1) @test normal(t) == vector(1, 0, 0) @test radii(t) == (T(2) * u"m", T(1) * u"m") - @test axis(t) == Line(point(1, 1, 1), point(2, 1, 1)) + @test axis(t) == Line(cart(1, 1, 1), cart(2, 1, 1)) @test measure(t) ≈ 8 * T(π)^2 * u"m^2" @test_throws ArgumentError length(t) @test_throws ArgumentError volume(t) - t = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) equaltest(t) isapproxtest(t) # torus passing through three points - p₁ = point(0, 0, 0) - p₂ = point(1, 2, 3) - p₃ = point(3, 2, 1) + p₁ = cart(0, 0, 0) + p₂ = cart(1, 2, 3) + p₃ = cart(3, 2, 1) t = Torus(p₁, p₂, p₃, T(1)) c = center(t) R, r = radii(t) @@ -1386,7 +1386,7 @@ @test Meshes.lentype(t) == Meshes.Met{Float64} @test isnothing(boundary(t)) - t = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) @test sprint(show, t) == "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" if T === Float32 diff --git a/test/refinement.jl b/test/refinement.jl index 6d68909e9..3878e9141 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -20,7 +20,7 @@ end @testset "QuadRefinement" begin - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, QuadRefinement()) @@ -46,8 +46,8 @@ @testset "RegularRefinement" begin # 2D grids - grid = CartesianGrid(point(0, 0), point(10, 10), dims=(10, 10)) - tgrid = CartesianGrid(point(0, 0), point(10, 10), dims=(20, 20)) + grid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(10, 10)) + tgrid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(20, 20)) @test refine(grid, RegularRefinement(2)) == tgrid rgrid = convert(RectilinearGrid, grid) trgrid = convert(RectilinearGrid, tgrid) @@ -69,7 +69,7 @@ end @testset "CatmullClark" begin - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -84,7 +84,7 @@ @test_reference "data/catmullclark-1-$T.png" fig end - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -99,7 +99,7 @@ @test_reference "data/catmullclark-2-$T.png" fig end - points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) connec = connect.([(1, 4, 3, 2), (5, 6, 7, 8), (1, 2, 6, 5), (3, 4, 8, 7), (1, 5, 8, 4), (2, 3, 7, 6)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, CatmullClark()) @@ -124,7 +124,7 @@ end @testset "TriSubdivision" begin - points = point.([(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)]) + points = cart.([(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)]) connec = connect.([(1, 2, 3), (3, 2, 4), (4, 2, 1), (1, 3, 4)]) mesh = SimpleMesh(points, connec) ref1 = refine(mesh, TriSubdivision()) diff --git a/test/runtests.jl b/test/runtests.jl index e88e286b0..076866b40 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,7 +31,7 @@ include("dummy.jl") # helper functions include("testutils.jl") -point(args...) = point(T, args...) +cart(args...) = cart(T, args...) vector(args...) = vector(T, args...) diff --git a/test/sampling.jl b/test/sampling.jl index d0b129f94..79ef7eb6d 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -69,16 +69,16 @@ end @testset "RegularSampling" begin - b = Box(point(0, 0), point(2, 2)) + b = Box(cart(0, 0), cart(2, 2)) ps = sample(b, RegularSampling(3)) - @test collect(ps) == point.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]) + @test collect(ps) == cart.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]) ps = sample(b, RegularSampling(2, 3)) - @test collect(ps) == point.([(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)]) + @test collect(ps) == cart.([(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)]) - b = BezierCurve([point(0, 0), point(1, 0), point(1, 1)]) + b = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) ps = sample(b, RegularSampling(4)) ts = - point.([ + cart.([ (0.0, 0.0), (0.5555555555555556, 0.1111111111111111), (0.8888888888888888, 0.4444444444444444), @@ -88,17 +88,17 @@ @test p ≈ t end - s = Sphere(point(0, 0), T(2)) + s = Sphere(cart(0, 0), T(2)) ps = sample(s, RegularSampling(4)) - ts = point.([(2, 0), (0, 2), (-2, 0), (0, -2)]) + ts = cart.([(2, 0), (0, 2), (-2, 0), (0, -2)]) for (p, t) in zip(ps, ts) @test p ≈ t end - s = Sphere(point(0, 0, 0), T(2)) + s = Sphere(cart(0, 0, 0), T(2)) ps = sample(s, RegularSampling(2, 2)) ts = - point.([ + cart.([ (1.7320508075688772, 0.0, 1.0), (1.7320508075688772, 0.0, -1.0), (-1.7320508075688772, 0.0, 1.0), @@ -110,10 +110,10 @@ @test p ≈ t end - e = Ellipsoid((T(3), T(2), T(1)), point(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) + e = Ellipsoid((T(3), T(2), T(1)), cart(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) ps = sample(e, RegularSampling(2, 2)) ts = - point.([ + cart.([ (2.725814800973295, 2.225814800973295, -0.5871173070873834), (1.872261410380021, 2.372261410380021, -1.0871173070873832), (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), @@ -125,11 +125,11 @@ @test p ≈ t end - b = Ball(point(0, 0), T(2)) + b = Ball(cart(0, 0), T(2)) ps = sample(b, RegularSampling(3, 4)) @test all(∈(b), ps) ts = - point.([ + cart.([ (0.6666666666666666, 0.0), (1.3333333333333333, 0.0), (2.0, 0.0), @@ -148,11 +148,11 @@ @test p ≈ t end - b = Ball(point(10, 10), T(2)) + b = Ball(cart(10, 10), T(2)) ps = sample(b, RegularSampling(4, 3)) @test all(∈(b), ps) ts = - point.([ + cart.([ (10.5, 10.0), (11.0, 10.0), (11.5, 10.0), @@ -171,11 +171,11 @@ @test p ≈ t end - b = Ball(point(0, 0, 0), T(2)) + b = Ball(cart(0, 0, 0), T(2)) ps = sample(b, RegularSampling(3, 2, 3)) @test all(∈(b), ps) ts = - point.([ + cart.([ (0.5773502691896257, 0.0, 0.3333333333333333), (1.1547005383792515, 0.0, 0.6666666666666666), (1.7320508075688772, 0.0, 1.0), @@ -200,12 +200,12 @@ @test p ≈ t end - b = Ball(point(10, 10, 10), T(2)) + b = Ball(cart(10, 10, 10), T(2)) ps = sample(b, RegularSampling(3, 2, 3)) @test all(∈(b), ps) # cylinder surface with parallel planes - c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(0, 0, 1), vector(0, 0, 1)), T(1)) + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = to.(ps) xs = getindex.(cs, 1) @@ -217,7 +217,7 @@ @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) # cylinder surface with parallel shifted planes - c = CylinderSurface(Plane(point(0, 0, 0), vector(0, 0, 1)), Plane(point(1, 1, 1), vector(0, 0, 1)), T(1)) + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = to.(ps) xs = getindex.(cs, 1) @@ -226,39 +226,39 @@ @test length(cs) == 200 + 2 # cylinder surface with non-parallel planes - c = CylinderSurface(Plane(point(0, 0, 0), vector(1, 0, 1)), Plane(point(1, 1, 1), vector(0, 1, 1)), T(1)) + c = CylinderSurface(Plane(cart(0, 0, 0), vector(1, 0, 1)), Plane(cart(1, 1, 1), vector(0, 1, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) cs = to.(ps) @test length(cs) == 200 + 2 - s = Segment(point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) ps = sample(s, RegularSampling(2)) - @test collect(ps) == point.([(0, 0), (1, 1)]) + @test collect(ps) == cart.([(0, 0), (1, 1)]) ps = sample(s, RegularSampling(3)) - @test collect(ps) == point.([(0, 0), (0.5, 0.5), (1, 1)]) + @test collect(ps) == cart.([(0, 0), (0.5, 0.5), (1, 1)]) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) ps = sample(q, RegularSampling(2, 2)) - @test collect(ps) == point.([(0, 0), (1, 0), (0, 1), (1, 1)]) + @test collect(ps) == cart.([(0, 0), (1, 0), (0, 1), (1, 1)]) ps = sample(q, RegularSampling(3, 3)) - @test collect(ps) == point.([(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)]) + @test collect(ps) == cart.([(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)]) h = Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) ps = sample(h, RegularSampling(2, 2, 2)) @test collect(ps) == - point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) + cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) ps = sample(h, RegularSampling(3, 2, 2)) @test collect(ps) == - point.([ + cart.([ (0, 0, 0), (0.5, 0, 0), (1, 0, 0), @@ -273,10 +273,10 @@ (1, 1, 1) ]) - torus = Torus(point(0, 0, 0), vector(1, 0, 0), T(2), T(1)) + torus = Torus(cart(0, 0, 0), vector(1, 0, 0), T(2), T(1)) ps = sample(torus, RegularSampling(3, 3)) ts = - point.([ + cart.([ (0, 0, -3), (-sqrt(3) / 2, 0, -1.5), (sqrt(3) / 2, 0, -1.5), @@ -297,24 +297,24 @@ end @testset "HomogeneousSampling" begin - s = Segment(point(0, 0), point(1, 0)) + s = Segment(cart(0, 0), cart(1, 0)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in to.(ps)) @test all(coords[2] == zero(ℳ) for coords in to.(ps)) - s = Segment(point(0, 0), point(0, 1)) + s = Segment(cart(0, 0), cart(0, 1)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(coords[1] == zero(ℳ) for coords in to.(ps)) @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) - s = Segment(point(0, 0), point(1, 1)) + s = Segment(cart(0, 0), cart(1, 1)) ps = sample(s, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) - c = Rope(point(0, 0), point(1, 0), point(0, 1), point(1, 1)) + c = Rope(cart(0, 0), cart(1, 0), cart(0, 1), cart(1, 1)) ps = sample(c, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all( @@ -322,33 +322,33 @@ coords in to.(ps) ) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) ps = sample(t, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(∈(t), ps) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) ps = sample(q, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(∈(q), ps) - b = Ball(point(10, 10), T(3)) + b = Ball(cart(10, 10), T(3)) ps = sample(b, HomogeneousSampling(100)) @test first(ps) isa Point{2} @test all(∈(b), ps) - b = Ball(point(10, 10, 10), T(10)) + b = Ball(cart(10, 10, 10), T(10)) ps = sample(b, HomogeneousSampling(100)) @test first(ps) isa Point{3} @test all(∈(b), ps) - poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) + poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, HomogeneousSampling(100)) - @test all(p -> (point(0, 0) ⪯ p ⪯ point(1, 1)) || (point(1, 1) ⪯ p ⪯ point(2, 2)), ps) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) mesh = SimpleMesh(points, connec) ps = sample(mesh, HomogeneousSampling(400)) @@ -360,13 +360,13 @@ end @testset "MinDistanceSampling" begin - poly1 = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly2 = PolyArea(point.([(1, 1), (2, 1), (2, 2), (1, 2)])) + poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, MinDistanceSampling(0.1)) - @test all(p -> (point(0, 0) ⪯ p ⪯ point(1, 1)) || (point(1, 1) ⪯ p ⪯ point(2, 2)), ps) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) mesh = SimpleMesh(points, connec) ps = sample(mesh, MinDistanceSampling(0.2)) @@ -377,7 +377,7 @@ # geometries with almost zero measure # can still be sampled (at least one point) - poly = PolyArea(point.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) + poly = PolyArea(cart.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) ps = sample(poly, MinDistanceSampling(3.2423333333753135e-5)) @test length(ps) > 0 end @@ -406,20 +406,20 @@ method = RegularSampling(10) for geom in [ - Box(point(0, 0), point(2, 2)) - Sphere(point(0, 0), T(2)) - Ball(point(0, 0), T(2)) - Segment(point(0, 0), point(1, 1)) - Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + Box(cart(0, 0), cart(2, 2)) + Sphere(cart(0, 0), T(2)) + Ball(cart(0, 0), T(2)) + Segment(cart(0, 0), cart(1, 1)) + Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) Hexahedron( - point(0, 0, 0), - point(1, 0, 0), - point(1, 1, 0), - point(0, 1, 0), - point(0, 0, 1), - point(1, 0, 1), - point(1, 1, 1), - point(0, 1, 1) + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) ) ] rng = StableRNG(2021) diff --git a/test/sets.jl b/test/sets.jl index 85d65cc47..53bcd5046 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -1,15 +1,15 @@ @testset "Sets" begin @testset "GeometrySet" begin - s = Segment(point(0, 0), point(1, 1)) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) - p = PolyArea(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + s = Segment(cart(0, 0), cart(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + p = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) gset = GeometrySet([s, t, p]) @test Meshes.crs(gset) <: Cartesian{NoDatum} @test Meshes.lentype(gset) == ℳ - @test [centroid(gset, i) for i in 1:3] == point.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) + @test [centroid(gset, i) for i in 1:3] == cart.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) - s = Segment(point(0, 0), point(1, 1)) - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + s = Segment(cart(0, 0), cart(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) geoms = [s, t] gset1 = GeometrySet(geoms) gset2 = GeometrySet(g for g in geoms) @@ -19,9 +19,9 @@ # make sure that eltype is inferred properly # https://github.com/JuliaGeometry/Meshes.jl/issues/643 geoms = Vector{Segment}() - push!(geoms, Segment(point(0, 0), point(1, 0))) - push!(geoms, Segment(point(1, 0), point(1, 1))) - push!(geoms, Segment(point(1, 1), point(0, 0))) + push!(geoms, Segment(cart(0, 0), cart(1, 0))) + push!(geoms, Segment(cart(1, 0), cart(1, 1))) + push!(geoms, Segment(cart(1, 1), cart(0, 0))) gset = GeometrySet(geoms) @test eltype(gset) <: Segment{2} @@ -55,8 +55,8 @@ @test nelements(pset) == 100 @test eltype(pset) <: Point{3} - pset1 = PointSet([point(1, 2, 3), point(4, 5, 6)]) - pset2 = PointSet(point(1, 2, 3), point(4, 5, 6)) + pset1 = PointSet([cart(1, 2, 3), cart(4, 5, 6)]) + pset2 = PointSet(cart(1, 2, 3), cart(4, 5, 6)) pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) pset5 = PointSet(T[1 4; 2 5; 3 6]) @@ -65,23 +65,23 @@ @test embeddim(pset) == 3 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 2 - @test pset[1] == point(1, 2, 3) - @test pset[2] == point(4, 5, 6) + @test pset[1] == cart(1, 2, 3) + @test pset[2] == cart(4, 5, 6) end - pset = PointSet(point.([(0, 0), (1, 0), (0, 1)])) - @test centroid(pset) == point(1 / 3, 1 / 3) + pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) + @test centroid(pset) == cart(1 / 3, 1 / 3) - pset = PointSet(point.([(1, 0), (0, 1)])) + pset = PointSet(cart.([(1, 0), (0, 1)])) @test nelements(pset) == 2 - @test centroid(pset, 1) == point(1, 0) - @test centroid(pset, 2) == point(0, 1) + @test centroid(pset, 1) == cart(1, 0) + @test centroid(pset, 2) == cart(0, 1) - pset = PointSet(point.([(0, 0), (1, 0), (0, 1)])) + pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) @test measure(pset) == zero(T) * u"m" # constructor with iterator - points = point.([(1, 0), (0, 1)]) + points = cart.([(1, 0), (0, 1)]) pset1 = PointSet(points) pset2 = PointSet(p for p in points) @test pset1 == pset2 @@ -92,7 +92,7 @@ pset = PointSet(points) @test datum(Meshes.crs(centroid(pset))) === WGS84Latest - pset = PointSet(point.([(1, 0), (0, 1)])) + pset = PointSet(cart.([(1, 0), (0, 1)])) @test sprint(show, pset) == "2 PointSet" if T == Float32 @test sprint(show, MIME"text/plain"(), pset) == """ diff --git a/test/sideof.jl b/test/sideof.jl index 0f4b53307..9cf4c3c9b 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -1,14 +1,14 @@ @testset "sideof" begin - p1, p2, p3 = point(0, 0), point(1, 1), point(0.25, 0.5) - l = Line(point(0.5, 0.0), point(0.0, 1.0)) + p1, p2, p3 = cart(0, 0), cart(1, 1), cart(0.25, 0.5) + l = Line(cart(0.5, 0.0), cart(0.0, 1.0)) @test sideof(p1, l) == LEFT @test sideof(p2, l) == RIGHT @test sideof(p3, l) == ON pts = [p1, p2, p3] @test sideof(pts, l) == [LEFT, RIGHT, ON] - p1, p2, p3 = point(0.5, 0.5), point(1.5, 0.5), point(1, 1) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + p1, p2, p3 = cart(0.5, 0.5), cart(1.5, 0.5), cart(1, 1) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT @test sideof(p3, c) == IN @@ -23,43 +23,43 @@ pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, IN] - points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)]) + points = cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)]) connec = connect.([(1, 3, 2), (1, 2, 4), (1, 4, 3), (2, 3, 4)], Triangle) mesh = SimpleMesh(points, connec) - @test sideof(point(0.25, 0.25, 0.1), mesh) == IN - @test sideof(point(0.25, 0.25, -0.1), mesh) == OUT - pts = point.([(0.25, 0.25, 0.1), (0.25, 0.25, -0.1)]) + @test sideof(cart(0.25, 0.25, 0.1), mesh) == IN + @test sideof(cart(0.25, 0.25, -0.1), mesh) == OUT + pts = cart.([(0.25, 0.25, 0.1), (0.25, 0.25, -0.1)]) @test sideof(pts, mesh) == [IN, OUT] # ray goes through vertex - @test sideof(point(0.25, 0.25, 0.1), mesh) == IN - @test sideof(point(0.25, 0.25, -0.1), mesh) == OUT + @test sideof(cart(0.25, 0.25, 0.1), mesh) == IN + @test sideof(cart(0.25, 0.25, -0.1), mesh) == OUT # ray goes through edge of triangle - @test sideof(point(0.1, 0.1, 0.1), mesh) == IN - @test sideof(point(0.1, 0.1, -0.1), mesh) == OUT + @test sideof(cart(0.1, 0.1, 0.1), mesh) == IN + @test sideof(cart(0.1, 0.1, -0.1), mesh) == OUT # point coincides with edge of triangle - @test sideof(point(0.5, 0.0, 0.0), mesh) == IN + @test sideof(cart(0.5, 0.0, 0.0), mesh) == IN # point coincides with corner of triangle - @test sideof(point(0.0, 0.0, 0.0), mesh) == IN + @test sideof(cart(0.0, 0.0, 0.0), mesh) == IN - points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) + points = cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) mesh = SimpleMesh(points, connec) # ray collinear with edge - @test sideof(point(0.0, 0.0, 0.1), mesh) == IN - @test sideof(point(0.0, 0.0, -0.1), mesh) == OUT + @test sideof(cart(0.0, 0.0, 0.1), mesh) == IN + @test sideof(cart(0.0, 0.0, -0.1), mesh) == OUT # sideof for meshes that have elements > 3-gons. - points = point.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1), (1, 1, 0)]) + points = cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1), (1, 1, 0)]) connec = connect.([(1, 2, 4), (1, 4, 3), (2, 3, 4), (1, 2, 5, 3)]) mesh = SimpleMesh(points, connec) - @test sideof(point(0.25, 0.25, 0.1), mesh) == IN + @test sideof(cart(0.25, 0.25, 0.1), mesh) == IN # sideof only defined for surface meshes - points = point.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) connec = connect.([(1, 2, 3, 4)], [Tetrahedron]) mesh = SimpleMesh(points, connec) - @test_throws AssertionError("winding number only defined for surface meshes") sideof(point(0, 0, 0), mesh) + @test_throws AssertionError("winding number only defined for surface meshes") sideof(cart(0, 0, 0), mesh) end diff --git a/test/simplification.jl b/test/simplification.jl index 50b561ee6..0e8b3f9e7 100644 --- a/test/simplification.jl +++ b/test/simplification.jl @@ -1,14 +1,14 @@ @testset "Simplification" begin @testset "DouglasPeucker" begin - c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = simplify(c, DouglasPeucker(T(0.1))) s2 = simplify(c, DouglasPeucker(T(0.5))) - @test s1 == Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - @test s2 == Ring(point.([(0, 0), (1.5, 0.5), (0, 1)])) + @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) - p = PolyArea(Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) + p = PolyArea(Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) s1 = simplify(p, DouglasPeucker(T(0.5))) - @test s1 == PolyArea(Ring(point.([(0, 0), (1.5, 0.5), (0, 1)]))) + @test s1 == PolyArea(Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)]))) m = Multi([p, p]) s2 = simplify(m, DouglasPeucker(T(0.5))) @test s2 == Multi([s1, s1]) @@ -17,7 +17,7 @@ @test s3 == GeometrySet([s1, s1]) # perform binary search for ϵ tolerance - c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = simplify(c, DouglasPeucker(T(0.1))) s2 = simplify(c, DouglasPeucker(max=6)) @test s1 == s2 @@ -27,29 +27,29 @@ end @testset "Selinger" begin - c = Ring(point.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) s1 = simplify(c, Selinger(0.1)) s2 = simplify(c, Selinger(0.5)) - @test s1 == Ring(point.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) - @test s2 == Ring(point.([(1, 0), (2, 2), (0, 2), (0, 0)])) + @test s1 == Ring(cart.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) + @test s2 == Ring(cart.([(1, 0), (2, 2), (0, 2), (0, 0)])) end @testset "Utilities" begin # decimate is a helper function to simplify # geometries with an appropriate method - b = Box(point(0, 0), point(1, 1)) + b = Box(cart(0, 0), cart(1, 1)) s = decimate(b, 1.0) @test s isa Polygon @test nvertices(s) == 3 - @test boundary(s) == Ring(point.([(0, 0), (1, 0), (0, 1)])) + @test boundary(s) == Ring(cart.([(0, 0), (1, 0), (0, 1)])) - c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = decimate(c, T(0.1)) s2 = decimate(c, T(0.5)) - @test s1 == Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - @test s2 == Ring(point.([(0, 0), (1.5, 0.5), (0, 1)])) + @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) - c = Ring(point.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) s1 = decimate(c, T(0.1)) s2 = decimate(c, max=6) @test s1 == s2 diff --git a/test/sorting.jl b/test/sorting.jl index 7e586c4b7..33671c949 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -3,7 +3,7 @@ g = cartgrid(3, 3) s = sort(g, DirectionSort((T(1), T(1)))) @test centroid.(s) == - point.([ + cart.([ (0.5, 0.5), (1.5, 0.5), (0.5, 1.5), diff --git a/test/subdomains.jl b/test/subdomains.jl index f3c05353e..95b74af18 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -23,7 +23,7 @@ @test centroid(v, i) == centroid(e) end - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) inds = rand(1:4, 3) @@ -46,9 +46,9 @@ @test v[3] == g[13] # centroid of view of PointSet - points = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) pview = view(PointSet(points), 1:4) - @test centroid(pview) == point(0.5, 0.5) + @test centroid(pview) == cart(0.5, 0.5) # measure of view g = cartgrid(10, 10) @@ -70,7 +70,7 @@ # eltype d1 = cartgrid(1000, 1000) d2 = cartgrid(1000, 1000, 1000) - d3 = GeometrySet([point(0, 0), Box(point(0, 0), point(1, 1)), point(2, 2)]) + d3 = GeometrySet([cart(0, 0), Box(cart(0, 0), cart(1, 1)), cart(2, 2)]) v1 = view(d1, 1:500000) v2 = view(d2, 1:500000000) v3 = view(d3, [1, 3]) @@ -79,7 +79,7 @@ @test eltype(v3) <: Primitive{2} # show - pset = PointSet(point.(1:100, 1:100)) + pset = PointSet(cart.(1:100, 1:100)) v1 = view(pset, 1:10) v2 = view(pset, [4, 8, 10, 7, 9, 1, 2, 3, 6, 5]) @test sprint(show, v1) == "10 view(::PointSet, 1:10)" diff --git a/test/supportfun.jl b/test/supportfun.jl index a480a44bd..ffb075426 100644 --- a/test/supportfun.jl +++ b/test/supportfun.jl @@ -1,20 +1,20 @@ @testset "Support function" begin - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) - @test supportfun(t, vector(1, 0)) == point(1, 0) - @test supportfun(t, vector(0, 1)) == point(0, 1) - @test supportfun(t, vector(-1, -1)) == point(0, 0) - @test supportfun(t, vector(-1, 1)) == point(0, 1) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test supportfun(t, vector(1, 0)) == cart(1, 0) + @test supportfun(t, vector(0, 1)) == cart(0, 1) + @test supportfun(t, vector(-1, -1)) == cart(0, 0) + @test supportfun(t, vector(-1, 1)) == cart(0, 1) - b = Ball(point(0, 0), T(2)) - @test supportfun(b, vector(1, 1)) ≈ point(√2, √2) - @test supportfun(b, vector(1, 0)) ≈ point(2, 0) - @test supportfun(b, vector(0, 1)) ≈ point(0, 2) - @test supportfun(b, vector(-1, 1)) ≈ point(-√2, √2) + b = Ball(cart(0, 0), T(2)) + @test supportfun(b, vector(1, 1)) ≈ cart(√2, √2) + @test supportfun(b, vector(1, 0)) ≈ cart(2, 0) + @test supportfun(b, vector(0, 1)) ≈ cart(0, 2) + @test supportfun(b, vector(-1, 1)) ≈ cart(-√2, √2) - b = Box(point(0, 0), point(1, 1)) - @test supportfun(b, vector(1, 1)) ≈ point(1, 1) - @test supportfun(b, vector(1, 0)) ≈ point(1, 0) - @test supportfun(b, vector(-1, 0)) ≈ point(0, 0) - @test supportfun(b, vector(-1, -1)) ≈ point(0, 0) - @test supportfun(b, vector(-1, 1)) ≈ point(0, 1) + b = Box(cart(0, 0), cart(1, 1)) + @test supportfun(b, vector(1, 1)) ≈ cart(1, 1) + @test supportfun(b, vector(1, 0)) ≈ cart(1, 0) + @test supportfun(b, vector(-1, 0)) ≈ cart(0, 0) + @test supportfun(b, vector(-1, -1)) ≈ cart(0, 0) + @test supportfun(b, vector(-1, 1)) ≈ cart(0, 1) end diff --git a/test/testutils.jl b/test/testutils.jl index 1a95004c6..10bfb4562 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -41,8 +41,8 @@ function readply(T, fname) SimpleMesh(points, connec) end -point(T::Type, coords...) = point(T, coords) -point(T::Type, coords::Tuple) = Point(T.(coords)) +cart(T::Type, coords...) = cart(T, coords) +cart(T::Type, coords::Tuple) = Point(T.(coords)) vector(T::Type, coords...) = vector(T, coords) vector(T::Type, coords::Tuple) = Vec(T.(coords)) diff --git a/test/trajecs.jl b/test/trajecs.jl index e03896777..67ca999ee 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -1,6 +1,6 @@ @testset "Trajectories" begin @testset "CylindricalTrajectory" begin - s = Segment(point(0, 0, 0), point(0, 0, 1)) + s = Segment(cart(0, 0, 0), cart(0, 0, 1)) c = [s(t) for t in range(T(0), stop=T(1), length=10)] t = CylindricalTrajectory(c) @test Meshes.crs(t) <: Cartesian{NoDatum} @@ -10,7 +10,7 @@ @test radius(t) == T(1) * u"m" @test topology(t) == GridTopology(10) - b = BezierCurve([point(0, 0, 0), point(3, 3, 0), point(3, 0, 7)]) + b = BezierCurve([cart(0, 0, 0), cart(3, 3, 0), cart(3, 0, 7)]) c = [b(t) for t in range(T(0), stop=T(1), length=20)] t = CylindricalTrajectory(c, T(2)) @test Meshes.crs(t) <: Cartesian{NoDatum} @@ -21,13 +21,13 @@ @test topology(t) == GridTopology(20) # trajectory with single cylinder - t = CylindricalTrajectory([point(0, 0, 0)], T(1)) + t = CylindricalTrajectory([cart(0, 0, 0)], T(1)) @test Meshes.crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 1 @test radius(t) == T(1) * u"m" @test topology(t) == GridTopology(1) - @test t[1] == Cylinder(point(0, 0, -0.5), point(0, 0, 0.5), T(1)) + @test t[1] == Cylinder(cart(0, 0, -0.5), cart(0, 0, 0.5), T(1)) end end diff --git a/test/transforms.jl b/test/transforms.jl index 6961ae71d..e3b08ab4f 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -23,9 +23,9 @@ # ------ f = Rotate(Angle2d(T(π / 2))) - g = point(1, 0) + g = cart(1, 0) r, c = TB.apply(f, g) - @test r ≈ point(0, 1) + @test r ≈ cart(0, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -33,9 +33,9 @@ # -------- f = Rotate(Angle2d(T(π / 2))) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(0, 0), point(0, 1)) + @test r ≈ Segment(cart(0, 0), cart(0, 1)) @test TB.revert(f, r, c) ≈ g # ---- @@ -43,27 +43,27 @@ # ---- f = Rotate(Angle2d(T(π / 2))) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Quadrangle - @test r ≈ Quadrangle(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) + @test r ≈ Quadrangle(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) q = TB.revert(f, r, c) @test q isa Quadrangle @test q ≈ convert(Quadrangle, g) f = Rotate(vector(1, 0, 0), vector(0, 1, 0)) - g = Box(point(0, 0, 0), point(1, 1, 1)) + g = Box(cart(0, 0, 0), cart(1, 1, 1)) r, c = TB.apply(f, g) @test r isa Hexahedron @test r ≈ Hexahedron( - point(0, 0, 0), - point(0, 1, 0), - point(-1, 1, 0), - point(-1, 0, 0), - point(0, 0, 1), - point(0, 1, 1), - point(-1, 1, 1), - point(-1, 0, 1) + cart(0, 0, 0), + cart(0, 1, 0), + cart(-1, 1, 0), + cart(-1, 0, 0), + cart(0, 0, 1), + cart(0, 1, 1), + cart(-1, 1, 1), + cart(-1, 0, 1) ) h = TB.revert(f, r, c) @test h isa Hexahedron @@ -74,15 +74,15 @@ # ---------- f = Rotate(Angle2d(T(π / 2))) - g = Rope(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + g = Rope(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) r, c = TB.apply(f, g) - @test r ≈ Rope(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) + @test r ≈ Rope(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) @test TB.revert(f, r, c) ≈ g f = Rotate(Angle2d(T(π / 2))) - g = Ring(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + g = Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) r, c = TB.apply(f, g) - @test r ≈ Ring(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) + @test r ≈ Ring(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -90,15 +90,15 @@ # --------- f = Rotate(AngleAxis(T(π / 2), T(0), T(0), T(1))) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(0, 0, 0), point(0, 1, 0), point(-1, 0, 0)) + @test r ≈ Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(-1, 0, 0)) @test TB.revert(f, r, c) ≈ g f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 0)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(0, 0, 0), point(0, 0, -1), point(0, 1, 0)) + @test r ≈ Triangle(cart(0, 0, 0), cart(0, 0, -1), cart(0, 1, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -106,9 +106,9 @@ # --------- f = Rotate(Angle2d(T(π / 2))) - p = PolyArea(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) r, c = TB.apply(f, p) - @test r ≈ PolyArea(point(0, 0), point(0, 1), point(-1, 1), point(-1, 0)) + @test r ≈ PolyArea(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) @test TB.revert(f, r, c) ≈ p # ---------- @@ -116,7 +116,7 @@ # ---------- f = Rotate(Angle2d(T(π / 2))) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -127,9 +127,9 @@ # ------ f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) - g = Plane(point(0, 0, 0), vector(0, 0, 1)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(point(0, 0, 0), vector(1, 0, 0)) + @test r ≈ Plane(cart(0, 0, 0), vector(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -139,7 +139,7 @@ f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(point(0, 0, 0), point(1, 0, 0)) + @test r ≈ Cylinder(cart(0, 0, 0), cart(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -147,9 +147,9 @@ # --------- f = Rotate(Angle2d(T(π / 2))) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([point(0, 0), point(0, 1), point(-1, 1)]) + @test r ≈ PointSet([cart(0, 0), cart(0, 1), cart(-1, 1)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -157,7 +157,7 @@ # ------------ f = Rotate(Angle2d(T(π / 2))) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -205,7 +205,7 @@ # ----------- f = Rotate(Angle2d(T(π / 2))) - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -259,9 +259,9 @@ # ------ f = Translate(T(1), T(1)) - g = point(1, 0) + g = cart(1, 0) r, c = TB.apply(f, g) - @test r ≈ point(2, 1) + @test r ≈ cart(2, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -269,9 +269,9 @@ # -------- f = Translate(T(1), T(1)) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(1, 1), point(2, 1)) + @test r ≈ Segment(cart(1, 1), cart(2, 1)) @test TB.revert(f, r, c) ≈ g # ---- @@ -279,10 +279,10 @@ # ---- f = Translate(T(1), T(1)) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(point(1, 1), point(2, 2)) + @test r ≈ Box(cart(1, 1), cart(2, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -290,9 +290,9 @@ # --------- f = Translate(T(1), T(2), T(3)) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(1, 2, 3), point(2, 2, 3), point(1, 3, 4)) + @test r ≈ Triangle(cart(1, 2, 3), cart(2, 2, 3), cart(1, 3, 4)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -300,7 +300,7 @@ # ---------- f = Translate(T(1), T(1)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -311,9 +311,9 @@ # ------ f = Translate(T(0), T(0), T(1)) - g = Plane(point(0, 0, 0), vector(0, 0, 1)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(point(0, 0, 1), vector(0, 0, 1)) + @test r ≈ Plane(cart(0, 0, 1), vector(0, 0, 1)) @test TB.revert(f, r, c) ≈ g # --------- @@ -323,7 +323,7 @@ f = Translate(T(0), T(0), T(1)) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(point(0, 0, 1), point(0, 0, 2)) + @test r ≈ Cylinder(cart(0, 0, 1), cart(0, 0, 2)) @test TB.revert(f, r, c) ≈ g # --------- @@ -331,9 +331,9 @@ # --------- f = Translate(T(1), T(1)) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([point(1, 1), point(2, 1), point(2, 2)]) + @test r ≈ PointSet([cart(1, 1), cart(2, 1), cart(2, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -341,7 +341,7 @@ # ------------ f = Translate(T(1), T(1)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -359,7 +359,7 @@ d = cartgrid(10, 10) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) + @test r ≈ CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ---------------- @@ -389,7 +389,7 @@ # ----------- f = Translate(T(1), T(1)) - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -429,9 +429,9 @@ # ------ f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = point(1, 0) + g = cart(1, 0) r, c = TB.apply(f, g) - @test r ≈ point(1, 2) + @test r ≈ cart(1, 2) @test TB.revert(f, r, c) ≈ g # -------- @@ -439,9 +439,9 @@ # -------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(1, 1), point(1, 2)) + @test r ≈ Segment(cart(1, 1), cart(1, 2)) @test TB.revert(f, r, c) ≈ g # ---- @@ -449,27 +449,27 @@ # ---- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Quadrangle - @test r ≈ Quadrangle(point(1, 1), point(1, 2), point(0, 2), point(0, 1)) + @test r ≈ Quadrangle(cart(1, 1), cart(1, 2), cart(0, 2), cart(0, 1)) q = TB.revert(f, r, c) @test q isa Quadrangle @test q ≈ convert(Quadrangle, g) f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) - g = Box(point(0, 0, 0), point(1, 1, 1)) + g = Box(cart(0, 0, 0), cart(1, 1, 1)) r, c = TB.apply(f, g) @test r isa Hexahedron @test r ≈ Hexahedron( - point(1, 2, 3), - point(1, 2, 2), - point(1, 3, 2), - point(1, 3, 3), - point(2, 2, 3), - point(2, 2, 2), - point(2, 3, 2), - point(2, 3, 3) + cart(1, 2, 3), + cart(1, 2, 2), + cart(1, 3, 2), + cart(1, 3, 3), + cart(2, 2, 3), + cart(2, 2, 2), + cart(2, 3, 2), + cart(2, 3, 3) ) h = TB.revert(f, r, c) @test h isa Hexahedron @@ -480,9 +480,9 @@ # --------- f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(1, 2, 3), point(1, 2, 2), point(2, 3, 3)) + @test r ≈ Triangle(cart(1, 2, 3), cart(1, 2, 2), cart(2, 3, 3)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -490,7 +490,7 @@ # ---------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -501,9 +501,9 @@ # ------ f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) - g = Plane(point(0, 0, 0), vector(0, 0, 1)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(point(0, 0, 1), vector(1, 0, 0)) + @test r ≈ Plane(cart(0, 0, 1), vector(1, 0, 0)) @test TB.revert(f, r, c) ≈ g # --------- @@ -513,7 +513,7 @@ f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) g = Cylinder(T(1)) r, c = TB.apply(f, g) - @test r ≈ Cylinder(point(0, 0, 1), point(1, 0, 1)) + @test r ≈ Cylinder(cart(0, 0, 1), cart(1, 0, 1)) @test TB.revert(f, r, c) ≈ g # --------- @@ -521,9 +521,9 @@ # --------- f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([point(1, 1), point(1, 2), point(0, 2)]) + @test r ≈ PointSet([cart(1, 1), cart(1, 2), cart(0, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -531,7 +531,7 @@ # ------------ f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -581,9 +581,9 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) s = Rotate(T(π / 2)) → Translate(T(1), T(1)) v = vector(1, 0) - g1 = point(1, 0) - g2 = Segment(point(0, 0), point(1, 0)) - g3 = Box(point(0, 0), point(1, 1)) + g1 = cart(1, 0) + g2 = Segment(cart(0, 0), cart(1, 0)) + g3 = Box(cart(0, 0), cart(1, 1)) @test f(v) ≈ s(v) @test f(g1) ≈ s(g1) @test f(g2) ≈ s(g2) @@ -638,9 +638,9 @@ # ------ f = Scale(T(1), T(2)) - g = point(1, 1) + g = cart(1, 1) r, c = TB.apply(f, g) - @test r ≈ point(1, 2) + @test r ≈ cart(1, 2) @test TB.revert(f, r, c) ≈ g # -------- @@ -648,15 +648,15 @@ # -------- f = Scale(T(1), T(2)) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(0, 0), point(1, 0)) + @test r ≈ Segment(cart(0, 0), cart(1, 0)) @test TB.revert(f, r, c) ≈ g f = Scale(T(2), T(1)) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(0, 0), point(2, 0)) + @test r ≈ Segment(cart(0, 0), cart(2, 0)) @test TB.revert(f, r, c) ≈ g # ---- @@ -664,10 +664,10 @@ # ---- f = Scale(T(1), T(2)) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(point(0, 0), point(1, 2)) + @test r ≈ Box(cart(0, 0), cart(1, 2)) @test TB.revert(f, r, c) ≈ g # ----- @@ -675,7 +675,7 @@ # ----- f = Scale(T(1), T(2)) - g = Ball(point(1, 2), T(3)) + g = Ball(cart(1, 2), T(3)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -688,7 +688,7 @@ # ------- f = Scale(T(1), T(2)) - g = Sphere(point(1, 2), T(3)) + g = Sphere(cart(1, 2), T(3)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -697,26 +697,26 @@ @test TB.revert(f, r, c) ≈ m f = Scale(T(1), T(2), T(3)) - g = Sphere(point(1, 2, 3), T(4)) + g = Sphere(cart(1, 2, 3), T(4)) r, c = TB.apply(f, g) @test r isa Ellipsoid - @test r ≈ Ellipsoid(T.((4, 8, 12)), point(1, 4, 9)) + @test r ≈ Ellipsoid(T.((4, 8, 12)), cart(1, 4, 9)) m = TB.revert(f, r, c) @test m isa SimpleMesh @test m ≈ discretize(g) f = Scale(T(2)) - g = Sphere(point(1, 2), T(3)) + g = Sphere(cart(1, 2), T(3)) r, c = TB.apply(f, g) @test r isa Sphere - @test r ≈ Sphere(point(2, 4), T(6)) + @test r ≈ Sphere(cart(2, 4), T(6)) @test TB.revert(f, r, c) ≈ g f = Scale(T(2)) - g = Sphere(point(1, 2, 3), T(4)) + g = Sphere(cart(1, 2, 3), T(4)) r, c = TB.apply(f, g) @test r isa Sphere - @test r ≈ Sphere(point(2, 4, 6), T(8)) + @test r ≈ Sphere(cart(2, 4, 6), T(8)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -737,7 +737,7 @@ # ----- f = Scale(T(1), T(2), T(3)) - g = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -750,7 +750,7 @@ # ------- f = Scale(T(1), T(2), T(3)) - g = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -776,7 +776,7 @@ # ------------------ f = Scale(T(1), T(2), T(3)) - g = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -788,7 +788,7 @@ # ------ f = Scale(T(1), T(2), T(3)) - g = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -801,9 +801,9 @@ # --------- f = Scale(T(1), T(2), T(3)) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 2, 3)) + @test r ≈ Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 2, 3)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -811,7 +811,7 @@ # ---------- f = Scale(T(1), T(2)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -822,13 +822,13 @@ # ------ f = Scale(T(1), T(1), T(2)) - g = Plane(point(1, 1, 1), vector(0, 0, 1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) - @test r ≈ Plane(point(1, 1, 2), vector(0, 0, 1)) + @test r ≈ Plane(cart(1, 1, 2), vector(0, 0, 1)) @test TB.revert(f, r, c) ≈ g f = Scale(T(2), T(1), T(1)) - g = Plane(point(1, 1, 1), vector(0, 0, 1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g @@ -838,9 +838,9 @@ # --------- f = Scale(T(1), T(2)) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([point(0, 0), point(1, 0), point(1, 2)]) + @test r ≈ PointSet([cart(0, 0), cart(1, 0), cart(1, 2)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -848,7 +848,7 @@ # ------------ f = Scale(T(1), T(2)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -863,10 +863,10 @@ # -------------- f = Scale(T(1), T(2)) - d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(point(1, 2), point(11, 22), dims=(10, 10)) + @test r ≈ CartesianGrid(cart(1, 2), cart(11, 22), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ---------------- @@ -896,7 +896,7 @@ # ----------- f = Scale(T(1), T(2)) - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -928,9 +928,9 @@ # ------ f = Stretch(T(1), T(2)) - g = point(1, 1) + g = cart(1, 1) r, c = TB.apply(f, g) - @test r ≈ point(1, 1) + @test r ≈ cart(1, 1) @test TB.revert(f, r, c) ≈ g # -------- @@ -938,15 +938,15 @@ # -------- f = Stretch(T(1), T(2)) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(0, 0), point(1, 0)) + @test r ≈ Segment(cart(0, 0), cart(1, 0)) @test TB.revert(f, r, c) ≈ g f = Stretch(T(2), T(1)) - g = Segment(point(0, 0), point(1, 0)) + g = Segment(cart(0, 0), cart(1, 0)) r, c = TB.apply(f, g) - @test r ≈ Segment(point(-0.5, 0), point(1.5, 0)) + @test r ≈ Segment(cart(-0.5, 0), cart(1.5, 0)) @test TB.revert(f, r, c) ≈ g # ---- @@ -954,10 +954,10 @@ # ---- f = Stretch(T(1), T(2)) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Box - @test r ≈ Box(point(0, -0.5), point(1, 1.5)) + @test r ≈ Box(cart(0, -0.5), cart(1, 1.5)) @test TB.revert(f, r, c) ≈ g # --------- @@ -965,9 +965,9 @@ # --------- f = Stretch(T(1), T(2), T(2)) - g = Triangle(point(0, 0, 0), point(1, 0, 0), point(0, 1, 1)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(0, -1 / 3, -1 / 3), point(1, -1 / 3, -1 / 3), point(0, 10 / 6, 10 / 6)) + @test r ≈ Triangle(cart(0, -1 / 3, -1 / 3), cart(1, -1 / 3, -1 / 3), cart(0, 10 / 6, 10 / 6)) @test TB.revert(f, r, c) ≈ g # ---------- @@ -975,7 +975,7 @@ # ---------- f = Stretch(T(1), T(2)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -986,13 +986,13 @@ # ------ f = Stretch(T(1), T(1), T(2)) - g = Plane(point(1, 1, 1), vector(0, 0, 1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g f = Stretch(T(2), T(1), T(1)) - g = Plane(point(1, 1, 1), vector(0, 0, 1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ g @test TB.revert(f, r, c) ≈ g @@ -1002,9 +1002,9 @@ # --------- f = Stretch(T(1), T(2)) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) - @test r ≈ PointSet([point(0, -1 / 3), point(1, -1 / 3), point(1, 10 / 6)]) + @test r ≈ PointSet([cart(0, -1 / 3), cart(1, -1 / 3), cart(1, 10 / 6)]) @test TB.revert(f, r, c) ≈ d # ------------ @@ -1012,7 +1012,7 @@ # ------------ f = Stretch(T(1), T(2)) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -1027,10 +1027,10 @@ # -------------- f = Stretch(T(1), T(2)) - d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(point(1, -4), point(11, 16), dims=(10, 10)) + @test r ≈ CartesianGrid(cart(1, -4), cart(11, 16), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d # ---------------- @@ -1060,7 +1060,7 @@ # ----------- f = Stretch(T(1), T(2)) - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -1089,16 +1089,16 @@ # -------------- f = StdCoords() - d = CartesianGrid(point(1, 1), point(11, 11), dims=(10, 10)) + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r ≈ CartesianGrid(point(-0.5, -0.5), point(0.5, 0.5), dims=(10, 10)) + @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d f = StdCoords() d = cartgrid(10, 20) r, c = TB.apply(f, d) - @test r ≈ CartesianGrid(point(-0.5, -0.5), point(0.5, 0.5), dims=(10, 20)) + @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 20)) @test TB.revert(f, r, c) ≈ d r2 = TB.reapply(f, d, c) @test r == r2 @@ -1123,14 +1123,14 @@ # ------ f = FlatCoords() - g = point(1, 1) + g = cart(1, 1) r, c = TB.apply(f, g) - @test r == point(1, 1) + @test r == cart(1, 1) f = FlatCoords() g = Point(Polar(T(√2), T(π / 4))) r, c = TB.apply(f, g) - @test r ≈ point(1, 1) + @test r ≈ cart(1, 1) f = FlatCoords() g = Point(LatLon(T(30), T(60))) @@ -1153,7 +1153,7 @@ @test r == Point(Cartesian{WGS84Latest}(T(1), T(1))) f = FlatCoords() - g = point(1, 1, 1) + g = cart(1, 1, 1) # error: points must have 2 coordinates @test_throws ArgumentError TB.apply(f, g) @@ -1164,12 +1164,12 @@ f = FlatCoords() g = Rope(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) r, c = TB.apply(f, g) - @test r ≈ Rope(point(0, 0), point(1, 0), point(0, 1)) + @test r ≈ Rope(cart(0, 0), cart(1, 0), cart(0, 1)) f = FlatCoords() g = Ring(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) r, c = TB.apply(f, g) - @test r ≈ Ring(point(0, 0), point(1, 0), point(0, 1)) + @test r ≈ Ring(cart(0, 0), cart(1, 0), cart(0, 1)) # --------- # TRIANGLE @@ -1178,7 +1178,7 @@ f = FlatCoords() g = Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) r, c = TB.apply(f, g) - @test r ≈ Triangle(point(0, 0), point(1, 0), point(0, 1)) + @test r ≈ Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) end @testset "Proj" begin @@ -1203,7 +1203,7 @@ # ------ f = Proj(Polar) - g = point(1, 1) + g = cart(1, 1) r, c = TB.apply(f, g) @test r ≈ Point(Polar(T(√2), T(π / 4))) @@ -1212,7 +1212,7 @@ # -------- f = Proj(Polar) - g = Segment(point(0, 0), point(1, 1)) + g = Segment(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r ≈ Segment(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) @@ -1221,7 +1221,7 @@ # ---- f = Proj(Polar) - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Box @test r ≈ Box(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) @@ -1231,7 +1231,7 @@ # --------- f = Proj(Polar) - g = Triangle(point(0, 0), point(1, 0), point(1, 1)) + g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r ≈ Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))) @@ -1240,7 +1240,7 @@ # ---------- f = Proj(Polar) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -1250,7 +1250,7 @@ # ------ f = Proj(Cylindrical) - g = Plane(point(1, 1, 1), vector(0, 0, 1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) r, c = TB.apply(f, g) @test r ≈ Plane(Point(Cylindrical(T(√2), T(π / 4), T(1))), vector(0, 0, 1)) @@ -1259,7 +1259,7 @@ # --------- f = Proj(Cylindrical) - g = Cylinder(point(0, 0, 0), point(1, 1, 1)) + g = Cylinder(cart(0, 0, 0), cart(1, 1, 1)) r, c = TB.apply(f, g) @test r ≈ Cylinder(Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(√2), T(π / 4), T(1)))) @@ -1268,7 +1268,7 @@ # --------- f = Proj(Polar) - d = PointSet([point(0, 0), point(1, 0), point(1, 1)]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) r, c = TB.apply(f, d) @test r ≈ PointSet([Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))]) @@ -1277,7 +1277,7 @@ # ------------ f = Proj(Polar) - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -1290,7 +1290,7 @@ # -------------- f = Proj(Polar) - d = CartesianGrid((10, 10), point(1, 1), T.((1, 1))) + d = CartesianGrid((10, 10), cart(1, 1), T.((1, 1))) r, c = TB.apply(f, d) @test r isa CartesianGrid @test r ≈ CartesianGrid((10, 10), Point(Polar(T(√2), T(π / 4))), T.((1, 1))) @@ -1320,7 +1320,7 @@ # ----------- f = Proj(Polar) - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -1347,7 +1347,7 @@ # ------ f = LengthUnit(u"cm") - g = point(1, 1) + g = cart(1, 1) r, c = TB.apply(f, g) @test r ≈ Point(T(100) * u"cm", T(100) * u"cm") @@ -1375,7 +1375,7 @@ # -------- f = LengthUnit(u"km") - g = Segment(point(0, 0), point(1000, 1000)) + g = Segment(cart(0, 0), cart(1000, 1000)) r, c = TB.apply(f, g) @test r ≈ Segment(Point(T(0) * u"km", T(0) * u"km"), Point(T(1) * u"km", T(1) * u"km")) @@ -1384,7 +1384,7 @@ # ---- f = LengthUnit(u"cm") - g = Box(point(0, 0), point(1, 1)) + g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) @test r isa Box @test r ≈ Box(Point(T(0) * u"cm", T(0) * u"cm"), Point(T(100) * u"cm", T(100) * u"cm")) @@ -1394,7 +1394,7 @@ # ------- f = LengthUnit(u"km") - g = Sphere(point(0, 0), T(1000)) + g = Sphere(cart(0, 0), T(1000)) r, c = TB.apply(f, g) @test r isa Sphere @test r ≈ Sphere(Point(T(0) * u"km", T(0) * u"km"), T(1) * u"km") @@ -1404,7 +1404,7 @@ # ---------- f = LengthUnit(u"cm") - g = Ellipsoid(T.((1, 1, 1)), point(0, 0, 0)) + g = Ellipsoid(T.((1, 1, 1)), cart(0, 0, 0)) r, c = TB.apply(f, g) @test r isa Ellipsoid @test r ≈ @@ -1415,7 +1415,7 @@ # --------- f = LengthUnit(u"km") - g = Triangle(point(0, 0), point(1000, 0), point(1000, 1000)) + g = Triangle(cart(0, 0), cart(1000, 0), cart(1000, 1000)) r, c = TB.apply(f, g) @test r ≈ Triangle( Point(T(0) * u"km", T(0) * u"km"), @@ -1428,7 +1428,7 @@ # ---------- f = LengthUnit(u"cm") - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) @@ -1438,7 +1438,7 @@ # --------- f = LengthUnit(u"km") - d = PointSet([point(0, 0), point(1000, 0), point(1000, 1000)]) + d = PointSet([cart(0, 0), cart(1000, 0), cart(1000, 1000)]) r, c = TB.apply(f, d) @test r ≈ PointSet([ Point(T(0) * u"km", T(0) * u"km"), @@ -1451,7 +1451,7 @@ # ------------ f = LengthUnit(u"cm") - t = Triangle(point(0, 0), point(1, 0), point(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r ≈ GeometrySet([f(t), f(t)]) @@ -1464,7 +1464,7 @@ # -------------- f = LengthUnit(u"km") - d = CartesianGrid((10, 10), point(1000, 1000), T.((1000, 1000))) + d = CartesianGrid((10, 10), cart(1000, 1000), T.((1000, 1000))) r, c = TB.apply(f, d) @test r isa CartesianGrid @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), (T(1) * u"km", T(1) * u"km")) @@ -1494,7 +1494,7 @@ # ----------- f = LengthUnit(u"cm") - p = point.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -1525,35 +1525,35 @@ # ------ f = Shadow(:xz) - g = point(1, 2, 3) + g = cart(1, 2, 3) r, c = TB.apply(f, g) - @test r == point(1, 3) + @test r == cart(1, 3) # -------- # SEGMENT # -------- f = Shadow(:yz) - g = Segment(point(1, 2, 3), point(4, 5, 6)) + g = Segment(cart(1, 2, 3), cart(4, 5, 6)) r, c = TB.apply(f, g) - @test r == Segment(point(2, 3), point(5, 6)) + @test r == Segment(cart(2, 3), cart(5, 6)) # ---- # BOX # ---- f = Shadow(:xy) - g = Box(point(1, 2, 3), point(4, 5, 6)) + g = Box(cart(1, 2, 3), cart(4, 5, 6)) r, c = TB.apply(f, g) @test r isa Box - @test r == Box(point(1, 2), point(4, 5)) + @test r == Box(cart(1, 2), cart(4, 5)) # ------ # PLANE # ------ f = Shadow(:xz) - g = Plane(point(0, 0, 0), vector(0, 0, 1)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) @test_throws ArgumentError TB.apply(f, g) # ---------- @@ -1572,7 +1572,7 @@ # ----- f = Shadow(:xy) - g = Disk(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -1583,7 +1583,7 @@ # ------- f = Shadow(:xz) - g = Circle(Plane(point(0, 0, 0), vector(0, 0, 1)), T(2)) + g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -1605,8 +1605,8 @@ # ------------ f = Shadow(:xy) - p = Plane(point(0, 0, 0), vector(0, 0, 1)) - g = ConeSurface(Disk(p, T(2)), point(0, 0, 1)) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + g = ConeSurface(Disk(p, T(2)), cart(0, 0, 1)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -1617,8 +1617,8 @@ # --------------- f = Shadow(:xz) - pb = Plane(point(0, 0, 0), vector(0, 0, 1)) - pt = Plane(point(0, 0, 10), vector(0, 0, 1)) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) g = FrustumSurface(Disk(pb, T(1)), Disk(pt, T(2))) m = discretize(g) r, c = TB.apply(f, g) @@ -1630,7 +1630,7 @@ # ------------------ f = Shadow(:yz) - g = ParaboloidSurface(point(0, 0, 0), T(1), T(2)) + g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -1641,7 +1641,7 @@ # ------ f = Shadow(:xy) - g = Torus(point(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) m = discretize(g) r, c = TB.apply(f, g) @test r isa SimpleMesh @@ -1652,16 +1652,16 @@ # --------- f = Shadow(:xz) - g = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + g = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) r, c = TB.apply(f, g) - @test r == Triangle(point(1, 3), point(4, 6), point(7, 9)) + @test r == Triangle(cart(1, 3), cart(4, 6), cart(7, 9)) # ---------- # MULTIGEOM # ---------- f = Shadow(:yz) - t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) g = Multi([t, t]) r, c = TB.apply(f, g) @test r == Multi([f(t), f(t)]) @@ -1671,16 +1671,16 @@ # --------- f = Shadow(:xy) - d = PointSet([point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)]) + d = PointSet([cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)]) r, c = TB.apply(f, d) - @test r == PointSet([point(1, 2), point(4, 5), point(7, 8)]) + @test r == PointSet([cart(1, 2), cart(4, 5), cart(7, 8)]) # ------------ # GEOMETRYSET # ------------ f = Shadow(:xz) - t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)) + t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) d = GeometrySet([t, t]) r, c = TB.apply(f, d) @test r == GeometrySet([f(t), f(t)]) @@ -1693,10 +1693,10 @@ # -------------- f = Shadow(:yz) - d = CartesianGrid((10, 11, 12), point(1, 2, 3), T.((1.0, 1.1, 1.2))) + d = CartesianGrid((10, 11, 12), cart(1, 2, 3), T.((1.0, 1.1, 1.2))) r, c = TB.apply(f, d) @test r isa CartesianGrid - @test r == CartesianGrid((11, 12), point(2, 3), T.((1.1, 1.2))) + @test r == CartesianGrid((11, 12), cart(2, 3), T.((1.1, 1.2))) # ---------------- # RECTILINEARGRID @@ -1723,7 +1723,7 @@ # ----------- f = Shadow(:yz) - p = point.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)]) + p = cart.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) d = SimpleMesh(p, c) r, c = TB.apply(f, d) @@ -1732,21 +1732,21 @@ @testset "Repair{0}" begin @test !isaffine(Repair) - poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) rpoly = poly |> Repair{0}() @test nvertices(rpoly) == 4 - @test vertices(rpoly) == point.([(0, 0), (1, 0), (1, 1), (0, 1)]) + @test vertices(rpoly) == cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) end @testset "Repair{1}" begin # a tetrahedron with an unused vertex - points = point.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) + points = cart.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) mesh = SimpleMesh(points, connec) rmesh = mesh |> Repair{1}() @test nvertices(rmesh) == nvertices(mesh) - 1 @test nelements(rmesh) == nelements(mesh) - @test point(5, 5, 5) ∉ vertices(rmesh) + @test cart(5, 5, 5) ∉ vertices(rmesh) end @testset "Repair{2}" begin end @@ -1777,30 +1777,30 @@ @testset "Repair{8}" begin poly = PolyArea( - point.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) + cart.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) ) rpoly = poly |> Repair{8}() @test nvertices(rpoly) == 4 - @test vertices(rpoly) == point.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) + @test vertices(rpoly) == cart.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) # degenerate triangle with repeated vertices - poly = PolyArea(point.([(0, 0), (1, 1), (1, 1)])) + poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) rpoly = poly |> Repair{8}() @test !hasholes(rpoly) - @test rings(rpoly) == [Ring(point(0, 0))] - @test vertices(rpoly) == [point(0, 0)] + @test rings(rpoly) == [Ring(cart(0, 0))] + @test vertices(rpoly) == [cart(0, 0)] end @testset "Repair{9}" begin - poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) + poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) bpoly = poly |> Repair{9}() @test bpoly isa Quadrangle @test bpoly == poly end @testset "Repair{10}" begin - outer = Ring(point.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) - inner = Ring(point.([(1, 1), (1, 2), (2, 2), (2, 1)])) + outer = Ring(cart.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) + inner = Ring(cart.([(1, 1), (1, 2), (2, 2), (2, 1)])) poly = PolyArea(outer, inner) repair = Repair{10}() rpoly, cache = TB.apply(repair, poly) @@ -1819,32 +1819,32 @@ @test TB.parameters(f) == (; δ) # https://github.com/JuliaGeometry/Meshes.jl/issues/566 - outer = Ring(point(6, 4), point(6, 7), point(1, 6), point(1, 1), point(5, 2)) - inner₁ = Ring(point(3, 3), point(3, 4), point(4, 3)) - inner₂ = Ring(point(2, 5), point(2, 6), point(3, 5)) + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) poly = PolyArea([outer, inner₁, inner₂]) bpoly = poly |> Bridge(T(0.1)) @test !hasholes(bpoly) @test nvertices(bpoly) == 15 # unique and bridges - poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) cpoly = poly |> Repair{0}() |> Bridge() - @test cpoly == PolyArea(point.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) + @test cpoly == PolyArea(cart.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) # basic ngon tests - t = Triangle(point(0, 0), point(1, 0), point(0, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test (t |> Bridge() |> boundary) == boundary(t) - q = Quadrangle(point(0, 0), point(1, 0), point(1, 1), point(0, 1)) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test (q |> Bridge() |> boundary) == boundary(q) # bridges between holes - outer = point.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = point.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = point.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) poly = PolyArea([outer, hole1, hole2]) @test vertices(poly) == - point.([ + cart.([ (0, 0), (1, 0), (1, 1), @@ -1860,7 +1860,7 @@ ]) bpoly = poly |> Bridge(T(0.01)) target = - point.([ + cart.([ (-0.0035355339059327372, 0.0035355339059327372), (0.19646446609406729, 0.20353553390593274), (0.2, 0.4), @@ -1880,7 +1880,7 @@ ]) @test all(vertices(bpoly) .≈ target) - poly = Quadrangle(point(0, 1, 0), point(1, 1, 0), point(1, 0, 0), point(0, 0, 0)) + poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) bpoly = poly |> Bridge() @test bpoly isa Quadrangle @test bpoly == poly diff --git a/test/utils.jl b/test/utils.jl index 067deb939..e9c3c16d4 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,7 +1,7 @@ @testset "Utilities" begin - a, b, c = point(0, 0), point(1, 0), point(0, 1) + a, b, c = cart(0, 0), cart(1, 0), cart(0, 1) @test signarea(a, b, c) == T(0.5) * u"m^2" - a, b, c = point(0, 0), point(0, 1), point(1, 0) + a, b, c = cart(0, 0), cart(0, 1), cart(1, 0) @test signarea(a, b, c) == T(-0.5) * u"m^2" normals = [ @@ -33,14 +33,14 @@ @test Meshes.mayberound(1.1, 1.0, 0.05) ≈ 1.1 # intersect parameters - p1, p2 = point(0, 0), point(1, 1) - p3, p4 = point(1, 0), point(0, 1) + p1, p2 = cart(0, 0), cart(1, 1) + p3, p4 = cart(1, 0), cart(0, 1) @inferred Meshes.intersectparameters(p1, p2, p3, p4) @inferred Meshes.intersectparameters(p1, p3, p2, p4) @inferred Meshes.intersectparameters(p1, p2, p1, p2) - p1, p2 = point(0, 0, 0), point(1, 1, 1) - p3, p4 = point(1, 0, 0), point(0, 1, 1) + p1, p2 = cart(0, 0, 0), cart(1, 1, 1) + p3, p4 = cart(1, 0, 0), cart(0, 1, 1) @inferred Meshes.intersectparameters(p1, p2, p3, p4) @inferred Meshes.intersectparameters(p1, p3, p2, p4) @inferred Meshes.intersectparameters(p1, p2, p1, p2) diff --git a/test/viewing.jl b/test/viewing.jl index a6ff6ebd7..79cfebbae 100644 --- a/test/viewing.jl +++ b/test/viewing.jl @@ -7,18 +7,18 @@ @test parentindices(g) == 1:100 g = cartgrid(10, 10) - b = Box(point(1, 1), point(5, 5)) + b = Box(cart(1, 1), cart(5, 5)) v = view(g, b) - @test v == CartesianGrid(point(0, 0), point(6, 6), dims=(6, 6)) + @test v == CartesianGrid(cart(0, 0), cart(6, 6), dims=(6, 6)) p = PointSet(collect(vertices(g))) v = view(p, b) - @test centroid(v, 1) == point(1, 1) - @test centroid(v, nelements(v)) == point(5, 5) + @test centroid(v, 1) == cart(1, 1) + @test centroid(v, nelements(v)) == cart(5, 5) g = cartgrid(10, 10) p = PointSet(collect(vertices(g))) - b = Ball(point(0, 0), T(2)) + b = Ball(cart(0, 0), T(2)) v = view(g, b) @test nelements(v) == 4 @test v[1] == g[1] @@ -27,26 +27,26 @@ @test to.(v) == vector.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)]) # convex polygons - tri = Triangle(point(5, 7), point(10, 12), point(15, 7)) - pent = Pentagon(point(6, 1), point(2, 10), point(10, 16), point(18, 10), point(14, 1)) + tri = Triangle(cart(5, 7), cart(10, 12), cart(15, 7)) + pent = Pentagon(cart(6, 1), cart(2, 10), cart(10, 16), cart(18, 10), cart(14, 1)) grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[10, 10] ∈ indices(grid, tri) @test linds[10, 6] ∈ indices(grid, pent) - grid = CartesianGrid(point(-2, -2), point(20, 20), T.((0.5, 1.5))) + grid = CartesianGrid(cart(-2, -2), cart(20, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[21, 7] ∈ indices(grid, tri) @test linds[21, 4] ∈ indices(grid, pent) - grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) + grid = CartesianGrid(cart(-100, -100), cart(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[57, 54] ∈ indices(grid, tri) @test linds[55, 53] ∈ indices(grid, pent) # non-convex polygons - poly1 = PolyArea(point.([(3, 3), (9, 9), (3, 15), (17, 15), (17, 3)])) + poly1 = PolyArea(cart.([(3, 3), (9, 9), (3, 15), (17, 15), (17, 3)])) poly2 = PolyArea([pointify(pent), pointify(tri)]) grid = cartgrid(20, 20) @@ -54,12 +54,12 @@ @test linds[12, 6] ∈ indices(grid, poly1) @test linds[10, 3] ∈ indices(grid, poly2) - grid = CartesianGrid(point(-2, -2), point(20, 20), T.((0.5, 1.5))) + grid = CartesianGrid(cart(-2, -2), cart(20, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[22, 6] ∈ indices(grid, poly1) @test linds[17, 4] ∈ indices(grid, poly2) - grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) + grid = CartesianGrid(cart(-100, -100), cart(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[57, 54] ∈ indices(grid, poly1) @test linds[55, 53] ∈ indices(grid, poly2) @@ -68,17 +68,17 @@ poly1 = poly1 |> Rotate(Angle2d(T(π / 2))) poly2 = poly2 |> Rotate(Angle2d(T(π / 2))) - grid = CartesianGrid(point(-20, 0), point(0, 20), T.((1, 1))) + grid = CartesianGrid(cart(-20, 0), cart(0, 20), T.((1, 1))) linds = LinearIndices(size(grid)) @test linds[12, 12] ∈ indices(grid, poly1) @test linds[16, 11] ∈ indices(grid, poly2) - grid = CartesianGrid(point(-22, -2), point(0, 20), T.((0.5, 1.5))) + grid = CartesianGrid(cart(-22, -2), cart(0, 20), T.((0.5, 1.5))) linds = LinearIndices(size(grid)) @test linds[26, 8] ∈ indices(grid, poly1) @test linds[36, 9] ∈ indices(grid, poly2) - grid = CartesianGrid(point(-100, -100), point(20, 20), T.((2, 2))) + grid = CartesianGrid(cart(-100, -100), cart(20, 20), T.((2, 2))) linds = LinearIndices(size(grid)) @test linds[46, 57] ∈ indices(grid, poly1) @test linds[48, 55] ∈ indices(grid, poly2) @@ -91,20 +91,20 @@ @test linds[10, 6] ∈ indices(grid, multi) # clipping - tri = Triangle(point(-4, 10), point(5, 19), point(5, 1)) + tri = Triangle(cart(-4, 10), cart(5, 19), cart(5, 1)) grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[3, 10] ∈ indices(grid, tri) # out of grid - tri = Triangle(point(-12, 8), point(-8, 14), point(-4, 8)) + tri = Triangle(cart(-12, 8), cart(-8, 14), cart(-4, 8)) grid = cartgrid(20, 20) @test isempty(indices(grid, tri)) # chain - seg = Segment(point(2, 12), point(16, 18)) - rope = Rope(point(8, 1), point(5, 9), point(9, 13), point(17, 10)) - ring = Ring(point(8, 1), point(5, 9), point(9, 13), point(17, 10)) + seg = Segment(cart(2, 12), cart(16, 18)) + rope = Rope(cart(8, 1), cart(5, 9), cart(9, 13), cart(17, 10)) + ring = Ring(cart(8, 1), cart(5, 9), cart(9, 13), cart(17, 10)) grid = cartgrid(20, 20) linds = LinearIndices(size(grid)) @test linds[9, 15] ∈ indices(grid, seg) @@ -112,12 +112,12 @@ @test linds[12, 5] ∈ indices(grid, ring) # points - p1 = point(0, 0) - p2 = point(0.5, 0.5) - p3 = point(1, 1) - p4 = point(2, 2) - p5 = point(10, 10) - p6 = point(11, 11) + p1 = cart(0, 0) + p2 = cart(0.5, 0.5) + p3 = cart(1, 1) + p4 = cart(2, 2) + p5 = cart(10, 10) + p6 = cart(11, 11) grid = cartgrid(10, 10) linds = LinearIndices(size(grid)) @test linds[1, 1] == only(indices(grid, p1)) diff --git a/test/winding.jl b/test/winding.jl index b6e26445b..77cbcb447 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -1,12 +1,12 @@ @testset "winding" begin - p = point(0.5, 0.5) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1)])) + p = cart(0.5, 0.5) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test winding(p, c) ≈ T(1) @test winding(p, reverse(c)) ≈ T(-1) @test winding([p, p], c) ≈ T[1, 1] - p = point(0.5, 0.5) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) + p = cart(0.5, 0.5) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) @test winding(p, c) ≈ T(2) @test winding(p, reverse(c)) ≈ T(-2) @test winding([p, p], c) ≈ T[2, 2] @@ -19,11 +19,11 @@ # error: both arguments must have the same CRS p = Point(LatLon(T(0.5), T(0.5))) - c = Ring(point.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) @test_throws ArgumentError winding(p, c) - m = boundary(Box(point(0, 0, 0), point(2, 2, 2))) + m = boundary(Box(cart(0, 0, 0), cart(2, 2, 2))) @test all(>(0), winding(vertices(m), m)) - @test isapprox(winding(point(1, 1, 1), m), T(1), atol=atol(T)) - @test isapprox(winding(point(3, 3, 3), m), T(0), atol=atol(T)) + @test isapprox(winding(cart(1, 1, 1), m), T(1), atol=atol(T)) + @test isapprox(winding(cart(3, 3, 3), m), T(0), atol=atol(T)) end From 0c3ceb859a2884c8852ac8c8d993482a555db4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Jul 2024 11:23:12 -0300 Subject: [PATCH 137/423] Rename randpoint --> randcart in test suite --- test/runtests.jl | 6 +++--- test/testutils.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 076866b40..1ce3403d1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -37,9 +37,9 @@ vector(args...) = vector(T, args...) cartgrid(args...) = cartgrid(T, args...) -randpoint1(n) = randpoint(T, 1, n) -randpoint2(n) = randpoint(T, 2, n) -randpoint3(n) = randpoint(T, 3, n) +randpoint1(n) = randcart(T, 1, n) +randpoint2(n) = randcart(T, 2, n) +randpoint3(n) = randcart(T, 3, n) # list of tests testfiles = [ diff --git a/test/testutils.jl b/test/testutils.jl index 10bfb4562..787d2f78d 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -55,7 +55,7 @@ function cartgrid(T::Type, dims::Dims{Dim}) where {Dim} CartesianGrid(dims, origin, spacing, offset) end -randpoint(T, Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] +randcart(T, Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] numconvert(T, x::Quantity{S,D,U}) where {S,D,U} = convert(Quantity{T,D,U}, x) From 23da29c3b37f2be3369bdb2862409647c0498991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Jul 2024 11:46:15 -0300 Subject: [PATCH 138/423] Add latlon helper in test suite --- test/predicates.jl | 20 ++++++++++---------- test/runtests.jl | 2 ++ test/sideof.jl | 4 ++-- test/testutils.jl | 3 +++ test/transforms.jl | 2 +- test/winding.jl | 6 +++--- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/test/predicates.jl b/test/predicates.jl index f6dc3f42b..59f370ad4 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -166,18 +166,18 @@ @test cart(-1, 0, 0) ∉ h @test cart(0, 2, 0) ∉ h - outer = Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))]) - hole1 = Point.([LatLon(T(0.2), T(0.2)), LatLon(T(0.4), T(0.2)), LatLon(T(0.4), T(0.4)), LatLon(T(0.2), T(0.4))]) - hole2 = Point.([LatLon(T(0.6), T(0.2)), LatLon(T(0.8), T(0.2)), LatLon(T(0.8), T(0.4)), LatLon(T(0.6), T(0.4))]) + outer = [latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)] + hole1 = [latlon(0.2, 0.2), latlon(0.4, 0.2), latlon(0.4, 0.4), latlon(0.2, 0.4)] + hole2 = [latlon(0.6, 0.2), latlon(0.8, 0.2), latlon(0.8, 0.4), latlon(0.6, 0.4)] poly = PolyArea([outer, hole1, hole2]) @test all(p ∈ poly for p in outer) - @test Point(LatLon(T(0.5), T(0.5))) ∈ poly - @test Point(LatLon(T(0.2), T(0.6))) ∈ poly - @test Point(LatLon(T(1.5), T(0.5))) ∉ poly - @test Point(LatLon(T(-0.5), T(0.5))) ∉ poly - @test Point(LatLon(T(0.25), T(0.25))) ∉ poly - @test Point(LatLon(T(0.75), T(0.25))) ∉ poly - @test Point(LatLon(T(0.75), T(0.75))) ∈ poly + @test latlon(0.5, 0.5) ∈ poly + @test latlon(0.2, 0.6) ∈ poly + @test latlon(1.5, 0.5) ∉ poly + @test latlon(-0.5, 0.5) ∉ poly + @test latlon(0.25, 0.25) ∉ poly + @test latlon(0.75, 0.25) ∉ poly + @test latlon(0.75, 0.75) ∈ poly end @testset "issubset" begin diff --git a/test/runtests.jl b/test/runtests.jl index 1ce3403d1..9bd276c83 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,6 +33,8 @@ include("testutils.jl") cart(args...) = cart(T, args...) +latlon(args...) = latlon(T, args...) + vector(args...) = vector(T, args...) cartgrid(args...) = cartgrid(T, args...) diff --git a/test/sideof.jl b/test/sideof.jl index 9cf4c3c9b..2aa42e43a 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -15,8 +15,8 @@ pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, IN] - p1, p2, p3 = Point(LatLon(T(0.5), T(0.5))), Point(LatLon(T(1.5), T(0.5))), Point(LatLon(T(1), T(1))) - c = Ring(Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))])) + p1, p2, p3 = latlon(0.5, 0.5), latlon(1.5, 0.5), latlon(1, 1) + c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT @test sideof(p3, c) == IN diff --git a/test/testutils.jl b/test/testutils.jl index 787d2f78d..d6627b8a6 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -44,6 +44,9 @@ end cart(T::Type, coords...) = cart(T, coords) cart(T::Type, coords::Tuple) = Point(T.(coords)) +latlon(T::Type, coords...) = latlon(T, coords) +latlon(T::Type, coords::Tuple) = Point(LatLon(T.(coords)...)) + vector(T::Type, coords...) = vector(T, coords) vector(T::Type, coords::Tuple) = Vec(T.(coords)) diff --git a/test/transforms.jl b/test/transforms.jl index e3b08ab4f..c5fb14607 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1133,7 +1133,7 @@ @test r ≈ cart(1, 1) f = FlatCoords() - g = Point(LatLon(T(30), T(60))) + g = latlon(30, 60) r, c = TB.apply(f, g) @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) diff --git a/test/winding.jl b/test/winding.jl index 77cbcb447..71268a9d8 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -11,14 +11,14 @@ @test winding(p, reverse(c)) ≈ T(-2) @test winding([p, p], c) ≈ T[2, 2] - p = Point(LatLon(T(0.5), T(0.5))) - c = Ring(Point.([LatLon(T(0), T(0)), LatLon(T(1), T(0)), LatLon(T(1), T(1)), LatLon(T(0), T(1))])) + p = latlon(0.5, 0.5) + c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) @test winding(p, c) ≈ T(1) @test winding(p, reverse(c)) ≈ T(-1) @test winding([p, p], c) ≈ T[1, 1] # error: both arguments must have the same CRS - p = Point(LatLon(T(0.5), T(0.5))) + p = latlon(0.5, 0.5) c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) @test_throws ArgumentError winding(p, c) From 2794c0dc4ac1a719102a7015fd09f947bad74fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 1 Jul 2024 12:07:16 -0300 Subject: [PATCH 139/423] Refactor winding.jl --- src/winding.jl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/winding.jl b/src/winding.jl index 6c3ea2bfc..bd15e620e 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -18,9 +18,16 @@ Generalized winding number of `points` with respect to the geometric `object`. """ function winding end +# ------ +# RINGS +# ------ + winding(points, ring::Ring) = _winding(_coords(points, ring), points, ring) -function _winding(::Cartesian{Datum,2}, points, ring) where {Datum} +winding(point::Point, ring::Ring) = winding((point,), ring) |> first + +# Cartesian coordinates are easy +function _winding(::Cartesian, points, ring) v = vertices(ring) n = nvertices(ring) @@ -32,17 +39,25 @@ function _winding(::Cartesian{Datum,2}, points, ring) where {Datum} tcollect(w(p) for p in points) end -_winding(::CRS, points, ring) = winding(map(FlatCoords(), points), ring |> FlatCoords()) +# flatten CRS to Cartesian in general case +function _winding(::CRS, points, ring) + flat = FlatCoords() + fpts = map(flat, points) + fring = ring |> flat + winding(fpts, fring) +end function _coords(points, ring) p = first(points) if crs(p) !== crs(ring) - throw(ArgumentError("both arguments must have the same CRS")) + throw(ArgumentError("arguments must have the same CRS")) end coords(p) end -winding(point::Point, ring::Ring) = winding((point,), ring) |> first +# ------- +# MESHES +# ------- # Jacobson et al 2013. function winding(points, mesh::Mesh{3}) From c99de936d99a4258b36f700428922d4a0c63436f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:21:27 -0300 Subject: [PATCH 140/423] :robot: Format .jl files (#914) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- test/clipping.jl | 30 ++++++------------------------ test/hulls.jl | 3 +-- test/intersections.jl | 16 +++------------- test/sampling.jl | 3 +-- 4 files changed, 11 insertions(+), 41 deletions(-) diff --git a/test/clipping.jl b/test/clipping.jl index 159364bb1..53d727a38 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -8,23 +8,13 @@ @test all(vertices(clipped) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) # octagon - poly = - Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) clipped = clip(poly, other, SutherlandHodgman()) @test !issimple(clipped) @test all( - vertices(clipped) .≈ [ - cart(3, 4), - cart(4, 3), - cart(5, 3), - cart(5, 2), - cart(4, 1), - cart(2, 1), - cart(2, 0), - cart(5, 0), - cart(5, 4) - ] + vertices(clipped) .≈ + [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] ) # inside @@ -48,8 +38,7 @@ @test all(vertices(clipped) .≈ vertices(other)) # PolyArea with box - outer = - Ring(cart(8, 0), cart(4, 8), cart(2, 8), cart(-2, 0), cart(0, 0), cart(1, 2), cart(5, 2), cart(6, 0)) + outer = Ring(cart(8, 0), cart(4, 8), cart(2, 8), cart(-2, 0), cart(0, 0), cart(1, 2), cart(5, 2), cart(6, 0)) inner = Ring(cart(4, 4), cart(2, 4), cart(3, 6)) poly = PolyArea([outer, inner]) other = Box(cart(0, 1), cart(3, 7)) @@ -57,15 +46,8 @@ crings = rings(clipped) @test !issimple(clipped) @test all( - vertices(crings[1]) .≈ [ - cart(1.5, 7.0), - cart(0.0, 4.0), - cart(0.0, 1.0), - cart(0.5, 1.0), - cart(1.0, 2.0), - cart(3.0, 2.0), - cart(3.0, 7.0) - ] + vertices(crings[1]) .≈ + [cart(1.5, 7.0), cart(0.0, 4.0), cart(0.0, 1.0), cart(0.5, 1.0), cart(1.0, 2.0), cart(3.0, 2.0), cart(3.0, 7.0)] ) @test all(vertices(crings[2]) .≈ [cart(3.0, 4.0), cart(2.0, 4.0), cart(3.0, 6.0)]) diff --git a/test/hulls.jl b/test/hulls.jl index f69d0be05..ce3c2b194 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -156,8 +156,7 @@ b1 = Box(cart(0, 0), cart(1, 1)) b2 = Box(cart(-1, -1), cart(0.5, 0.5)) @test convexhull(Multi([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) - @test convexhull(GeometrySet([b1, b2])) == - PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + @test convexhull(GeometrySet([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) b1 = Ball(cart(0, 0), T(1)) b2 = Box(cart(-1, -1), cart(0, 0)) diff --git a/test/intersections.jl b/test/intersections.jl index c8f318e1b..64097b9ec 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1147,22 +1147,12 @@ @test all(vertices(poly ∩ other) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) # octagon - poly = - Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) @test intersection(poly, other) |> type == Intersecting @test all( - vertices(poly ∩ other) .≈ [ - cart(3, 4), - cart(4, 3), - cart(5, 3), - cart(5, 2), - cart(4, 1), - cart(2, 1), - cart(2, 0), - cart(5, 0), - cart(5, 4) - ] + vertices(poly ∩ other) .≈ + [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] ) # inside diff --git a/test/sampling.jl b/test/sampling.jl index 79ef7eb6d..62146548f 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -254,8 +254,7 @@ cart(0, 1, 1) ) ps = sample(h, RegularSampling(2, 2, 2)) - @test collect(ps) == - cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) + @test collect(ps) == cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) ps = sample(h, RegularSampling(3, 2, 2)) @test collect(ps) == cart.([ From 7f8d398f03fde426eaf4a5697c274c6168d6b7c2 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:25:50 -0300 Subject: [PATCH 141/423] Add `Overlaps` transform (#915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'Within' transform * Apply suggestions * Add docstring * Add to documentation * Add tests * Update tests * Update src/transforms/within.jl Co-authored-by: Júlio Hoffimann * Apply suggestions * Apply suggestions * Fix typo --------- Co-authored-by: Júlio Hoffimann --- docs/src/transforms.md | 16 +++++++++ src/Meshes.jl | 3 +- src/transforms.jl | 1 + src/transforms/overlaps.jl | 61 ++++++++++++++++++++++++++++++++++ test/transforms.jl | 68 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/transforms/overlaps.jl diff --git a/docs/src/transforms.md b/docs/src/transforms.md index b137692a1..8b85e0b80 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -189,6 +189,22 @@ viz(fig[1,2], disk) fig ``` +## Overlaps + +```@docs +Overlaps +``` + +```@example transforms +grid = CartesianGrid(10, 10) +subgrid = grid |> Overlaps(x=(1.5, 6.5), y=(3.5, 8.5)) + +fig = Mke.Figure(size = (800, 400)) +viz(fig[1,1], grid) +viz(fig[1,2], subgrid) +fig +``` + ## Repair ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index f84b991bc..1f0eb1ddc 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -42,7 +42,7 @@ import NearestNeighbors: MinkowskiMetric import TransformsBase: Transform, → import TransformsBase: isrevertible, isinvertible import TransformsBase: apply, revert, reapply, inverse -import TransformsBase: parameters +import TransformsBase: parameters, preprocess # CoordRefSystems API import CoordRefSystems: lentype @@ -518,6 +518,7 @@ export Proj, LengthUnit, Shadow, + Overlaps, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 2348be191..949293655 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -99,6 +99,7 @@ include("transforms/flatcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") +include("transforms/overlaps.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/overlaps.jl b/src/transforms/overlaps.jl new file mode 100644 index 000000000..199efa50e --- /dev/null +++ b/src/transforms/overlaps.jl @@ -0,0 +1,61 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + Overlaps(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) + +Retain the domain geometries that overlap with `x` limits [`xmax`,`xmax`], +`y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units +(default to meters). + +## Examples + +```julia +Overlaps(x=(2, 4)) +Overlaps(x=(1u"km", 3u"km")) +Overlaps(y=(1.2, 1.8), z=(2.4, 3.0)) +``` +""" +struct Overlaps{X,Y,Z} <: GeometricTransform + x::X + y::Y + z::Z +end + +Overlaps(; x=nothing, y=nothing, z=nothing) = + Overlaps(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) + +parameters(t::Overlaps) = (; x=t.x, y=t.y, z=t.z) + +function preprocess(t::Overlaps, d::Domain) + bbox = boundingbox(d) + bbox₁ = _overlaps(1, t.x, bbox) + bbox₂ = _overlaps(2, t.y, bbox₁) + bbox₃ = _overlaps(3, t.z, bbox₂) + indices(d, bbox₃) +end + +function apply(t::Overlaps, d::Domain) + inds = preprocess(t, d) + n = view(d, inds) + n, nothing +end + +_aslen(x::Len) = float(x) +_aslen(x::Number) = float(x) * u"m" +_aslen(::Quantity) = throw(ArgumentError("invalid units, please check the documentation")) + +function _overlaps(dim, lims, bbox) + Dim = embeddim(bbox) + if Dim < dim || isnothing(lims) + bbox + else + lmin, lmax = lims + min = to(minimum(bbox)) + max = to(maximum(bbox)) + nmin = Vec(ntuple(i -> i == dim ? lmin : min[i], Dim)) + nmax = Vec(ntuple(i -> i == dim ? lmax : max[i], Dim)) + Box(withdatum(bbox, nmin), withdatum(bbox, nmax)) + end +end diff --git a/test/transforms.jl b/test/transforms.jl index c5fb14607..8eae5579c 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1730,6 +1730,74 @@ @test r == SimpleMesh(f.(vertices(d)), topology(d)) end + @testset "Overlaps" begin + @test !isaffine(Overlaps(x=(T(2), T(4)))) + @test !TB.isrevertible(Overlaps(x=(T(2), T(4)))) + @test !TB.isinvertible(Overlaps(x=(T(2), T(4)))) + @test TB.parameters(Overlaps(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) + @test TB.parameters(Overlaps(y=(T(2) * u"km", T(4) * u"km"))) == + (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) + @test TB.parameters(Overlaps(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) + @test_throws ArgumentError Overlaps(x=(T(2) * u"°", T(4) * u"°")) + + # --------- + # POINTSET + # --------- + + f = Overlaps(x=(T(1.5), T(3.5))) + d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) + r, c = TB.apply(f, d) + @test r == PointSet([cart(2, 1), cart(3, 1)]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Overlaps(x=(T(1.5), T(3.5))) + t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + t2 = t1 |> Translate(T(2), T(2)) + t3 = t2 |> Translate(T(2), T(2)) + d = GeometrySet([t1, t2, t3]) + r, c = TB.apply(f, d) + @test r == GeometrySet([t2]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Overlaps(z=(T(1.5), T(4.5))) + d = cartgrid(10, 10, 10) + r, c = TB.apply(f, d) + @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Overlaps(y=(T(3.5), T(6.5))) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Overlaps(x=(T(5.5), T(8.5))) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Overlaps(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) + d = convert(SimpleMesh, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) + end + @testset "Repair{0}" begin @test !isaffine(Repair) poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) From 0aa702e6f4e2de915bf726a636cee7155c4d9768 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Tue, 2 Jul 2024 12:38:51 +0200 Subject: [PATCH 142/423] update _winding for crs to reduce allocations (#916) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update _winding for crs * Refactor test * Fix typo in test --------- Co-authored-by: Júlio Hoffimann --- src/winding.jl | 12 +++++++++--- test/winding.jl | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/winding.jl b/src/winding.jl index bd15e620e..0aea66e35 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -42,9 +42,15 @@ end # flatten CRS to Cartesian in general case function _winding(::CRS, points, ring) flat = FlatCoords() - fpts = map(flat, points) - fring = ring |> flat - winding(fpts, fring) + v = vertices(ring) + n = nvertices(ring) + + function w(p) + ∑ = sum(∠(flat(v[i]), flat(p), flat(v[i + 1])) for i in 1:n) + ∑ / oftype(∑, 2π) + end + + tcollect(w(p) for p in points) end function _coords(points, ring) diff --git a/test/winding.jl b/test/winding.jl index 71268a9d8..67f3a9f42 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -10,12 +10,19 @@ @test winding(p, c) ≈ T(2) @test winding(p, reverse(c)) ≈ T(-2) @test winding([p, p], c) ≈ T[2, 2] + # record allocations for cartesian + alloccart = @allocated winding(p, c) p = latlon(0.5, 0.5) c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) @test winding(p, c) ≈ T(1) @test winding(p, reverse(c)) ≈ T(-1) @test winding([p, p], c) ≈ T[1, 1] + # record allocations for latlon + alloclatlon = @allocated winding(p, c) + + # exact same memory allocations + @test alloccart == alloclatlon # error: both arguments must have the same CRS p = latlon(0.5, 0.5) From a5a0c08fec511e97b8934cd5895c2aaf561f3d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 08:32:07 -0300 Subject: [PATCH 143/423] Replace FlatCoords by flat util --- docs/src/transforms.md | 13 ------ src/Meshes.jl | 1 - src/transforms.jl | 1 - src/transforms/flatcoords.jl | 27 ------------- src/utils.jl | 13 ++++++ src/winding.jl | 1 - test/transforms.jl | 77 ------------------------------------ 7 files changed, 13 insertions(+), 120 deletions(-) delete mode 100644 src/transforms/flatcoords.jl diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 8b85e0b80..cd26b86e4 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -129,19 +129,6 @@ viz(fig[1,2], mesh) fig ``` -## FlatCoords - -```@docs -FlatCoords -``` - -```@example transforms -point = Point(LatLon(30, 60)) -triangle = Triangle(Point(LatLon(30, 60)), Point(LatLon(30, 61)), Point(LatLon(31, 60))) - -[point, triangle] |> FlatCoords() -``` - ## Proj ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index 1f0eb1ddc..23a878eb7 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -514,7 +514,6 @@ export Affine, Stretch, StdCoords, - FlatCoords, Proj, LengthUnit, Shadow, diff --git a/src/transforms.jl b/src/transforms.jl index 949293655..f3d71cb63 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -95,7 +95,6 @@ include("transforms/scale.jl") include("transforms/affine.jl") include("transforms/stretch.jl") include("transforms/stdcoords.jl") -include("transforms/flatcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") diff --git a/src/transforms/flatcoords.jl b/src/transforms/flatcoords.jl deleted file mode 100644 index baf84f601..000000000 --- a/src/transforms/flatcoords.jl +++ /dev/null @@ -1,27 +0,0 @@ -# ------------------------------------------------------------------ -# Licensed under the MIT License. See LICENSE in the project root. -# ------------------------------------------------------------------ - -""" - FlatCoords() - -Flatten the coordinates of a geometry or domain to 2D Cartesian coordinates when possible. -""" -struct FlatCoords <: CoordinateTransform end - -applycoord(::FlatCoords, v::Vec) = v - -applycoord(::FlatCoords, p::Point) = Point(_flatcoords(coords(p))) - -function _flatcoords(coords::CRS) - if CoordRefSystems.ncoords(coords) ≠ 2 - throw(ArgumentError("points must have 2 coordinates")) - end - convert(Cartesian, coords) -end - -_flatcoords(coords::LatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) - -_flatcoords(coords::GeocentricLatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) - -_flatcoords(coords::AuthalicLatLon) = Cartesian{datum(coords)}(CoordRefSystems.rawvalues(coords)) diff --git a/src/utils.jl b/src/utils.jl index 5d7571dbb..000f3739b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -49,6 +49,19 @@ Point at the end of the vector `v` with the same datum of `g`. """ withdatum(g::GeometryOrDomain, v::StaticVector) = Point(Cartesian{datum(crs(g))}(Tuple(v))) +""" + flat(p) + +Flatten coordinates of point `p` to Cartesian coordinates, +ignoring the original units of the coordinate reference system. +""" +flat(p::Point) = Point(flat(coords(p))) +flat(c::GeodeticLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) +flat(c::GeocentricLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) +flat(c::AuthalicLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) +flat(c::CRS) = convert(Cartesian, c) + + """ signarea(A, B, C) diff --git a/src/winding.jl b/src/winding.jl index 0aea66e35..64d1514b2 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -41,7 +41,6 @@ end # flatten CRS to Cartesian in general case function _winding(::CRS, points, ring) - flat = FlatCoords() v = vertices(ring) n = nvertices(ring) diff --git a/test/transforms.jl b/test/transforms.jl index 8eae5579c..2e190db87 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1104,83 +1104,6 @@ @test r == r2 end - @testset "FlatCoords" begin - @test !isaffine(FlatCoords()) - @test !TB.isrevertible(FlatCoords()) - @test !TB.isinvertible(FlatCoords()) - - # ---- - # VEC - # ---- - - f = FlatCoords() - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r == v - - # ------ - # POINT - # ------ - - f = FlatCoords() - g = cart(1, 1) - r, c = TB.apply(f, g) - @test r == cart(1, 1) - - f = FlatCoords() - g = Point(Polar(T(√2), T(π / 4))) - r, c = TB.apply(f, g) - @test r ≈ cart(1, 1) - - f = FlatCoords() - g = latlon(30, 60) - r, c = TB.apply(f, g) - @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) - - f = FlatCoords() - g = Point(GeocentricLatLon(T(30), T(60))) - r, c = TB.apply(f, g) - @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) - - f = FlatCoords() - g = Point(AuthalicLatLon(T(30), T(60))) - r, c = TB.apply(f, g) - @test r == Point(Cartesian{WGS84Latest}(T(30), T(60))) - - f = FlatCoords() - g = Point(Mercator(T(1), T(1))) - r, c = TB.apply(f, g) - @test r == Point(Cartesian{WGS84Latest}(T(1), T(1))) - - f = FlatCoords() - g = cart(1, 1, 1) - # error: points must have 2 coordinates - @test_throws ArgumentError TB.apply(f, g) - - # ---------- - # ROPE/RING - # ---------- - - f = FlatCoords() - g = Rope(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) - r, c = TB.apply(f, g) - @test r ≈ Rope(cart(0, 0), cart(1, 0), cart(0, 1)) - - f = FlatCoords() - g = Ring(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) - r, c = TB.apply(f, g) - @test r ≈ Ring(cart(0, 0), cart(1, 0), cart(0, 1)) - - # --------- - # TRIANGLE - # --------- - - f = FlatCoords() - g = Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(1), T(π / 2)))) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - end - @testset "Proj" begin @test !isaffine(Proj(Polar)) @test !TB.isrevertible(Proj(Polar)) From 2a01f02866fe442b5d5cb72afc6e68f679f10b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 08:42:14 -0300 Subject: [PATCH 144/423] Refactor flat util --- src/utils.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 000f3739b..62a517407 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -56,12 +56,9 @@ Flatten coordinates of point `p` to Cartesian coordinates, ignoring the original units of the coordinate reference system. """ flat(p::Point) = Point(flat(coords(p))) -flat(c::GeodeticLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) -flat(c::GeocentricLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) -flat(c::AuthalicLatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) +flat(c::LatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) flat(c::CRS) = convert(Cartesian, c) - """ signarea(A, B, C) From a8f0d2369f29ee9adafa377c30ed179d7dad9426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 08:42:21 -0300 Subject: [PATCH 145/423] Refactor winding.jl --- src/winding.jl | 32 ++++---------------------------- test/winding.jl | 5 ----- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/winding.jl b/src/winding.jl index 64d1514b2..9d51e9ac1 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -22,43 +22,19 @@ function winding end # RINGS # ------ -winding(points, ring::Ring) = _winding(_coords(points, ring), points, ring) - -winding(point::Point, ring::Ring) = winding((point,), ring) |> first - -# Cartesian coordinates are easy -function _winding(::Cartesian, points, ring) +function winding(points, ring::Ring) v = vertices(ring) n = nvertices(ring) function w(p) - ∑ = sum(∠(v[i], p, v[i + 1]) for i in 1:n) - ∑ / oftype(∑, 2π) + Σ = sum(∠(flat(v[i]), flat(p), flat(v[i + 1])) for i in 1:n) + Σ / oftype(Σ, 2π) end tcollect(w(p) for p in points) end -# flatten CRS to Cartesian in general case -function _winding(::CRS, points, ring) - v = vertices(ring) - n = nvertices(ring) - - function w(p) - ∑ = sum(∠(flat(v[i]), flat(p), flat(v[i + 1])) for i in 1:n) - ∑ / oftype(∑, 2π) - end - - tcollect(w(p) for p in points) -end - -function _coords(points, ring) - p = first(points) - if crs(p) !== crs(ring) - throw(ArgumentError("arguments must have the same CRS")) - end - coords(p) -end +winding(point::Point, ring::Ring) = winding((point,), ring) |> first # ------- # MESHES diff --git a/test/winding.jl b/test/winding.jl index 67f3a9f42..5ba4f2dfb 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -24,11 +24,6 @@ # exact same memory allocations @test alloccart == alloclatlon - # error: both arguments must have the same CRS - p = latlon(0.5, 0.5) - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)])) - @test_throws ArgumentError winding(p, c) - m = boundary(Box(cart(0, 0, 0), cart(2, 2, 2))) @test all(>(0), winding(vertices(m), m)) @test isapprox(winding(cart(1, 1, 1), m), T(1), atol=atol(T)) From aa9ce4487de553b84413e4ebaa8603473dcb9b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 10:55:29 -0300 Subject: [PATCH 146/423] Implement Hao et al point in poly algorithm --- src/predicates/in.jl | 2 +- src/sideof.jl | 97 ++++++++++++++++++++++++++++++++++++++++---- test/sideof.jl | 8 ++-- 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 2e8c7adb9..6e1a4dd7a 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -158,7 +158,7 @@ Base.in(p::Point, ngon::Ngon) = any(Δ -> p ∈ Δ, simplexify(ngon)) function Base.in(p::Point, poly::PolyArea) r = rings(poly) - inside = sideof(p, first(r)) == IN + inside = sideof(p, first(r)) != OUT if hasholes(poly) outside = all(sideof(p, r[i]) == OUT for i in 2:length(r)) inside && outside diff --git a/src/sideof.jl b/src/sideof.jl index f9096a434..89a846fd5 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -39,7 +39,7 @@ Possible results are `LEFT`, `RIGHT` or `ON` the `line`. * Assumes the orientation of `Segment(line(0), line(1))`. """ -function sideof(point::Point{2}, line::Line{2}) +function sideof(point::Point, line::Line) a = signarea(point, line(0), line(1)) ifelse(a > atol(a), LEFT, ifelse(a < -atol(a), RIGHT, ON)) end @@ -48,9 +48,89 @@ end sideof(point, ring) Determines on which side the `point` is in relation to the `ring`. -Possible results are `IN` or `OUT` the `ring`. +Possible results are `IN`, `OUT` or `ON` the `ring`. + +## References + +* Hao et al. 2018. [Optimal Reliable Point-in-Polygon Test and + Differential Coding Boolean Operations on Polygons] + (https://www.mdpi.com/2073-8994/10/10/477) """ -sideof(point::Point, ring::Ring) = ifelse(isapproxzero(winding(point, ring)), OUT, IN) +function sideof(point::Point, ring::Ring) + v = vertices(ring) + n = nvertices(ring) + + # flat coordinates of query point + p = flat(coords(point)) + xₚ, yₚ = p.x, p.y + + k = 0 + for i in 1:n + # flat coordinates of segment i -- i+1 + pᵢ = flat(coords(v[i])) + pⱼ = flat(coords(v[i+1])) + xᵢ, yᵢ = pᵢ.x, pᵢ.y + xⱼ, yⱼ = pⱼ.x, pⱼ.y + + v₁ = yᵢ - yₚ + v₂ = yⱼ - yₚ + + if (isnegative(v₁) && isnegative(v₂)) || (ispositive(v₁) && ispositive(v₂)) + # case 11, 26 + continue + end + + u₁ = xᵢ - xₚ + u₂ = xⱼ - xₚ + + if ispositive(v₂) && isnonpositive(v₁) + # case 3, 9, 16, 21, 13, 24 + f = u₁ * v₂ - u₂ * v₁ + if ispositive(f) + # case 3, 9 + k += 1 + elseif f == zero(f) + # case 16, 21 + return ON + end + elseif ispositive(v₁) && isnonpositive(v₂) + # case 4, 10, 19, 20, 12, 25 + f = u₁ * v₂ - u₂ * v₁ + if isnegative(f) + # case 4, 10 + k += 1 + elseif f == zero(f) + # case 19, 20 + return ON + end + elseif v₂ == zero(v₂) && isnegative(v₁) + # case 7, 14, 17 + f = u₁ * v₂ - u₂ * v₁ + if f == zero(f) + # case 17 + return ON + end + elseif v₁ == zero(v₁) && isnegative(v₂) + # case 8, 15, 18 + f = u₁ * v₂ - u₂ * v₁ + if f == zero(f) + # case 18 + return ON + end + elseif v₁ == zero(v₁) && v₂ == zero(v₂) + # case 1, 2, 5, 6, 22, 23 + if isnonpositive(u₂) && isnonnegative(u₁) + # case 1 + return ON + elseif isnonpositive(u₁) && isnonnegative(u₂) + # case 2 + return ON + end + end + end + + iseven(k) ? OUT : IN +end # ----- # MESH @@ -62,20 +142,23 @@ sideof(point::Point, ring::Ring) = ifelse(isapproxzero(winding(point, ring)), OU Determines on which side the `point` is in relation to the surface `mesh`. Possible results are `IN` or `OUT` the `mesh`. """ -sideof(point::Point{3}, mesh::Mesh{3}) = sideof((point,), mesh) |> first +sideof(point::Point, mesh::Mesh) = sideof((point,), mesh) |> first # ---------- # FALLBACKS # ---------- -sideof(points, line::Line{2}) = map(point -> sideof(point, line), points) +sideof(points, line::Line) = map(point -> sideof(point, line), points) function sideof(points, object::GeometryOrDomain) bbox = boundingbox(object) isin = tcollect(point ∈ bbox for point in points) inds = findall(isin) - wind = winding(collectat(points, inds), object) side = fill(OUT, length(isin)) - side[inds] .= map(w -> ifelse(isapproxzero(w), OUT, IN), wind) + side[inds] .= sidewithinbox(collectat(points, inds), object) side end + +sidewithinbox(points, ring::Ring) = map(point -> sideof(point, ring), points) + +sidewithinbox(points, mesh::Mesh) = map(w -> ifelse(isapproxzero(w), OUT, IN), winding(points, mesh)) diff --git a/test/sideof.jl b/test/sideof.jl index 2aa42e43a..a1bd2eda0 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -11,17 +11,17 @@ c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT - @test sideof(p3, c) == IN + @test sideof(p3, c) == ON pts = [p1, p2, p3] - @test sideof(pts, c) == [IN, OUT, IN] + @test sideof(pts, c) == [IN, OUT, ON] p1, p2, p3 = latlon(0.5, 0.5), latlon(1.5, 0.5), latlon(1, 1) c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT - @test sideof(p3, c) == IN + @test sideof(p3, c) == ON pts = [p1, p2, p3] - @test sideof(pts, c) == [IN, OUT, IN] + @test sideof(pts, c) == [IN, OUT, ON] points = cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.25, 0.25, 1)]) connec = connect.([(1, 3, 2), (1, 2, 4), (1, 4, 3), (2, 3, 4)], Triangle) From eeba574c5a1ab08d6f32a9e2a46ee06b3f42f6a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:23:14 -0300 Subject: [PATCH 147/423] :robot: Format .jl files (#918) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/sideof.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sideof.jl b/src/sideof.jl index 89a846fd5..46f938d01 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -68,7 +68,7 @@ function sideof(point::Point, ring::Ring) for i in 1:n # flat coordinates of segment i -- i+1 pᵢ = flat(coords(v[i])) - pⱼ = flat(coords(v[i+1])) + pⱼ = flat(coords(v[i + 1])) xᵢ, yᵢ = pᵢ.x, pᵢ.y xⱼ, yⱼ = pⱼ.x, pⱼ.y From 54868dcb1a58413980b754e2e9131895368719d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 11:33:05 -0300 Subject: [PATCH 148/423] Refactor in.jl --- src/predicates/in.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 6e1a4dd7a..073243263 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -156,11 +156,11 @@ end Base.in(p::Point, ngon::Ngon) = any(Δ -> p ∈ Δ, simplexify(ngon)) -function Base.in(p::Point, poly::PolyArea) +function Base.in(point::Point, poly::Polygon) r = rings(poly) - inside = sideof(p, first(r)) != OUT + inside = sideof(point, first(r)) != OUT if hasholes(poly) - outside = all(sideof(p, r[i]) == OUT for i in 2:length(r)) + outside = all(sideof(point, r[i]) == OUT for i in 2:length(r)) inside && outside else inside From 9bfa87f931a65a5ba4c4563156299197d0377ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 12:00:00 -0300 Subject: [PATCH 149/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 943620255..06daca723 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.45.2" +version = "0.46.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 966834533be035d6ecbd727ac7972ac236d4db5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 2 Jul 2024 14:22:13 -0300 Subject: [PATCH 150/423] Refactor sideof with isequalzero --- src/sideof.jl | 14 +++++++------- src/utils.jl | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sideof.jl b/src/sideof.jl index 46f938d01..7a3a3520a 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -89,7 +89,7 @@ function sideof(point::Point, ring::Ring) if ispositive(f) # case 3, 9 k += 1 - elseif f == zero(f) + elseif isequalzero(f) # case 16, 21 return ON end @@ -99,25 +99,25 @@ function sideof(point::Point, ring::Ring) if isnegative(f) # case 4, 10 k += 1 - elseif f == zero(f) + elseif isequalzero(f) # case 19, 20 return ON end - elseif v₂ == zero(v₂) && isnegative(v₁) + elseif isequalzero(v₂) && isnegative(v₁) # case 7, 14, 17 f = u₁ * v₂ - u₂ * v₁ - if f == zero(f) + if isequalzero(f) # case 17 return ON end - elseif v₁ == zero(v₁) && isnegative(v₂) + elseif isequalzero(v₁) && isnegative(v₂) # case 8, 15, 18 f = u₁ * v₂ - u₂ * v₁ - if f == zero(f) + if isequalzero(f) # case 18 return ON end - elseif v₁ == zero(v₁) && v₂ == zero(v₂) + elseif isequalzero(v₁) && isequalzero(v₂) # case 1, 2, 5, 6, 22, 23 if isnonpositive(u₂) && isnonnegative(u₁) # case 1 diff --git a/src/utils.jl b/src/utils.jl index 62a517407..a1b6d04eb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -188,6 +188,9 @@ Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. Expr(:tuple, exprs...) end +isequalzero(x) = x == zero(x) +isequalone(x) = x == oneunit(x) + isapproxequal(x, y; atol=atol(x), kwargs...) = isapprox(x, y; atol, kwargs...) isapproxzero(x; atol=atol(x), kwargs...) = isapprox(x, zero(x); atol, kwargs...) isapproxone(x; atol=atol(x), kwargs...) = isapprox(x, oneunit(x); atol, kwargs...) From 4911a6495d09164172f6aa904508ca3f0317783b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:42:57 -0300 Subject: [PATCH 151/423] Add support for mixed units in 'CartesianGrid' constructor (#922) --- src/mesh/cartesiangrid.jl | 35 ++++++++++++++++++++++++++--------- test/mesh.jl | 8 ++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index cdb4321b0..57dfadb85 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -69,13 +69,24 @@ struct CartesianGrid{Dim,C<:CRS,ℒ<:Len} <: Grid{Dim,C} end end -CartesianGrid(origin::Point{Dim}, spacing::NTuple{Dim}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = - CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) +CartesianGrid( + origin::Point{Dim}, + spacing::NTuple{Dim,Len}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim} = CartesianGrid(origin, promote(spacing...), offset, topology) + +CartesianGrid( + origin::Point{Dim}, + spacing::NTuple{Dim,Number}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) function CartesianGrid( dims::Dims{Dim}, origin::Point{Dim}, - spacing::NTuple{Dim}, + spacing::NTuple{Dim,Number}, offset::Dims{Dim}=ntuple(i -> 1, Dim) ) where {Dim} if !all(>(0), dims) @@ -86,8 +97,8 @@ end CartesianGrid( dims::Dims{Dim}, - origin::NTuple{Dim}, - spacing::NTuple{Dim}, + origin::NTuple{Dim,Number}, + spacing::NTuple{Dim,Number}, offset::Dims{Dim}=ntuple(i -> 1, Dim) ) where {Dim} = CartesianGrid(dims, Point(origin), spacing, offset) @@ -98,10 +109,13 @@ function CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Di CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim}) where {Dim} = +CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,Len}) where {Dim} = + CartesianGrid(start, finish, promote(spacing...)) + +CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,Number}) where {Dim} = CartesianGrid(start, finish, addunit.(spacing, u"m")) -CartesianGrid(start::NTuple{Dim}, finish::NTuple{Dim}, spacing::NTuple{Dim}) where {Dim} = +CartesianGrid(start::NTuple{Dim,Number}, finish::NTuple{Dim,Number}, spacing::NTuple{Dim,Number}) where {Dim} = CartesianGrid(Point(start), Point(finish), spacing) function CartesianGrid(start::Point{Dim}, finish::Point{Dim}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} @@ -111,8 +125,11 @@ function CartesianGrid(start::Point{Dim}, finish::Point{Dim}; dims::Dims{Dim}=nt CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid(start::NTuple{Dim}, finish::NTuple{Dim}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} = - CartesianGrid(Point(start), Point(finish); dims) +CartesianGrid( + start::NTuple{Dim,Number}, + finish::NTuple{Dim,Number}; + dims::Dims{Dim}=ntuple(i -> 100, Dim) +) where {Dim} = CartesianGrid(Point(start), Point(finish); dims) function CartesianGrid(dims::Dims{Dim}) where {Dim} origin = ntuple(i -> 0.0, Dim) diff --git a/test/mesh.jl b/test/mesh.jl index d99e99356..70d8d9c88 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -106,6 +106,14 @@ @test nelements(grid) == 10 * 10 @test eltype(grid) <: Quadrangle{2} + # mixed units + grid = CartesianGrid((10, 10), (T(0) * u"m", T(0) * u"cm"), (T(100) * u"cm", T(1) * u"m")) + @test unit(Meshes.lentype(grid)) == u"m" + grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), (T(100) * u"cm", T(1) * u"m")) + @test unit(Meshes.lentype(grid)) == u"m" + grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), dims=(10, 10)) + @test unit(Meshes.lentype(grid)) == u"m" + # indexing into a subgrid grid = cartgrid(10, 10) sub = grid[1:2, 1:2] From 279a74d3261906ea1e2f03e471716b08145d39fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 3 Jul 2024 10:25:00 -0300 Subject: [PATCH 152/423] Minor adjustments --- src/mesh/cartesiangrid.jl | 4 +--- src/sideof.jl | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 57dfadb85..d84cac41c 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -160,9 +160,7 @@ XYZ(g::CartesianGrid) = XYZ(xyz(g)) function centroid(g::CartesianGrid, ind::Int) ijk = elem2cart(topology(g), ind) - p = vertex(g, ijk) - δ = Vec(spacing(g) ./ 2) - p + δ + vertex(g, ijk) + Vec(spacing(g) ./ 2) end function Base.getindex(g::CartesianGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} diff --git a/src/sideof.jl b/src/sideof.jl index 7a3a3520a..dbf175742 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -57,6 +57,8 @@ Possible results are `IN`, `OUT` or `ON` the `ring`. (https://www.mdpi.com/2073-8994/10/10/477) """ function sideof(point::Point, ring::Ring) + assertion(CoordRefSystems.ncoords(crs(point)) == 2, "points must have 2 coordinates") + v = vertices(ring) n = nvertices(ring) From ec678f18f7ae7f5106e8a5f3efd461e55e295454 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:39:35 -0300 Subject: [PATCH 153/423] Allow intersection of boxes with different units (#923) --- src/intersections/boxes.jl | 4 ++-- test/intersections.jl | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 17b9b218a..a32600635 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -14,8 +14,8 @@ function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} m2, M2 = to.(extrema(box₂)) # relevant vertices - u = withdatum(box₁, max.(m1, m2)) - v = withdatum(box₁, min.(M1, M2)) + u = withdatum(box₁, max.(promote(m1, m2)...)) + v = withdatum(box₁, min.(promote(M1, M2)...)) # auxiliary variables δ = v - u diff --git a/test/intersections.jl b/test/intersections.jl index 64097b9ec..5d58dfdee 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -827,10 +827,20 @@ @test intersection(b1, b3) |> type == Touching @test b1 ∩ b3 == Box(cart(1, 0, 0), cart(2, 2, 0)) + # different units + b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) + b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) + @test intersection(b1, b2) |> type == Overlapping + @test unit(Meshes.lentype(b1 ∩ b2)) == u"m" + @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) + # type stability tests b1 = Box(cart(0, 0), cart(1, 1)) b2 = Box(cart(0.5, 0.5), cart(2, 2)) @inferred someornone(b1, b2) + b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) + b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) + @inferred someornone(b1, b2) # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0)) From 634e975199ba354d3325db6cacb6d12c8d2cec4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 3 Jul 2024 12:40:13 -0300 Subject: [PATCH 154/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 06daca723..b2d27d66c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.0" +version = "0.46.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 009fe45977e6b2cbe1a193298c90504759075875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 4 Jul 2024 07:40:11 -0300 Subject: [PATCH 155/423] Attempt to use threads in sideof (#919) * Attempt to use threads in sideof * Fix code * Update src/sideof.jl * Add comments --------- Co-authored-by: Elias Carvalho --- src/sideof.jl | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/sideof.jl b/src/sideof.jl index dbf175742..34b989d6b 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -66,8 +66,12 @@ function sideof(point::Point, ring::Ring) p = flat(coords(point)) xₚ, yₚ = p.x, p.y - k = 0 - for i in 1:n + k = Threads.Atomic{Int}(0) + ison = Threads.Atomic{Bool}(false) + Threads.@threads for i in 1:n + # premature termination if point is on ring + ison[] && break + # flat coordinates of segment i -- i+1 pᵢ = flat(coords(v[i])) pⱼ = flat(coords(v[i + 1])) @@ -90,48 +94,50 @@ function sideof(point::Point, ring::Ring) f = u₁ * v₂ - u₂ * v₁ if ispositive(f) # case 3, 9 - k += 1 + Threads.atomic_add!(k, 1) elseif isequalzero(f) # case 16, 21 - return ON + ison[] = true end elseif ispositive(v₁) && isnonpositive(v₂) # case 4, 10, 19, 20, 12, 25 f = u₁ * v₂ - u₂ * v₁ if isnegative(f) # case 4, 10 - k += 1 + Threads.atomic_add!(k, 1) elseif isequalzero(f) # case 19, 20 - return ON + ison[] = true end elseif isequalzero(v₂) && isnegative(v₁) # case 7, 14, 17 f = u₁ * v₂ - u₂ * v₁ if isequalzero(f) # case 17 - return ON + ison[] = true end elseif isequalzero(v₁) && isnegative(v₂) # case 8, 15, 18 f = u₁ * v₂ - u₂ * v₁ if isequalzero(f) # case 18 - return ON + ison[] = true end elseif isequalzero(v₁) && isequalzero(v₂) # case 1, 2, 5, 6, 22, 23 if isnonpositive(u₂) && isnonnegative(u₁) # case 1 - return ON + ison[] = true elseif isnonpositive(u₁) && isnonnegative(u₂) # case 2 - return ON + ison[] = true end end end - iseven(k) ? OUT : IN + # when `ison` is true, the value of `k` might be incorrectly set by multiple threads, + # however that does not matter in the following return statement + ison[] ? ON : (iseven(k[]) ? OUT : IN) end # ----- From 299c4815a2845d851e34d7d440a9b7030bdaec09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 4 Jul 2024 07:40:30 -0300 Subject: [PATCH 156/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b2d27d66c..d63cd42a2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.1" +version = "0.46.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 9383972c4c444f2c85067acb2935d2471c7e7087 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:22:03 -0300 Subject: [PATCH 157/423] Export the 'crs' function (#925) --- src/Meshes.jl | 2 ++ test/boundingboxes.jl | 4 +-- test/domains.jl | 4 +-- test/intersections.jl | 4 +-- test/mesh.jl | 28 +++++++++---------- test/multigeoms.jl | 4 +-- test/partitioning.jl | 2 +- test/polytopes.jl | 30 ++++++++++----------- test/primitives.jl | 62 +++++++++++++++++++++---------------------- test/refinement.jl | 6 ++--- test/sets.jl | 10 +++---- test/subdomains.jl | 6 ++--- test/tesselation.jl | 8 +++--- test/trajecs.jl | 6 ++--- test/transforms.jl | 4 +-- 15 files changed, 91 insertions(+), 89 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index 23a878eb7..c0d88627e 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -137,6 +137,7 @@ export Geometry, embeddim, paramdim, + crs, center, centroid, @@ -276,6 +277,7 @@ export SubDomain, embeddim, paramdim, + crs, element, nelements, diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index ea393551d..6f70645e0 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -122,9 +122,9 @@ # datum propagation c = Cartesian{WGS84Latest}(T(-1), T(1)) r = Ray(Point(c), vector(1, -1)) - @test datum(Meshes.crs(boundingbox(r))) === WGS84Latest + @test datum(crs(boundingbox(r))) === WGS84Latest c = Cartesian{WGS84Latest}(T(0), T(0)) g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) m = convert(SimpleMesh, g) - @test datum(Meshes.crs(boundingbox(m))) === WGS84Latest + @test datum(crs(boundingbox(m))) === WGS84Latest end diff --git a/test/domains.jl b/test/domains.jl index 30e048c06..1913f83ab 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -2,7 +2,7 @@ # basic properties dom = DummyDomain(cart(0, 0)) @test embeddim(dom) == 2 - @test Meshes.crs(dom) <: Cartesian{NoDatum} + @test crs(dom) <: Cartesian{NoDatum} @test Meshes.lentype(dom) == ℳ @test !isparametrized(dom) @@ -33,7 +33,7 @@ # datum propagation c = Cartesian{WGS84Latest}(T(1), T(1)) dom = DummyDomain(Point(c)) - @test datum(Meshes.crs(centroid(dom))) === WGS84Latest + @test datum(crs(centroid(dom))) === WGS84Latest dom = DummyDomain(cart(0, 0)) @test sprint(show, dom) == "3 DummyDomain" diff --git a/test/intersections.jl b/test/intersections.jl index 5d58dfdee..219f3f33f 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -770,7 +770,7 @@ c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) p1 = Plane(Point(c1), vector(0, 0, 1)) p2 = Plane(Point(c2), vector(1 / sqrt(2), 0, 1 / sqrt(2))) - @test datum(Meshes.crs(p1 ∩ p2)) === WGS84Latest + @test datum(crs(p1 ∩ p2)) === WGS84Latest end @testset "Boxes" begin @@ -849,7 +849,7 @@ c4 = Cartesian{WGS84Latest}(T(2), T(2)) b1 = Box(Point(c1), Point(c2)) b2 = Box(Point(c3), Point(c4)) - @test datum(Meshes.crs(b1 ∩ b2)) === WGS84Latest + @test datum(crs(b1 ∩ b2)) === WGS84Latest # Ray-Box intersection b = Box(cart(0, 0, 0), cart(1, 1, 1)) diff --git a/test/mesh.jl b/test/mesh.jl index 70d8d9c88..e1e15205d 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -2,7 +2,7 @@ @testset "CartesianGrid" begin grid = cartgrid(100) @test embeddim(grid) == 1 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (100,) @test minimum(grid) == cart(0) @@ -19,7 +19,7 @@ grid = cartgrid(200, 100) @test embeddim(grid) == 2 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) @test minimum(grid) == cart(0, 0) @@ -36,7 +36,7 @@ grid = CartesianGrid((200, 100, 50), T.((0, 0, 0)), T.((1, 1, 1))) @test embeddim(grid) == 3 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100, 50) @test minimum(grid) == cart(0, 0, 0) @@ -53,7 +53,7 @@ grid = CartesianGrid(T.((0, 0, 0)), T.((1, 1, 1)), T.((0.1, 0.1, 0.1))) @test embeddim(grid) == 3 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10, 10) @test minimum(grid) == cart(0, 0, 0) @@ -62,7 +62,7 @@ grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) @test embeddim(grid) == 2 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (200, 100) @test minimum(grid) == cart(-1.0, -1.0) @@ -73,7 +73,7 @@ grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) @test embeddim(grid) == 3 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (20, 10, 5) @test minimum(grid) == cart(0, 0, 0) @@ -97,7 +97,7 @@ # constructor with offset grid = CartesianGrid((10, 10), T.((1.0, 1.0)), T.((1.0, 1.0)), (2, 2)) @test embeddim(grid) == 2 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (10, 10) @test minimum(grid) == cart(0.0, 0.0) @@ -250,7 +250,7 @@ y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) @test embeddim(grid) == 2 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) @test minimum(grid) == cart(0, 0) @@ -290,8 +290,8 @@ # constructor with datum & datum propagation grid = RectilinearGrid{WGS84Latest}(x, y) - @test datum(Meshes.crs(grid)) === WGS84Latest - @test datum(Meshes.crs(centroid(grid))) === WGS84Latest + @test datum(crs(grid)) === WGS84Latest + @test datum(crs(centroid(grid))) === WGS84Latest # conversion cg = cartgrid(10, 10) @@ -376,7 +376,7 @@ Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) grid = StructuredGrid(X, Y) @test embeddim(grid) == 2 - @test Meshes.crs(grid) <: Cartesian{NoDatum} + @test crs(grid) <: Cartesian{NoDatum} @test Meshes.lentype(grid) == ℳ @test size(grid) == (5, 5) @test minimum(grid) == cart(0, 0) @@ -410,7 +410,7 @@ # constructor with datum grid = StructuredGrid{WGS84Latest}(X, Y) - @test datum(Meshes.crs(grid)) === WGS84Latest + @test datum(crs(grid)) === WGS84Latest # conversion cg = cartgrid(10, 10) @@ -517,7 +517,7 @@ (cart(1.0, 1.0), cart(0.0, 1.0), cart(0.5, 0.5)), (cart(0.0, 1.0), cart(0.0, 0.0), cart(0.5, 0.5)) ]) - @test Meshes.crs(mesh) <: Cartesian{NoDatum} + @test crs(mesh) <: Cartesian{NoDatum} @test Meshes.lentype(mesh) == ℳ @test vertices(mesh) == points @test collect(faces(mesh, 2)) == triangles @@ -702,7 +702,7 @@ mesh = convert(SimpleMesh, grid) trans = Identity() tmesh = TransformedMesh(mesh, trans) - @test Meshes.crs(tmesh) <: Cartesian{NoDatum} + @test crs(tmesh) <: Cartesian{NoDatum} @test Meshes.lentype(tmesh) == ℳ @test parent(tmesh) === mesh @test Meshes.transform(tmesh) === trans diff --git a/test/multigeoms.jl b/test/multigeoms.jl index e97000e13..c717c9ab6 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -7,7 +7,7 @@ @test multi == multi @test multi ≈ multi @test paramdim(multi) == 2 - @test Meshes.crs(multi) <: Cartesian{NoDatum} + @test crs(multi) <: Cartesian{NoDatum} @test Meshes.lentype(multi) == ℳ @test vertex(multi, 1) == vertex(poly, 1) @test vertices(multi) == [vertices(poly); vertices(poly)] @@ -100,5 +100,5 @@ poly1 = PolyArea(points1) poly2 = PolyArea(points2) multi = Multi([poly1, poly2]) - @test datum(Meshes.crs(centroid(multi))) === WGS84Latest + @test datum(crs(centroid(multi))) === WGS84Latest end diff --git a/test/partitioning.jl b/test/partitioning.jl index cac1d838a..bcbecabba 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -212,7 +212,7 @@ c = Cartesian{WGS84Latest}(T(0), T(0)) g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - @test datum(Meshes.crs(first(p))) === WGS84Latest + @test datum(crs(first(p))) === WGS84Latest end @testset "BallPartition" begin diff --git a/test/polytopes.jl b/test/polytopes.jl index 0618b9197..31ac4db66 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -4,7 +4,7 @@ @test nvertices(Segment) == 2 s = Segment(cart(1.0), cart(2.0)) - @test Meshes.crs(s) <: Cartesian{NoDatum} + @test crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test vertex(s, 1) == cart(1.0) @test vertex(s, 2) == cart(2.0) @@ -78,7 +78,7 @@ c1 = Cartesian{WGS84Latest}(T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(1)) s = Segment(Point(c1), Point(c2)) - @test datum(Meshes.crs(s(T(0)))) === WGS84Latest + @test datum(crs(s(T(0)))) === WGS84Latest s = Segment(cart(0, 0), cart(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" @@ -120,12 +120,12 @@ @test c1 ≗ c2 ≗ c3 c = Rope(cart.([(1, 1), (2, 2)])) - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test vertex(c, 1) == cart(1, 1) @test vertex(c, 2) == cart(2, 2) c = Ring(cart.([(1, 1), (2, 2)])) - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test vertex(c, 0) == cart(2, 2) @test vertex(c, 1) == cart(1, 1) @@ -278,7 +278,7 @@ tuples = [T.((0, 0)), T.((1, 0)), T.((1, 1)), T.((0, 1))] points = Point.(Cartesian{WGS84Latest}.(tuples)) r = Ring(points) - @test datum(Meshes.crs(centroid(r))) === WGS84Latest + @test datum(crs(centroid(r))) === WGS84Latest ri = Ring(cart.([(1, 1), (2, 2), (3, 3)])) ro = Rope(cart.([(1, 1), (2, 2), (3, 3)])) @@ -346,7 +346,7 @@ # Triangle in 2D space t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test vertex(t, 1) == cart(0, 0) @test vertex(t, 2) == cart(1, 0) @@ -436,7 +436,7 @@ c2 = Cartesian{WGS84Latest}(T(1), T(0)) c3 = Cartesian{WGS84Latest}(T(0), T(1)) t = Triangle(Point(c1), Point(c2), Point(c3)) - @test datum(Meshes.crs(t(T(0), T(0)))) === WGS84Latest + @test datum(crs(t(T(0), T(0)))) === WGS84Latest t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" @@ -460,7 +460,7 @@ # Quadrangle in 2D space q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test Meshes.crs(q) <: Cartesian{NoDatum} + @test crs(q) <: Cartesian{NoDatum} @test Meshes.lentype(q) == ℳ @test vertex(q, 1) == cart(0, 0) @test vertex(q, 2) == cart(1, 0) @@ -512,7 +512,7 @@ c3 = Cartesian{WGS84Latest}(T(1), T(1)) c4 = Cartesian{WGS84Latest}(T(0), T(1)) q = Quadrangle(Point(c1), Point(c2), Point(c3), Point(c4)) - @test datum(Meshes.crs(q(T(0), T(0)))) === WGS84Latest + @test datum(crs(q(T(0), T(0)))) === WGS84Latest q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" @@ -543,7 +543,7 @@ poly = PolyArea([outer, hole1, hole2]) @test poly == poly @test poly ≈ poly - @test Meshes.crs(poly) <: Cartesian{NoDatum} + @test crs(poly) <: Cartesian{NoDatum} @test Meshes.lentype(poly) == ℳ p = PolyArea(cart(0, 0), cart(1, 0), cart(0, 1)) @@ -752,7 +752,7 @@ @test nvertices(Tetrahedron) == 4 t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test vertex(t, 1) == cart(0, 0, 0) @test vertex(t, 2) == cart(1, 0, 0) @@ -789,7 +789,7 @@ c3 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) c4 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) - @test datum(Meshes.crs(t(T(0), T(0), T(0)))) === WGS84Latest + @test datum(crs(t(T(0), T(0), T(0)))) === WGS84Latest t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -822,7 +822,7 @@ cart(1, 1, 1), cart(0, 1, 1) ) - @test Meshes.crs(h) <: Cartesian{NoDatum} + @test crs(h) <: Cartesian{NoDatum} @test Meshes.lentype(h) == ℳ @test vertex(h, 1) == cart(0, 0, 0) @test vertex(h, 8) == cart(0, 1, 1) @@ -916,7 +916,7 @@ c7 = Cartesian{WGS84Latest}(T(1), T(1), T(1)) c8 = Cartesian{WGS84Latest}(T(0), T(1), T(1)) h = Hexahedron(Point(c1), Point(c2), Point(c3), Point(c4), Point(c5), Point(c6), Point(c7), Point(c8)) - @test datum(Meshes.crs(h(T(0), T(0), T(0)))) === WGS84Latest + @test datum(crs(h(T(0), T(0), T(0)))) === WGS84Latest h = Hexahedron( cart(0, 0, 0), @@ -957,7 +957,7 @@ @test nvertices(Pyramid) == 5 p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test Meshes.crs(p) <: Cartesian{NoDatum} + @test crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test volume(p) ≈ T(1 / 3) * u"m^3" m = boundary(p) diff --git a/test/primitives.jl b/test/primitives.jl index c4074d80f..25ff159cf 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -3,9 +3,9 @@ @test embeddim(Point(1)) == 1 @test embeddim(Point(1, 2)) == 2 @test embeddim(Point(1, 2, 3)) == 3 - @test Meshes.crs(cart(1, 1)) <: Cartesian{NoDatum} - @test Meshes.crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} - @test Meshes.crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} + @test crs(cart(1, 1)) <: Cartesian{NoDatum} + @test crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} + @test crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} @test Meshes.lentype(Point(1.0, 1.0)) == Meshes.Met{Float64} @test Meshes.lentype(Point(1.0f0, 1.0f0)) == Meshes.Met{Float32} @@ -167,8 +167,8 @@ # datum propagation c = Cartesian{WGS84Latest}(T(1), T(1)) - @test datum(Meshes.crs(Point(c) + vector(1, 1))) === WGS84Latest - @test datum(Meshes.crs(Point(c) - vector(1, 1))) === WGS84Latest + @test datum(crs(Point(c) + vector(1, 1))) === WGS84Latest + @test datum(crs(Point(c) - vector(1, 1))) === WGS84Latest p = cart(0, 1) @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" @@ -190,7 +190,7 @@ @testset "Ray" begin r = Ray(cart(0, 0), vector(1, 1)) @test paramdim(r) == 1 - @test Meshes.crs(r) <: Cartesian{NoDatum} + @test crs(r) <: Cartesian{NoDatum} @test Meshes.lentype(r) == ℳ @test measure(r) == typemax(ℳ) @test length(r) == typemax(ℳ) @@ -257,7 +257,7 @@ @testset "Line" begin l = Line(cart(0, 0), cart(1, 1)) @test paramdim(l) == 1 - @test Meshes.crs(l) <: Cartesian{NoDatum} + @test crs(l) <: Cartesian{NoDatum} @test Meshes.lentype(l) == ℳ @test measure(l) == typemax(ℳ) @test length(l) == typemax(ℳ) @@ -298,7 +298,7 @@ @test p(T(1), T(0)) == cart(1, 0, 0) @test paramdim(p) == 2 @test embeddim(p) == 3 - @test Meshes.crs(p) <: Cartesian{NoDatum} + @test crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test measure(p) == typemax(ℳ)^2 @test area(p) == typemax(ℳ)^2 @@ -367,7 +367,7 @@ b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) @test embeddim(b) == 2 @test paramdim(b) == 1 - @test Meshes.crs(b) <: Cartesian{NoDatum} + @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ b = BezierCurve(cart(0, 0), cart(1, 1)) @@ -409,7 +409,7 @@ c2 = Cartesian{WGS84Latest}(T(0.5), T(1)) c3 = Cartesian{WGS84Latest}(T(1), T(1)) b = BezierCurve(Point(c1), Point(c2), Point(c3)) - @test datum(Meshes.crs(b(T(0), Horner()))) === WGS84Latest + @test datum(crs(b(T(0), Horner()))) === WGS84Latest b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" @@ -428,7 +428,7 @@ b = Box(cart(0), cart(1)) @test embeddim(b) == 1 @test paramdim(b) == 1 - @test Meshes.crs(b) <: Cartesian{NoDatum} + @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == cart(0) @test maximum(b) == cart(1) @@ -437,7 +437,7 @@ b = Box(cart(0, 0), cart(1, 1)) @test embeddim(b) == 2 @test paramdim(b) == 2 - @test Meshes.crs(b) <: Cartesian{NoDatum} + @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == cart(0, 0) @test maximum(b) == cart(1, 1) @@ -446,7 +446,7 @@ b = Box(cart(0, 0, 0), cart(1, 1, 1)) @test embeddim(b) == 3 @test paramdim(b) == 3 - @test Meshes.crs(b) <: Cartesian{NoDatum} + @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test minimum(b) == cart(0, 0, 0) @test maximum(b) == cart(1, 1, 1) @@ -551,7 +551,7 @@ c1 = Cartesian{WGS84Latest}(T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(1)) b = Box(Point(c1), Point(c2)) - @test datum(Meshes.crs(center(b))) === WGS84Latest + @test datum(crs(center(b))) === WGS84Latest b = Box(cart(0, 0), cart(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" @@ -572,7 +572,7 @@ b = Ball(cart(1, 2, 3), T(5)) @test embeddim(b) == 3 @test paramdim(b) == 3 - @test Meshes.crs(b) <: Cartesian{NoDatum} + @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ @test Meshes.center(b) == cart(1, 2, 3) @test radius(b) == T(5) * u"m" @@ -656,7 +656,7 @@ s = Sphere(cart(0, 0, 0), T(1)) @test embeddim(s) == 3 @test paramdim(s) == 2 - @test Meshes.crs(s) <: Cartesian{NoDatum} + @test crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test Meshes.center(s) == cart(0, 0, 0) @test radius(s) == T(1) * u"m" @@ -765,7 +765,7 @@ e = Ellipsoid((T(3), T(2), T(1))) @test embeddim(e) == 3 @test paramdim(e) == 2 - @test Meshes.crs(e) <: Cartesian{NoDatum} + @test crs(e) <: Cartesian{NoDatum} @test Meshes.lentype(e) == ℳ @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") @test center(e) == cart(0, 0, 0) @@ -799,7 +799,7 @@ d = Disk(p, T(2)) @test embeddim(d) == 3 @test paramdim(d) == 2 - @test Meshes.crs(d) <: Cartesian{NoDatum} + @test crs(d) <: Cartesian{NoDatum} @test Meshes.lentype(d) == ℳ @test plane(d) == p @test Meshes.center(d) == cart(0, 0, 0) @@ -842,7 +842,7 @@ c = Circle(p, T(2)) @test embeddim(c) == 3 @test paramdim(c) == 1 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test plane(c) == p @test Meshes.center(c) == cart(0, 0, 0) @@ -886,7 +886,7 @@ c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) c3 = Cartesian{WGS84Latest}(T(0), T(0), T(4)) c = Circle(Point(c1), Point(c2), Point(c3)) - @test datum(Meshes.crs(c)) === WGS84Latest + @test datum(crs(c)) === WGS84Latest p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @@ -909,7 +909,7 @@ c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) @test embeddim(c) == 3 @test paramdim(c) == 3 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(5) * u"m" @test bottom(c) == Plane(cart(1, 2, 3), vector(0, 0, 1)) @@ -990,7 +990,7 @@ c = CylinderSurface(T(2)) @test embeddim(c) == 3 @test paramdim(c) == 2 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test radius(c) == T(2) * u"m" @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) @@ -1040,7 +1040,7 @@ c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) c = CylinderSurface(Point(c1), Point(c2), T(1)) - @test datum(Meshes.crs(center(c))) === WGS84Latest + @test datum(crs(center(c))) === WGS84Latest c = CylinderSurface(T(1)) @test sprint(show, c) == @@ -1064,7 +1064,7 @@ p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) @test embeddim(p) == 3 @test paramdim(p) == 2 - @test Meshes.crs(p) <: Cartesian{NoDatum} + @test crs(p) <: Cartesian{NoDatum} @test Meshes.lentype(p) == ℳ @test focallength(p) == T(2) * u"m" @test radius(p) == T(1) * u"m" @@ -1141,7 +1141,7 @@ c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) @@ -1151,7 +1151,7 @@ c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ p = Plane(cart(0, 0, 0), vector(0, 0, 1)) @@ -1219,7 +1219,7 @@ s = ConeSurface(d, a) @test embeddim(s) == 3 @test paramdim(s) == 2 - @test Meshes.crs(s) <: Cartesian{NoDatum} + @test crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) @@ -1229,7 +1229,7 @@ c = ConeSurface(d, a) @test embeddim(c) == 3 @test paramdim(c) == 2 - @test Meshes.crs(c) <: Cartesian{NoDatum} + @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ p = Plane(cart(0, 0, 0), vector(0, 0, 1)) @@ -1269,7 +1269,7 @@ dt = Disk(pt, T(2)) f = Frustum(db, dt) @test embeddim(f) == 3 - @test Meshes.crs(f) <: Cartesian{NoDatum} + @test crs(f) <: Cartesian{NoDatum} @test Meshes.lentype(f) == ℳ @test boundary(f) == FrustumSurface(db, dt) @@ -1321,7 +1321,7 @@ f = FrustumSurface(db, dt) @test embeddim(f) == 3 @test paramdim(f) == 2 - @test Meshes.crs(f) <: Cartesian{NoDatum} + @test crs(f) <: Cartesian{NoDatum} @test Meshes.lentype(f) == ℳ @test isnothing(boundary(f)) @@ -1344,7 +1344,7 @@ @test cart(1, 1, -1) ∈ t @test cart(1, 1, 1) ∉ t @test paramdim(t) == 2 - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test Meshes.center(t) == cart(1, 1, 1) @test normal(t) == vector(1, 0, 0) diff --git a/test/refinement.jl b/test/refinement.jl index 3878e9141..73ebf81b8 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -16,7 +16,7 @@ c = Cartesian{WGS84Latest}(T(0), T(0)) grid = CartesianGrid((3, 3), Point(c), (T(1), T(1))) ref = refine(grid, TriRefinement()) - @test datum(Meshes.crs(ref)) === WGS84Latest + @test datum(crs(ref)) === WGS84Latest end @testset "QuadRefinement" begin @@ -41,7 +41,7 @@ connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref = refine(mesh, QuadRefinement()) - @test datum(Meshes.crs(ref)) === WGS84Latest + @test datum(crs(ref)) === WGS84Latest end @testset "RegularRefinement" begin @@ -120,7 +120,7 @@ connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) ref = refine(mesh, CatmullClark()) - @test datum(Meshes.crs(ref)) === WGS84Latest + @test datum(crs(ref)) === WGS84Latest end @testset "TriSubdivision" begin diff --git a/test/sets.jl b/test/sets.jl index 53bcd5046..89d4b22c2 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -4,7 +4,7 @@ t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) p = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) gset = GeometrySet([s, t, p]) - @test Meshes.crs(gset) <: Cartesian{NoDatum} + @test crs(gset) <: Cartesian{NoDatum} @test Meshes.lentype(gset) == ℳ @test [centroid(gset, i) for i in 1:3] == cart.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) @@ -36,21 +36,21 @@ @testset "PointSet" begin pset = PointSet(randpoint1(100)) @test embeddim(pset) == 1 - @test Meshes.crs(pset) <: Cartesian{NoDatum} + @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{1} pset = PointSet(randpoint2(100)) @test embeddim(pset) == 2 - @test Meshes.crs(pset) <: Cartesian{NoDatum} + @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{2} pset = PointSet(randpoint3(100)) @test embeddim(pset) == 3 - @test Meshes.crs(pset) <: Cartesian{NoDatum} + @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 @test eltype(pset) <: Point{3} @@ -90,7 +90,7 @@ tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1))] points = Point.(Cartesian{WGS84Latest}.(tuples)) pset = PointSet(points) - @test datum(Meshes.crs(centroid(pset))) === WGS84Latest + @test datum(crs(centroid(pset))) === WGS84Latest pset = PointSet(cart.([(1, 0), (0, 1)])) @test sprint(show, pset) == "2 PointSet" diff --git a/test/subdomains.jl b/test/subdomains.jl index 95b74af18..9f7a95ae9 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -3,7 +3,7 @@ inds = rand(1:100, 3) v = view(pset, inds) @test nelements(v) == 3 - @test Meshes.crs(v) <: Cartesian{NoDatum} + @test crs(v) <: Cartesian{NoDatum} @test Meshes.lentype(v) == ℳ for i in 1:3 p = pset[inds[i]] @@ -15,7 +15,7 @@ inds = rand(1:100, 3) v = view(grid, inds) @test nelements(v) == 3 - @test Meshes.crs(v) <: Cartesian{NoDatum} + @test crs(v) <: Cartesian{NoDatum} @test Meshes.lentype(v) == ℳ for i in 1:3 e = grid[inds[i]] @@ -29,7 +29,7 @@ inds = rand(1:4, 3) v = view(mesh, inds) @test nelements(v) == 3 - @test Meshes.crs(v) <: Cartesian{NoDatum} + @test crs(v) <: Cartesian{NoDatum} @test Meshes.lentype(v) == ℳ for i in 1:3 e = mesh[inds[i]] diff --git a/test/tesselation.jl b/test/tesselation.jl index dacb5d506..3b07e7d44 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -10,12 +10,12 @@ tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test Meshes.crs(mesh) === Meshes.crs(pset) + @test crs(mesh) === crs(pset) coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] pset = PointSet(Point.(coords)) mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test Meshes.crs(mesh) === Meshes.crs(pset) + @test crs(mesh) === crs(pset) # error: the number of coordinates of the points must be 2 pts = randpoint3(10) @@ -34,12 +34,12 @@ tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test Meshes.crs(mesh) === Meshes.crs(pset) + @test crs(mesh) === crs(pset) coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] pset = PointSet(Point.(coords)) mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test Meshes.crs(mesh) === Meshes.crs(pset) + @test crs(mesh) === crs(pset) # error: the number of coordinates of the points must be 2 pts = randpoint3(10) diff --git a/test/trajecs.jl b/test/trajecs.jl index 67ca999ee..f6fb9641f 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -3,7 +3,7 @@ s = Segment(cart(0, 0, 0), cart(0, 0, 1)) c = [s(t) for t in range(T(0), stop=T(1), length=10)] t = CylindricalTrajectory(c) - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 10 @@ -13,7 +13,7 @@ b = BezierCurve([cart(0, 0, 0), cart(3, 3, 0), cart(3, 0, 7)]) c = [b(t) for t in range(T(0), stop=T(1), length=20)] t = CylindricalTrajectory(c, T(2)) - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 20 @@ -22,7 +22,7 @@ # trajectory with single cylinder t = CylindricalTrajectory([cart(0, 0, 0)], T(1)) - @test Meshes.crs(t) <: Cartesian{NoDatum} + @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test eltype(t) <: Cylinder @test nelements(t) == 1 diff --git a/test/transforms.jl b/test/transforms.jl index 2e190db87..70f5381d1 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -226,7 +226,7 @@ f = Rotate(Angle2d(T(π / 2))) c = Cartesian{WGS84Latest}(T(1), T(0)) p = Point(c) - @test datum(Meshes.crs(f(p))) === WGS84Latest + @test datum(crs(f(p))) === WGS84Latest end @testset "Translate" begin @@ -606,7 +606,7 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) c = Cartesian{WGS84Latest}(T(1), T(0)) p = Point(c) - @test datum(Meshes.crs(f(p))) === WGS84Latest + @test datum(crs(f(p))) === WGS84Latest # error: A must be a square matrix @test_throws ArgumentError Affine(T[1 1; 2 2; 3 3], T[1, 2]) From 76f6297036706c4f56010c925ac41532103de3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 4 Jul 2024 16:22:19 -0300 Subject: [PATCH 158/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d63cd42a2..563728b39 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.2" +version = "0.46.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ef6baafe672217fe09310fe682cf847303a0ddc7 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:35:01 +0200 Subject: [PATCH 159/423] Fix constructor of `PointSet` taking `Vector{Vector}` (#926) * fix constructor of PointSet taking Vector{Vector} * fix rendering of docstring of TriSubdivision --- src/refinement/trisubdivision.jl | 6 +++--- src/sets.jl | 2 +- test/sets.jl | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/refinement/trisubdivision.jl b/src/refinement/trisubdivision.jl index 9ffdb0c99..c63bc66d3 100644 --- a/src/refinement/trisubdivision.jl +++ b/src/refinement/trisubdivision.jl @@ -3,15 +3,15 @@ # ------------------------------------------------------------------ """ - TriSubdivision() + TriSubdivision() Refinement of a mesh by preliminarly triangulating it if needed and then subdividing each triangle into four triangles. ## References -* Charles Loop. 1987. [Smooth subdivision surfaces based on - triangles](https://charlesloop.com/thesis.pdf). +* Charles Loop. 1987. [Smooth subdivision surfaces based on + triangles](https://charlesloop.com/thesis.pdf). Master's thesis, University of Utah. """ struct TriSubdivision <: RefinementMethod end diff --git a/src/sets.jl b/src/sets.jl index 3a28af9a5..a3c3694f0 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -62,7 +62,7 @@ PointSet(points::AbstractVector{Point{Dim,C}}) where {Dim,C<:CRS} = PointSet{Dim PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) -PointSet(coords::AbstractVector{V}) where {V<:AbstractVector} = PointSet(Point.(coords)) +PointSet(coords::AbstractVector{V}) where {V<:AbstractVector} = PointSet(Tuple.(coords)) PointSet(coords::Vararg{V}) where {V<:AbstractVector} = PointSet(collect(coords)) PointSet(coords::AbstractMatrix) = PointSet(Tuple.(eachcol(coords))) diff --git a/test/sets.jl b/test/sets.jl index 89d4b22c2..6388f3b83 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -60,8 +60,10 @@ pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) pset5 = PointSet(T[1 4; 2 5; 3 6]) - @test pset1 == pset2 == pset3 == pset4 == pset5 - for pset in [pset1, pset2, pset3, pset4, pset5] + pset6 = PointSet(T[1, 2, 3], T[4, 5, 6]) + pset7 = PointSet([T[1, 2, 3], T[4, 5, 6]]) + @test pset1 == pset2 == pset3 == pset4 == pset5 == pset6 == pset7 + for pset in [pset1, pset2, pset3, pset4, pset5, pset6, pset7] @test embeddim(pset) == 3 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 2 From 63213959d845f73080021ac3fb8b559ec54a1914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 9 Jul 2024 09:25:01 -0300 Subject: [PATCH 160/423] Remove obsolete PointSet constructors (#927) --- docs/src/algorithms/clamping.md | 2 +- src/sets.jl | 6 ------ test/boundingboxes.jl | 6 +++--- test/intersections.jl | 2 +- test/partitioning.jl | 5 +---- test/sets.jl | 7 ++----- 6 files changed, 8 insertions(+), 20 deletions(-) diff --git a/docs/src/algorithms/clamping.md b/docs/src/algorithms/clamping.md index f87bc4668..ff23dc0c5 100644 --- a/docs/src/algorithms/clamping.md +++ b/docs/src/algorithms/clamping.md @@ -12,7 +12,7 @@ using Meshes # hide import CairoMakie as Mke # hide # set of 2D points to clamp -points = PointSet(rand(2, 100)) +points = PointSet(rand(Point{2}, 100)) # 2D box defining the clamping boundaries box = Box((0.25, 0.25), (0.75, 0.75)) diff --git a/src/sets.jl b/src/sets.jl index a3c3694f0..ff9ca5daf 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -53,18 +53,12 @@ julia> PointSet([Point(1,2,3), Point(4,5,6)]) julia> PointSet(Point(1,2,3), Point(4,5,6)) julia> PointSet([(1,2,3), (4,5,6)]) julia> PointSet((1,2,3), (4,5,6)) -julia> PointSet([[1,2,3], [4,5,6]]) -julia> PointSet([1,2,3], [4,5,6]) -julia> PointSet([1 4; 2 5; 3 6]) ``` """ PointSet(points::AbstractVector{Point{Dim,C}}) where {Dim,C<:CRS} = PointSet{Dim,C}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) -PointSet(coords::AbstractVector{V}) where {V<:AbstractVector} = PointSet(Tuple.(coords)) -PointSet(coords::Vararg{V}) where {V<:AbstractVector} = PointSet(collect(coords)) -PointSet(coords::AbstractMatrix) = PointSet(Tuple.(eachcol(coords))) # constructor with iterator of points PointSet(points) = PointSet(map(identity, points)) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 6f70645e0..c344ba8b2 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -68,10 +68,10 @@ @test @allocated(boundingbox(m)) < 50 @test @allocated(boundingbox(d)) < 50 - d = PointSet(T[0 1 2; 0 2 1]) + d = PointSet(cart(0, 0), cart(1, 2), cart(2, 1)) @test boundingbox(d) == Box(cart(0, 0), cart(2, 2)) @test @allocated(boundingbox(d)) < 50 - d = PointSet(T[1 2; 2 1]) + d = PointSet(cart(1, 2), cart(2, 1)) @test boundingbox(d) == Box(cart(1, 1), cart(2, 2)) @test @allocated(boundingbox(d)) < 50 @@ -85,7 +85,7 @@ @test boundingbox(d) == Box(cart(1, 1), cart(11, 11)) @test @allocated(boundingbox(d)) < 50 - d = PointSet(T[0 1 2; 0 2 1]) + d = PointSet(cart(0, 0), cart(1, 2), cart(2, 1)) v = view(d, 1:2) @test boundingbox(v) == Box(cart(0, 0), cart(1, 2)) @test @allocated(boundingbox(v)) < 50 diff --git a/test/intersections.jl b/test/intersections.jl index 219f3f33f..2730ff1d3 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1142,7 +1142,7 @@ r = Ray(cart(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) @test intersection(r, o) |> type == Intersecting - @test r ∩ o == PointSet([cart(0.0, 0.0, 0.0)]) + @test r ∩ o == PointSet(cart(0.0, 0.0, 0.0)) r = Ray(cart(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) @test intersection(r, o) |> type == NotIntersecting diff --git a/test/partitioning.jl b/test/partitioning.jl index bcbecabba..8f95e39e0 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -216,10 +216,7 @@ end @testset "BallPartition" begin - pset = PointSet(T[ - 0 1 1 0 0.2 - 0 0 1 1 0.2 - ]) + pset = PointSet(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1), cart(0.2, 0.2)) # 3 balls with 1 point, and 1 ball with 2 points p = partition(pset, BallPartition(T(0.5))) diff --git a/test/sets.jl b/test/sets.jl index 6388f3b83..2fd54dbf8 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -59,11 +59,8 @@ pset2 = PointSet(cart(1, 2, 3), cart(4, 5, 6)) pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) - pset5 = PointSet(T[1 4; 2 5; 3 6]) - pset6 = PointSet(T[1, 2, 3], T[4, 5, 6]) - pset7 = PointSet([T[1, 2, 3], T[4, 5, 6]]) - @test pset1 == pset2 == pset3 == pset4 == pset5 == pset6 == pset7 - for pset in [pset1, pset2, pset3, pset4, pset5, pset6, pset7] + @test pset1 == pset2 == pset3 == pset4 + for pset in [pset1, pset2, pset3, pset4] @test embeddim(pset) == 3 @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 2 From 826f48cfa69dac858eca2b2832c84aa96ccd29bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 10 Jul 2024 06:57:44 -0300 Subject: [PATCH 161/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 563728b39..0427bbc85 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.3" +version = "0.46.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 076db1aa202d001c6a623eeaf17b2e5cd710fbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Jul 2024 10:18:13 -0300 Subject: [PATCH 162/423] Add rad units to angles (#930) --- src/vectors.jl | 4 ++-- test/polytopes.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vectors.jl b/src/vectors.jl index 54b61da21..c94e1eb89 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -74,11 +74,11 @@ See . ``` """ function ∠(u::Vec{2}, v::Vec{2}) # preserve sign - θ = atan(u × v, u ⋅ v) + θ = atan(u × v, u ⋅ v) * u"rad" θ == oftype(θ, -π) ? -θ : θ end -∠(u::Vec{3}, v::Vec{3}) = atan(norm(u × v), u ⋅ v) # discard sign +∠(u::Vec{3}, v::Vec{3}) = atan(norm(u × v), u ⋅ v) * u"rad" # discard sign # ----------- # IO METHODS diff --git a/test/polytopes.jl b/test/polytopes.jl index 31ac4db66..8334e35a8 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -401,8 +401,8 @@ # test angles t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45], atol=8 * eps(T))) - @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45], atol=8 * eps(T))) + @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45] * u"°", atol=8 * eps(T))) + @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45] * u"°", atol=8 * eps(T))) # Triangle in 3D space t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) From dafd5d8ca2343271400f4d5ef35807bb456c5a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 11 Jul 2024 13:13:30 -0300 Subject: [PATCH 163/423] Cleanup Dim usage (#931) * Rmove unused dim from dispatch * Refactor Ellipsoid constructors * Generalize point in triangle --- src/predicates/in.jl | 33 +++++++---------------------- src/predicates/iscoplanar.jl | 2 +- src/primitives/circle.jl | 2 +- src/primitives/cylinder.jl | 4 ++-- src/primitives/cylindersurface.jl | 4 ++-- src/primitives/ellipsoid.jl | 9 ++++---- src/primitives/paraboloidsurface.jl | 8 +++---- src/primitives/plane.jl | 4 ++-- src/primitives/point.jl | 3 +-- src/primitives/sphere.jl | 4 ++-- src/primitives/torus.jl | 8 +++---- src/tesselation.jl | 2 +- src/winding.jl | 4 ++-- 13 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 073243263..071f3507d 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -30,7 +30,7 @@ end Base.in(p::Point, c::Chain) = any(s -> p ∈ s, segments(c)) -Base.in(p::Point{3}, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) +Base.in(p::Point, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) Base.in(p::Point, b::Box) = minimum(b) ⪯ p ⪯ maximum(b) @@ -48,7 +48,7 @@ function Base.in(p::Point{Dim}, s::Sphere{Dim}) where {Dim} isapproxequal(s, r) end -function Base.in(p::Point{3}, d::Disk) +function Base.in(p::Point, d::Disk) p ∉ plane(d) && return false c = center(d) r = radius(d) @@ -56,7 +56,7 @@ function Base.in(p::Point{3}, d::Disk) s < r || isapproxequal(s, r) end -function Base.in(p::Point{3}, c::Circle) +function Base.in(p::Point, c::Circle) p ∉ plane(c) && return false o = center(c) r = radius(c) @@ -64,7 +64,7 @@ function Base.in(p::Point{3}, c::Circle) isapproxequal(s, r) end -function Base.in(p::Point{3}, c::Cone) +function Base.in(p::Point, c::Cone) a = apex(c) b = center(base(c)) ax = a - b @@ -73,7 +73,7 @@ function Base.in(p::Point{3}, c::Cone) ∠(b, a, p) ≤ halfangle(c) end -function Base.in(p::Point{3}, c::Cylinder) +function Base.in(p::Point, c::Cylinder) b = bottom(c)(0, 0) t = top(c)(0, 0) r = radius(c) @@ -83,7 +83,7 @@ function Base.in(p::Point{3}, c::Cylinder) norm((p - b) × a) / norm(a) ≤ r end -function Base.in(p::Point{3}, f::Frustum) +function Base.in(p::Point, f::Frustum) t = center(top(f)) b = center(bottom(f)) ax = b - t @@ -101,7 +101,7 @@ function Base.in(p::Point{3}, f::Frustum) rd ≤ r end -function Base.in(p::Point{3}, t::Torus) +function Base.in(p::Point, t::Torus) ℒ = lentype(p) R, r = radii(t) c, n = center(t), normal(t) @@ -110,24 +110,7 @@ function Base.in(p::Point{3}, t::Torus) (R - √(x^2 + y^2))^2 + z^2 ≤ r^2 end -function Base.in(p::Point{2}, t::Triangle{2}) - # given coordinates - a, b, c = vertices(t) - x₁, y₁ = to(a) - x₂, y₂ = to(b) - x₃, y₃ = to(c) - x, y = to(p) - - # barycentric coordinates - λ₁ = ((y₂ - y₃) * (x - x₃) + (x₃ - x₂) * (y - y₃)) / ((y₂ - y₃) * (x₁ - x₃) + (x₃ - x₂) * (y₁ - y₃)) - λ₂ = ((y₃ - y₁) * (x - x₃) + (x₁ - x₃) * (y - y₃)) / ((y₂ - y₃) * (x₁ - x₃) + (x₃ - x₂) * (y₁ - y₃)) - λ₃ = 1 - λ₁ - λ₂ - - # barycentric check - 0 ≤ λ₁ ≤ 1 && 0 ≤ λ₂ ≤ 1 && 0 ≤ λ₃ ≤ 1 -end - -function Base.in(p::Point{3}, t::Triangle{3}) +function Base.in(p::Point, t::Triangle) # given coordinates a, b, c = vertices(t) diff --git a/src/predicates/iscoplanar.jl b/src/predicates/iscoplanar.jl index 85dc93339..d1a0a3f55 100644 --- a/src/predicates/iscoplanar.jl +++ b/src/predicates/iscoplanar.jl @@ -7,4 +7,4 @@ Tells whether or not the points `A`, `B`, `C` and `D` are coplanar. """ -iscoplanar(A::Point{3}, B::Point{3}, C::Point{3}, D::Point{3}) = isapproxzero(volume(Tetrahedron(A, B, C, D))) +iscoplanar(A::Point, B::Point, C::Point, D::Point) = isapproxzero(volume(Tetrahedron(A, B, C, D))) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 0edbd1d0d..40352ab33 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -23,7 +23,7 @@ Circle(plane::Plane, radius) = Circle(plane, addunit(radius, u"m")) A circle passing through points `p1`, `p2` and `p3`. """ -function Circle(p1::Point{3}, p2::Point{3}, p3::Point{3}) +function Circle(p1::Point, p2::Point, p3::Point) v12 = p2 - p1 v13 = p3 - p1 m12 = to(p1 + v12 / 2) diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index 4e354ba50..ce3225dba 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -33,7 +33,7 @@ end Cylinder(bot::P, top::P, radius) where {P<:Plane} = Cylinder(bot, top, addunit(radius, u"m")) -function Cylinder(start::Point{3}, finish::Point{3}, radius) +function Cylinder(start::Point, finish::Point, radius) dir = finish - start bot = Plane(start, dir) top = Plane(finish, dir) @@ -42,7 +42,7 @@ end Cylinder(start::Tuple, finish::Tuple, radius) = Cylinder(Point(start), Point(finish), radius) -Cylinder(start::Point{3}, finish::Point{3}) = Cylinder(start, finish, oneunit(lentype(start))) +Cylinder(start::Point, finish::Point) = Cylinder(start, finish, oneunit(lentype(start))) Cylinder(start::Tuple, finish::Tuple) = Cylinder(Point(start), Point(finish)) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 12d5c4224..97ae156a2 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -33,7 +33,7 @@ end CylinderSurface(bot::P, top::P, radius) where {P<:Plane} = CylinderSurface(bot, top, addunit(radius, u"m")) -function CylinderSurface(start::Point{3}, finish::Point{3}, radius) +function CylinderSurface(start::Point, finish::Point, radius) dir = finish - start bot = Plane(start, dir) top = Plane(finish, dir) @@ -42,7 +42,7 @@ end CylinderSurface(start::Tuple, finish::Tuple, radius) = CylinderSurface(Point(start), Point(finish), radius) -CylinderSurface(start::Point{3}, finish::Point{3}) = CylinderSurface(start, finish, oneunit(lentype(start))) +CylinderSurface(start::Point, finish::Point) = CylinderSurface(start, finish, oneunit(lentype(start))) CylinderSurface(start::Tuple, finish::Tuple) = CylinderSurface(Point(start), Point(finish)) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 1ddfb5d61..2aecab21c 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -15,12 +15,13 @@ struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{3,C} new{float(ℒ),C,R}(radii, center, rotation) end -Ellipsoid(radii::NTuple{3}, center::Point{3}, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) +Ellipsoid(radii::Tuple, center::Point, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) -Ellipsoid(radii::NTuple{3}, center::NTuple{3}, rotation) = Ellipsoid(radii, Point(center), rotation) +Ellipsoid(radii::Tuple, center::Tuple, rotation) = Ellipsoid(radii, Point(center), rotation) -Ellipsoid(radii::NTuple{3,T}, center=(zero(T), zero(T), zero(T)), rotation=I) where {T} = - Ellipsoid(radii, center, rotation) +Ellipsoid(radii::Tuple, center=(_zero(radii), _zero(radii), _zero(radii)), rotation=I) = Ellipsoid(radii, center, rotation) + +_zero(radii) = zero(first(radii)) paramdim(::Type{<:Ellipsoid}) = 2 diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 455862094..00f371f4a 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -40,19 +40,19 @@ struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{3,C} new{C,float(ℒ)}(apex, radius, focallength) end -ParaboloidSurface(apex::Point{3}, radius::Len, focallength::Len) = +ParaboloidSurface(apex::Point, radius::Len, focallength::Len) = ParaboloidSurface(apex, promote(radius, focallength)...) -ParaboloidSurface(apex::Point{3}, radius, focallength) = +ParaboloidSurface(apex::Point, radius, focallength) = ParaboloidSurface(apex, addunit(radius, u"m"), addunit(focallength, u"m")) ParaboloidSurface(apex::Tuple, radius, focallength) = ParaboloidSurface(Point(apex), radius, focallength) -ParaboloidSurface(apex::Point{3}, radius) = ParaboloidSurface(apex, radius, oneunit(radius)) +ParaboloidSurface(apex::Point, radius) = ParaboloidSurface(apex, radius, oneunit(radius)) ParaboloidSurface(apex::Tuple, radius) = ParaboloidSurface(Point(apex), radius) -ParaboloidSurface(apex::Point{3}) = ParaboloidSurface(apex, oneunit(lentype(apex))) +ParaboloidSurface(apex::Point) = ParaboloidSurface(apex, oneunit(lentype(apex))) ParaboloidSurface(apex::Tuple) = ParaboloidSurface(Point(apex)) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 5a0542443..675e8ba87 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -19,7 +19,7 @@ struct Plane{C<:CRS,ℒ<:Len} <: Primitive{3,C} v::Vec{3,ℒ} end -function Plane(p::Point{3}, n::Vec{3}) +function Plane(p::Point, n::Vec) u, v = householderbasis(n) Plane(p, u, v) end @@ -28,7 +28,7 @@ Plane(p::Tuple, u::Tuple, v::Tuple) = Plane(Point(p), Vec(u), Vec(v)) Plane(p::Tuple, n::Tuple) = Plane(Point(p), Vec(n)) -function Plane(p1::Point{3}, p2::Point{3}, p3::Point{3}) +function Plane(p1::Point, p2::Point, p3::Point) t = Triangle(p1, p2, p3) if isapproxzero(area(t)) throw(ArgumentError("The three points are colinear.")) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index ea682c272..e1465a457 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -124,8 +124,7 @@ See . ∠(Point(1,0), Point(0,0), Point(0,1)) == π/2 ``` """ -∠(A::P, B::P, C::P) where {P<:Point{2}} = ∠(A - B, C - B) -∠(A::P, B::P, C::P) where {P<:Point{3}} = ∠(A - B, C - B) +∠(A::P, B::P, C::P) where {P<:Point} = ∠(A - B, C - B) Random.rand(rng::Random.AbstractRNG, ::Type{Point{Dim}}) where {Dim} = Point(rand(rng, Cartesian{NoDatum,Dim})) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 2b04e3f12..ed38add4d 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -28,7 +28,7 @@ Sphere(center::Tuple) = Sphere(Point(center)) A 2D sphere passing through points `p1`, `p2` and `p3`. """ -function Sphere(p1::Point{2}, p2::Point{2}, p3::Point{2}) +function Sphere(p1::Point, p2::Point, p3::Point) x1, y1 = p2 - p1 x2, y2 = p3 - p2 c1 = centroid(Segment(p1, p2)) @@ -47,7 +47,7 @@ Sphere(p1::Tuple, p2::Tuple, p3::Tuple) = Sphere(Point(p1), Point(p2), Point(p3) A 3D sphere passing through points `p1`, `p2`, `p3` and `p4`. """ -function Sphere(p1::Point{3}, p2::Point{3}, p3::Point{3}, p4::Point{3}) +function Sphere(p1::Point, p2::Point, p3::Point, p4::Point) v1 = p1 - p4 v2 = p2 - p4 v3 = p3 - p4 diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 27dcbbc79..89f5a5876 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -18,12 +18,12 @@ struct Torus{C<:CRS,ℒ<:Len} <: Primitive{3,C} new{C,float(ℒ)}(center, normal, major, minor) end -function Torus(center::Point{3}, normal::Vec{3}, major::Len, minor::Len) +function Torus(center::Point, normal::Vec, major::Len, minor::Len) ℒ = promote_type(eltype(normal), typeof(major), typeof(minor)) Torus(center, Vec{3,ℒ}(normal), ℒ(major), ℒ(minor)) end -Torus(center::Point{3}, normal::Vec{3}, major, minor) = +Torus(center::Point, normal::Vec, major, minor) = Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(normal), major, minor) @@ -34,13 +34,13 @@ Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(nor The torus whose centerline passes through points `p1`, `p2` and `p3` and with minor radius `minor`. """ -function Torus(p1::Point{3}, p2::Point{3}, p3::Point{3}, minor::Len) +function Torus(p1::Point, p2::Point, p3::Point, minor::Len) c = Circle(p1, p2, p3) p = Plane(p1, p2, p3) Torus(center(c), normal(p), radius(c), minor) end -Torus(p1::Point{3}, p2::Point{3}, p3::Point{3}, minor) = Torus(p1, p2, p3, addunit(minor, u"m")) +Torus(p1::Point, p2::Point, p3::Point, minor) = Torus(p1, p2, p3, addunit(minor, u"m")) Torus(p1::Tuple, p2::Tuple, p3::Tuple, minor) = Torus(Point(p1), Point(p2), Point(p3), minor) diff --git a/src/tesselation.jl b/src/tesselation.jl index a7bb445e4..949d0051d 100644 --- a/src/tesselation.jl +++ b/src/tesselation.jl @@ -18,7 +18,7 @@ If the `method` is ommitted, a default algorithm is used. """ function tesselate end -tesselate(points::AbstractVector{<:Point{2}}, method::TesselationMethod) = tesselate(PointSet(points), method) +tesselate(points::AbstractVector{<:Point}, method::TesselationMethod) = tesselate(PointSet(points), method) # ---------------- # IMPLEMENTATIONS diff --git a/src/winding.jl b/src/winding.jl index 9d51e9ac1..c1f9e3ac8 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -41,7 +41,7 @@ winding(point::Point, ring::Ring) = winding((point,), ring) |> first # ------- # Jacobson et al 2013. -function winding(points, mesh::Mesh{3}) +function winding(points, mesh::Mesh) assertion(paramdim(mesh) == 2, "winding number only defined for surface meshes") (eltype(mesh) <: Triangle) || return winding(points, simplexify(mesh)) @@ -64,4 +64,4 @@ function winding(points, mesh::Mesh{3}) tcollect(w(p) for p in points) end -winding(point::Point{3}, mesh::Mesh{3}) = winding((point,), mesh) |> first +winding(point::Point, mesh::Mesh) = winding((point,), mesh) |> first From 585d09c589054a1a51090ec765596c89a46962fd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:15:26 -0300 Subject: [PATCH 164/423] :robot: Format .jl files (#932) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/primitives/ellipsoid.jl | 3 ++- src/primitives/paraboloidsurface.jl | 3 +-- src/primitives/torus.jl | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 2aecab21c..ea0d16047 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -19,7 +19,8 @@ Ellipsoid(radii::Tuple, center::Point, rotation) = Ellipsoid(addunit.(radii, u"m Ellipsoid(radii::Tuple, center::Tuple, rotation) = Ellipsoid(radii, Point(center), rotation) -Ellipsoid(radii::Tuple, center=(_zero(radii), _zero(radii), _zero(radii)), rotation=I) = Ellipsoid(radii, center, rotation) +Ellipsoid(radii::Tuple, center=(_zero(radii), _zero(radii), _zero(radii)), rotation=I) = + Ellipsoid(radii, center, rotation) _zero(radii) = zero(first(radii)) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 00f371f4a..e58c6f05b 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -40,8 +40,7 @@ struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{3,C} new{C,float(ℒ)}(apex, radius, focallength) end -ParaboloidSurface(apex::Point, radius::Len, focallength::Len) = - ParaboloidSurface(apex, promote(radius, focallength)...) +ParaboloidSurface(apex::Point, radius::Len, focallength::Len) = ParaboloidSurface(apex, promote(radius, focallength)...) ParaboloidSurface(apex::Point, radius, focallength) = ParaboloidSurface(apex, addunit(radius, u"m"), addunit(focallength, u"m")) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 89f5a5876..0a9700bdb 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -23,8 +23,7 @@ function Torus(center::Point, normal::Vec, major::Len, minor::Len) Torus(center, Vec{3,ℒ}(normal), ℒ(major), ℒ(minor)) end -Torus(center::Point, normal::Vec, major, minor) = - Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) +Torus(center::Point, normal::Vec, major, minor) = Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(normal), major, minor) From e79872cb7e4a5c2f504969d73aa015f26425723d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 12 Jul 2024 11:36:21 -0300 Subject: [PATCH 165/423] Implement isperiodic for Cylinder --- src/predicates/isperiodic.jl | 6 +++++- test/predicates.jl | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index d335c4877..793a7b3c9 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -32,6 +32,8 @@ isperiodic(::Type{<:Disk}) = (false, true) isperiodic(::Type{<:Circle}) = (true,) +isperiodic(::Type{<:Cylinder}) = (false, true, false) + isperiodic(::Type{<:CylinderSurface}) = (true, false) isperiodic(::Type{<:ConeSurface}) = (true, false) @@ -42,7 +44,9 @@ isperiodic(::Type{<:ParaboloidSurface}) = (false, true) isperiodic(::Type{<:Torus}) = (true, true) -isperiodic(c::Type{<:Chain}) = (isclosed(c),) +isperiodic(::Type{<:Rope}) = (false,) + +isperiodic(::Type{<:Ring}) = (true,) isperiodic(::Type{<:Quadrangle}) = (false, false) diff --git a/test/predicates.jl b/test/predicates.jl index 59f370ad4..34c613eaa 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -147,6 +147,8 @@ @test isperiodic(Sphere{2}) == (true,) @test isperiodic(Sphere{3}) == (true, true) @test isperiodic(Ellipsoid) == (true, true) + @test isperiodic(Cylinder) == (false, true, false) + @test isperiodic(CylinderSurface) == (true, false) @test isperiodic(ParaboloidSurface) == (false, true) @test isperiodic(Torus) == (true, true) From 36fa41ab3179ea3fdbac744228f99177ee5b2871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 12 Jul 2024 11:46:18 -0300 Subject: [PATCH 166/423] Implement RegularSampling for Cylinder --- src/sampling/regular.jl | 27 ++++++++++++++++++--------- test/sampling.jl | 12 ++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 9d8986b16..222abd2f2 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -33,41 +33,50 @@ function sample(::AbstractRNG, geom::Geometry, method::RegularSampling) tₑ = ntuple(i -> T(1 - δₑ[i](sz[i])), D) rs = (range(tₛ[i], stop=tₑ[i], length=sz[i]) for i in 1:D) iᵣ = (geom(uv...) for uv in Iterators.product(rs...)) - iₚ = (p for p in extrapoints(geom)) + iₚ = (p for p in extrapoints(geom, sz)) Iterators.flatmap(identity, (iᵣ, iₚ)) end firstoffset(g::Geometry) = ntuple(i -> (n -> zero(n)), paramdim(g)) lastoffset(g::Geometry) = ntuple(i -> (n -> isperiodic(g)[i] ? inv(n) : zero(n)), paramdim(g)) -extrapoints(::Geometry) = () +extrapoints(::Geometry, sz) = () firstoffset(d::Disk) = (n -> inv(n), firstoffset(boundary(d))...) lastoffset(d::Disk) = (n -> zero(n), lastoffset(boundary(d))...) -extrapoints(d::Disk) = (center(d),) +extrapoints(d::Disk, sz) = (center(d),) firstoffset(b::Ball) = (n -> inv(n), firstoffset(boundary(b))...) lastoffset(b::Ball) = (n -> zero(n), lastoffset(boundary(b))...) -extrapoints(b::Ball) = (center(b),) +extrapoints(b::Ball, sz) = (center(b),) firstoffset(::Sphere{3}) = (n -> inv(n + 1), n -> zero(n)) lastoffset(::Sphere{3}) = (n -> inv(n + 1), n -> inv(n)) -extrapoints(s::Sphere{3}) = (s(0, 0), s(1, 0)) +extrapoints(s::Sphere{3}, sz) = (s(0, 0), s(1, 0)) firstoffset(::Ellipsoid) = (n -> inv(n + 1), n -> zero(n)) lastoffset(::Ellipsoid) = (n -> inv(n + 1), n -> inv(n)) -extrapoints(e::Ellipsoid) = (e(0, 0), e(1, 0)) +extrapoints(e::Ellipsoid, sz) = (e(0, 0), e(1, 0)) + +firstoffset(::Cylinder) = (n -> inv(n), n -> zero(n), n -> zero(n)) +lastoffset(::Cylinder) = (n -> zero(n), n -> inv(n), n -> zero(n)) +function extrapoints(c::Cylinder, sz) + b = bottom(c)(0, 0) + t = top(c)(0, 0) + s = Segment(b, t) + [s(t) for t in range(0, 1, sz[3])] +end firstoffset(::CylinderSurface) = (n -> zero(n), n -> zero(n)) lastoffset(::CylinderSurface) = (n -> inv(n), n -> zero(n)) -extrapoints(c::CylinderSurface) = (bottom(c)(0, 0), top(c)(0, 0)) +extrapoints(c::CylinderSurface, sz) = (bottom(c)(0, 0), top(c)(0, 0)) firstoffset(::ConeSurface) = (n -> zero(n), n -> inv(n)) lastoffset(::ConeSurface) = (n -> inv(n), n -> zero(n)) -extrapoints(c::ConeSurface) = (apex(c), base(c)(0, 0)) +extrapoints(c::ConeSurface, sz) = (apex(c), base(c)(0, 0)) firstoffset(::FrustumSurface) = (n -> zero(n), n -> zero(n)) lastoffset(::FrustumSurface) = (n -> inv(n), n -> zero(n)) -extrapoints(c::FrustumSurface) = (bottom(c)(0, 0), top(c)(0, 0)) +extrapoints(c::FrustumSurface, sz) = (bottom(c)(0, 0), top(c)(0, 0)) # -------------- # SPECIAL CASES diff --git a/test/sampling.jl b/test/sampling.jl index 62146548f..135912f44 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -204,6 +204,18 @@ ps = sample(b, RegularSampling(3, 2, 3)) @test all(∈(b), ps) + # cylinder with parallel planes + c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) + ps = sample(c, RegularSampling(2, 20, 10)) + cs = to.(ps) + xs = getindex.(cs, 1) + ys = getindex.(cs, 2) + zs = getindex.(cs, 3) + @test length(cs) == 200 + 200 + 10 + @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) + @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) + @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) + # cylinder surface with parallel planes c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) ps = sample(c, RegularSampling(20, 10)) From 40ea56627135db2cc50fdcf558515607f425ef74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 12 Jul 2024 12:22:21 -0300 Subject: [PATCH 167/423] Add Wedge geometry --- src/Meshes.jl | 3 ++- src/boundary.jl | 5 ++++ src/discretization/tetra.jl | 5 ++++ src/polytopes.jl | 1 + src/polytopes/wedge.jl | 20 ++++++++++++++++ test/polytopes.jl | 47 +++++++++++++++++++++++++++++++++++-- 6 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/polytopes/wedge.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index c0d88627e..4c7e8d43b 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -205,8 +205,9 @@ export PolyArea, Polyhedron, Tetrahedron, - Pyramid, Hexahedron, + Pyramid, + Wedge, vertex, vertices, nvertices, diff --git a/src/boundary.jl b/src/boundary.jl index 17f1f37d4..182a49ac5 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -102,6 +102,11 @@ function boundary(p::Pyramid) SimpleMesh(pointify(p), connect.(indices)) end +function boundary(w::Wedge) + indices = [(1, 3, 2), (4, 5, 6), (1, 2, 5, 4), (2, 3, 6, 5), (3, 1, 4, 6)] + SimpleMesh(pointify(w), connect.(indices)) +end + function boundary(m::Multi) bounds = [boundary(geom) for geom in parent(m)] valid = filter(!isnothing, bounds) diff --git a/src/discretization/tetra.jl b/src/discretization/tetra.jl index 6044cbb5f..236170ad2 100644 --- a/src/discretization/tetra.jl +++ b/src/discretization/tetra.jl @@ -23,3 +23,8 @@ function discretize(pyramid::Pyramid, ::Tetrahedralization) indices = [(1, 2, 4, 5), (3, 4, 2, 5)] SimpleMesh(pointify(pyramid), connect.(indices, Tetrahedron)) end + +function discretize(wedge::Wedge, ::Tetrahedralization) + indices = [(1, 2, 3, 4), (4, 5, 6, 2), (4, 5, 6, 3)] + SimpleMesh(pointify(wedge), connect.(indices, Tetrahedron)) +end diff --git a/src/polytopes.jl b/src/polytopes.jl index bae5b0b3b..30d0fcf7e 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -204,6 +204,7 @@ const Polyhedron = Polytope{3} include("polytopes/tetrahedron.jl") include("polytopes/hexahedron.jl") include("polytopes/pyramid.jl") +include("polytopes/wedge.jl") # ----------------------- # N-POLYTOPE (FALLBACKS) diff --git a/src/polytopes/wedge.jl b/src/polytopes/wedge.jl new file mode 100644 index 000000000..aa2196fd2 --- /dev/null +++ b/src/polytopes/wedge.jl @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + Wedge(p1, p2, p3, p4, p5, p6) + +A Wedge with points `p1`, `p2`, `p3`, `p4`, `p5`, `p6`. +""" +@polytope Wedge 3 6 + +nvertices(::Type{<:Wedge}) = 6 + +==(t₁::Wedge, t₂::Wedge) = t₁.vertices == t₂.vertices + +Base.isapprox(t₁::Wedge, t₂::Wedge; atol=atol(lentype(t₁)), kwargs...) = + all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Wedge{Dim}}) where {Dim} = + Wedge(ntuple(i -> rand(rng, Point{Dim}), 6)) diff --git a/test/polytopes.jl b/test/polytopes.jl index 8334e35a8..c44f9b92d 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -968,8 +968,6 @@ @test m[3] isa Triangle @test m[4] isa Triangle @test m[5] isa Triangle - - p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) equaltest(p) isapproxtest(p) @@ -997,5 +995,50 @@ ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end + + @test paramdim(Wedge) == 3 + @test nvertices(Wedge) == 6 + + w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) + @test crs(w) <: Cartesian{NoDatum} + @test Meshes.lentype(w) == ℳ + @test volume(w) ≈ T(1 / 2) * u"m^3" + m = boundary(w) + @test m isa Mesh + @test nelements(m) == 5 + @test m[1] isa Triangle + @test m[2] isa Triangle + @test m[3] isa Quadrangle + @test m[4] isa Quadrangle + @test m[5] isa Quadrangle + equaltest(w) + isapproxtest(w) + + w = rand(Wedge{3}) + @test w isa Wedge + @test embeddim(w) == 3 + @test Meshes.lentype(w) === Meshes.Met{Float64} + + w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) + @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), w) == """ + Wedge + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), w) == """ + Wedge + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" + end end end From 5e50b68aab03b3cb3b47f6e06ada89f0f4ba6527 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:27:14 -0300 Subject: [PATCH 168/423] :robot: Format .jl files (#933) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/polytopes/wedge.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/polytopes/wedge.jl b/src/polytopes/wedge.jl index aa2196fd2..a7795f20d 100644 --- a/src/polytopes/wedge.jl +++ b/src/polytopes/wedge.jl @@ -16,5 +16,4 @@ nvertices(::Type{<:Wedge}) = 6 Base.isapprox(t₁::Wedge, t₂::Wedge; atol=atol(lentype(t₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) -Random.rand(rng::Random.AbstractRNG, ::Type{Wedge{Dim}}) where {Dim} = - Wedge(ntuple(i -> rand(rng, Point{Dim}), 6)) +Random.rand(rng::Random.AbstractRNG, ::Type{Wedge{Dim}}) where {Dim} = Wedge(ntuple(i -> rand(rng, Point{Dim}), 6)) From 1b361132a316f017181934ad5bfef82ba0a02458 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 12 Jul 2024 16:02:29 -0300 Subject: [PATCH 169/423] Add support for using 'rand' without 'Dim' for all geometries (#934) --- src/polytopes/hexahedron.jl | 2 ++ src/polytopes/ngon.jl | 2 ++ src/polytopes/polyarea.jl | 2 ++ src/polytopes/pyramid.jl | 2 ++ src/polytopes/ring.jl | 2 ++ src/polytopes/rope.jl | 2 ++ src/polytopes/segment.jl | 2 ++ src/polytopes/tetrahedron.jl | 2 ++ src/polytopes/wedge.jl | 2 ++ src/primitives/ball.jl | 2 ++ src/primitives/bezier.jl | 2 ++ src/primitives/box.jl | 2 ++ src/primitives/line.jl | 2 ++ src/primitives/point.jl | 2 ++ src/primitives/ray.jl | 2 ++ src/primitives/sphere.jl | 2 ++ test/polytopes.jl | 36 ++++++++++++++++++++++++++++++++++++ test/primitives.jl | 20 ++++++++++++++++++++ 18 files changed, 88 insertions(+) diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index a3c4d0608..46637e410 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -36,3 +36,5 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{Hexahedron{Dim}}) where {Dim} = Hexahedron(ntuple(i -> rand(rng, Point{Dim}), 8)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Hexahedron}) = rand(rng, Hexahedron{3}) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index b8faca136..536ab2016 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -67,6 +67,8 @@ signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) Random.rand(rng::Random.AbstractRNG, ::Type{Ngon{N,Dim}}) where {N,Dim} = Ngon{N}(ntuple(i -> rand(rng, Point{Dim}), N)) +Random.rand(rng::Random.AbstractRNG, ::Type{Ngon{N}}) where {N} = rand(rng, Ngon{N,3}) + # ---------- # TRIANGLES # ---------- diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 636a71f68..750e1d692 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -115,3 +115,5 @@ function Base.show(io::IO, ::MIME"text/plain", p::PolyArea) end Random.rand(rng::Random.AbstractRNG, ::Type{PolyArea{Dim}}) where {Dim} = PolyArea(rand(rng, Ring{Dim})) + +Random.rand(rng::Random.AbstractRNG, ::Type{PolyArea}) = rand(rng, PolyArea{3}) diff --git a/src/polytopes/pyramid.jl b/src/polytopes/pyramid.jl index 36974c6b8..561861a09 100644 --- a/src/polytopes/pyramid.jl +++ b/src/polytopes/pyramid.jl @@ -17,3 +17,5 @@ Base.isapprox(p₁::Pyramid, p₂::Pyramid; atol=atol(lentype(p₁)), kwargs...) all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid{Dim}}) where {Dim} = Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid}) = rand(rng, Pyramid{3}) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 14b6e668d..0000e7b97 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -66,6 +66,8 @@ function Random.rand(rng::Random.AbstractRNG, ::Type{Ring{Dim}}) where {Dim} Ring(v) end +Random.rand(rng::Random.AbstractRNG, ::Type{Ring}) = rand(rng, Ring{3}) + """ innerangles(ring) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index ce7359f72..bf8d73440 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -31,3 +31,5 @@ Base.open(r::Rope) = r Base.reverse!(r::Rope) = (reverse!(r.vertices); r) Random.rand(rng::Random.AbstractRNG, ::Type{Rope{Dim}}) where {Dim} = Rope(rand(rng, Point{Dim}, rand(rng, 2:50))) + +Random.rand(rng::Random.AbstractRNG, ::Type{Rope}) = rand(rng, Rope{3}) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index 8ca2fed3e..88f059ecd 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -40,3 +40,5 @@ function (s::Segment)(t) end Random.rand(rng::Random.AbstractRNG, ::Type{Segment{Dim}}) where {Dim} = Segment(ntuple(i -> rand(rng, Point{Dim}), 2)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Segment}) = rand(rng, Segment{3}) diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index be1837f23..38d1912bf 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -27,3 +27,5 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron{Dim}}) where {Dim} = Tetrahedron(ntuple(i -> rand(rng, Point{Dim}), 4)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron}) = rand(rng, Tetrahedron{3}) diff --git a/src/polytopes/wedge.jl b/src/polytopes/wedge.jl index a7795f20d..71d25374b 100644 --- a/src/polytopes/wedge.jl +++ b/src/polytopes/wedge.jl @@ -17,3 +17,5 @@ Base.isapprox(t₁::Wedge, t₂::Wedge; atol=atol(lentype(t₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) Random.rand(rng::Random.AbstractRNG, ::Type{Wedge{Dim}}) where {Dim} = Wedge(ntuple(i -> rand(rng, Point{Dim}), 6)) + +Random.rand(rng::Random.AbstractRNG, ::Type{Wedge}) = rand(rng, Wedge{3}) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index d45906d80..276fdb056 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -66,3 +66,5 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{Ball{Dim}}) where {Dim} = Ball(rand(rng, Point{Dim}), rand(rng, Met{Float64})) + +Random.rand(rng::Random.AbstractRNG, ::Type{Ball}) = rand(rng, Ball{3}) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 598261f77..93f8afae5 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -114,6 +114,8 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve{Dim}}) where {Dim} = BezierCurve(rand(rng, Point{Dim}, 5)) +Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve}) = rand(rng, BezierCurve{3}) + # ----------- # IO METHODS # ----------- diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 1f7b512fc..a99108312 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -60,3 +60,5 @@ function Random.rand(rng::Random.AbstractRNG, ::Type{Box{Dim}}) where {Dim} max = min + rand(rng, Vec{Dim,Met{Float64}}) Box(min, max) end + +Random.rand(rng::Random.AbstractRNG, ::Type{Box}) = rand(rng, Box{3}) diff --git a/src/primitives/line.jl b/src/primitives/line.jl index 9d275f85b..a7ee093fb 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -26,3 +26,5 @@ Base.isapprox(l₁::Line, l₂::Line; atol=atol(lentype(l₁)), kwargs...) = (l::Line)(t) = l.a + t * (l.b - l.a) Random.rand(rng::Random.AbstractRNG, ::Type{Line{Dim}}) where {Dim} = Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) + +Random.rand(rng::Random.AbstractRNG, ::Type{Line}) = rand(rng, Line{3}) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index e1465a457..04ea25c50 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -128,6 +128,8 @@ See . Random.rand(rng::Random.AbstractRNG, ::Type{Point{Dim}}) where {Dim} = Point(rand(rng, Cartesian{NoDatum,Dim})) +Random.rand(rng::Random.AbstractRNG, ::Type{Point}) = rand(rng, Point{3}) + # ----------- # IO METHODS # ----------- diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 9899a7e73..05d3c17e3 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -32,3 +32,5 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{Ray{Dim}}) where {Dim} = Ray(rand(rng, Point{Dim}), rand(rng, Vec{Dim,Met{Float64}})) + +Random.rand(rng::Random.AbstractRNG, ::Type{Ray}) = rand(rng, Ray{3}) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index ed38add4d..e2b92eebc 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -101,3 +101,5 @@ end Random.rand(rng::Random.AbstractRNG, ::Type{Sphere{Dim}}) where {Dim} = Sphere(rand(rng, Point{Dim}), rand(rng, Met{Float64})) + +Random.rand(rng::Random.AbstractRNG, ::Type{Sphere}) = rand(rng, Sphere{3}) diff --git a/test/polytopes.jl b/test/polytopes.jl index c44f9b92d..08fcfd20f 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -73,6 +73,10 @@ @test s isa Segment @test embeddim(s) == 3 @test Meshes.lentype(s) === Meshes.Met{Float64} + s = rand(Segment) + @test s isa Segment + @test embeddim(s) == 3 + @test Meshes.lentype(s) === Meshes.Met{Float64} # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0)) @@ -253,6 +257,10 @@ @test r isa Rope @test embeddim(r) == 3 @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Rope) + @test r isa Rope + @test embeddim(r) == 3 + @test Meshes.lentype(r) === Meshes.Met{Float64} r = rand(Ring{2}) @test r isa Ring @@ -262,6 +270,10 @@ @test r isa Ring @test embeddim(r) == 3 @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Ring) + @test r isa Ring + @test embeddim(r) == 3 + @test Meshes.lentype(r) === Meshes.Met{Float64} # issimple benchmark r = Sphere(cart(0, 0), T(1)) |> pointify |> Ring @@ -334,6 +346,10 @@ @test n isa NGON @test embeddim(n) == 3 @test Meshes.lentype(n) === Meshes.Met{Float64} + n = rand(NGON) + @test n isa NGON + @test embeddim(n) == 3 + @test Meshes.lentype(n) === Meshes.Met{Float64} end # error: the number of vertices must be greater than or equal to 3 @@ -722,6 +738,10 @@ @test p isa PolyArea @test embeddim(p) == 3 @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(PolyArea) + @test p isa PolyArea + @test embeddim(p) == 3 + @test Meshes.lentype(p) === Meshes.Met{Float64} outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) @@ -782,6 +802,10 @@ @test t isa Tetrahedron @test embeddim(t) == 3 @test Meshes.lentype(t) === Meshes.Met{Float64} + t = rand(Tetrahedron) + @test t isa Tetrahedron + @test embeddim(t) == 3 + @test Meshes.lentype(t) === Meshes.Met{Float64} # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) @@ -905,6 +929,10 @@ @test h isa Hexahedron @test embeddim(h) == 3 @test Meshes.lentype(h) === Meshes.Met{Float64} + h = rand(Hexahedron) + @test h isa Hexahedron + @test embeddim(h) == 3 + @test Meshes.lentype(h) === Meshes.Met{Float64} # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) @@ -975,6 +1003,10 @@ @test p isa Pyramid @test embeddim(p) == 3 @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(Pyramid) + @test p isa Pyramid + @test embeddim(p) == 3 + @test Meshes.lentype(p) === Meshes.Met{Float64} p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -1018,6 +1050,10 @@ @test w isa Wedge @test embeddim(w) == 3 @test Meshes.lentype(w) === Meshes.Met{Float64} + w = rand(Wedge) + @test w isa Wedge + @test embeddim(w) == 3 + @test Meshes.lentype(w) === Meshes.Met{Float64} w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" diff --git a/test/primitives.jl b/test/primitives.jl index 25ff159cf..363a0650f 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -48,9 +48,11 @@ @test embeddim(rand(Point{1})) == 1 @test embeddim(rand(Point{2})) == 2 @test embeddim(rand(Point{3})) == 3 + @test embeddim(rand(Point)) == 3 @test Meshes.lentype(rand(Point{1})) == Meshes.Met{Float64} @test Meshes.lentype(rand(Point{2})) == Meshes.Met{Float64} @test Meshes.lentype(rand(Point{3})) == Meshes.Met{Float64} + @test Meshes.lentype(rand(Point)) == Meshes.Met{Float64} @test cart(1) ≈ cart(1 + eps(T)) @test cart(1, 2) ≈ cart(1 + eps(T), T(2)) @@ -234,10 +236,13 @@ r2 = rand(Ray{2}) r3 = rand(Ray{3}) + r = rand(Ray) @test r2 isa Ray @test r3 isa Ray + @test r isa Ray @test embeddim(r2) == 2 @test embeddim(r3) == 3 + @test embeddim(r) == 3 r = Ray(cart(0, 0), vector(1, 1)) @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" @@ -273,10 +278,13 @@ l2 = rand(Line{2}) l3 = rand(Line{3}) + l = rand(Line) @test l2 isa Line @test l3 isa Line + @test l isa Line @test embeddim(l2) == 2 @test embeddim(l3) == 3 + @test embeddim(l) == 3 l = Line(cart(0, 0), cart(1, 1)) @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" @@ -399,10 +407,13 @@ b2 = rand(BezierCurve{2}) b3 = rand(BezierCurve{3}) + b = rand(BezierCurve) @test b2 isa BezierCurve @test b3 isa BezierCurve + @test b isa BezierCurve @test embeddim(b2) == 2 @test embeddim(b3) == 3 + @test embeddim(b) == 3 # datum propagation c1 = Cartesian{WGS84Latest}(T(0), T(0)) @@ -517,12 +528,15 @@ b1 = rand(Box{1}) b2 = rand(Box{2}) b3 = rand(Box{3}) + b = rand(Box) @test b1 isa Box @test b2 isa Box @test b3 isa Box + @test b isa Box @test embeddim(b1) == 1 @test embeddim(b2) == 2 @test embeddim(b3) == 3 + @test embeddim(b) == 3 @test_throws AssertionError Box(cart(1), cart(0)) @test_throws AssertionError Box(cart(1, 1), cart(0, 0)) @@ -630,12 +644,15 @@ b1 = rand(Ball{1}) b2 = rand(Ball{2}) b3 = rand(Ball{3}) + b = rand(Ball) @test b1 isa Ball @test b2 isa Ball @test b3 isa Ball + @test b isa Ball @test embeddim(b1) == 1 @test embeddim(b2) == 2 @test embeddim(b3) == 3 + @test embeddim(b) == 3 b = Ball(cart(0, 0), T(1)) @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" @@ -739,12 +756,15 @@ s1 = rand(Sphere{1}) s2 = rand(Sphere{2}) s3 = rand(Sphere{3}) + s = rand(Sphere) @test s1 isa Sphere @test s2 isa Sphere @test s3 isa Sphere + @test s isa Sphere @test embeddim(s1) == 1 @test embeddim(s2) == 2 @test embeddim(s3) == 3 + @test embeddim(s) == 3 s = Sphere(cart(0, 0, 0), T(1)) @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" From 926defed388fb19ad0eaf40200c05ba5981e564d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 12 Jul 2024 17:19:35 -0300 Subject: [PATCH 170/423] Add RegularDiscretization method for Cylinder (#935) * Add RegularDiscretization method for Cylinder * Refactor n --> nv * Fix machine type in extrapoints of Cylinder --- src/discretization/regular.jl | 37 +++++++++++++++++++++++++++++++++++ src/sampling/regular.jl | 3 ++- test/discretization.jl | 7 +++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index 68f5be1ff..7d11576f5 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -60,6 +60,8 @@ appendtopo(::Sphere{3}, tg) = _appendpoles(tg, 2, true) appendtopo(::Ellipsoid, tg) = _appendpoles(tg, 2, true) +appendtopo(::Cylinder, tg) = _appendaxis(tg) + appendtopo(::CylinderSurface, tg) = _appendpoles(tg, 1, false) appendtopo(::ConeSurface, tg) = _appendpoles(tg, 1, false) @@ -89,6 +91,41 @@ function _appendcenter(tg) SimpleTopology([quads; tris]) end +function _appendaxis(tg) + # auxiliary variables + _, ny, nz = size(tg) + + # number of grid vertices + nvert = nvertices(tg) + + # connect hexahedra in the volume + hexas = collect(elements(tg)) + + # connect axis with wedges + inds = NTuple{6,Int}[] + for k in 1:nz + for j in 1:(ny - 1) + a1 = nvert + k + b1 = cart2corner(tg, 1, j, k) + c1 = cart2corner(tg, 1, j + 1, k) + a2 = nvert + k + 1 + b2 = cart2corner(tg, 1, j, k + 1) + c2 = cart2corner(tg, 1, j + 1, k + 1) + push!(inds, (a1, b1, c1, a2, b2, c2)) + end + a1 = nvert + k + b1 = cart2corner(tg, 1, ny, k) + c1 = cart2corner(tg, 1, 1, k) + a2 = nvert + k + 1 + b2 = cart2corner(tg, 1, ny, k + 1) + c2 = cart2corner(tg, 1, 1, k + 1) + push!(inds, (a1, b1, c1, a2, b2, c2)) + end + wedges = [connect(ind, Wedge) for ind in inds] + + SimpleTopology([hexas; wedges]) +end + # connect north and south poles to # grid topology along given dimension # and counter-clockwise orientation diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 222abd2f2..d29b4b558 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -60,10 +60,11 @@ extrapoints(e::Ellipsoid, sz) = (e(0, 0), e(1, 0)) firstoffset(::Cylinder) = (n -> inv(n), n -> zero(n), n -> zero(n)) lastoffset(::Cylinder) = (n -> zero(n), n -> inv(n), n -> zero(n)) function extrapoints(c::Cylinder, sz) + T = numtype(lentype(c)) b = bottom(c)(0, 0) t = top(c)(0, 0) s = Segment(b, t) - [s(t) for t in range(0, 1, sz[3])] + [s(t) for t in range(zero(T), one(T), sz[3])] end firstoffset(::CylinderSurface) = (n -> zero(n), n -> zero(n)) diff --git a/test/discretization.jl b/test/discretization.jl index ac27400a8..94b8ebb3b 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -360,6 +360,13 @@ @test eltype(mesh) <: Ngon @test nvertices.(mesh) ⊆ [3, 4] + cyl = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) + mesh = discretize(cyl, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 * 11 + 11 + @test nelements(mesh) == 11 * 10 * 10 + @test eltype(mesh) <: Polyhedron + @test nvertices.(mesh) ⊆ [6, 8] + cylsurf = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) mesh = discretize(cylsurf, RegularDiscretization(10)) @test nvertices(mesh) == 10 * 11 + 2 From e08a660f36df508a1d71822868950faf3c43f157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 13 Jul 2024 08:29:05 -0300 Subject: [PATCH 171/423] Add default discretize method for Cylinder --- src/discretization.jl | 2 ++ test/discretization.jl | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/discretization.jl b/src/discretization.jl index 5e150f708..92ad6f052 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -119,6 +119,8 @@ discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(5 discretize(torus::Torus) = discretize(torus, RegularDiscretization(50)) +discretize(cyl::Cylinder) = discretize(cyl, RegularDiscretization(2, 50, 2)) + discretize(cylsurf::CylinderSurface) = discretize(cylsurf, RegularDiscretization(50, 2)) discretize(consurf::ConeSurface) = discretize(consurf, RegularDiscretization(50, 2)) diff --git a/test/discretization.jl b/test/discretization.jl index 94b8ebb3b..5981e8989 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -410,6 +410,12 @@ @test !(eltype(mesh) <: Quadrangle) @test nelements(mesh) == 2600 + cyl = Cylinder(T(1)) + mesh = discretize(cyl) + @test !(eltype(mesh) <: Wedge) + @test !(eltype(mesh) <: Hexahedron) + @test nelements(mesh) == 300 + cylsurf = CylinderSurface(T(1)) mesh = discretize(cylsurf) @test !(eltype(mesh) <: Triangle) From d9ed4705ebda8debc070375d17d9baf4afd6f52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 13 Jul 2024 08:32:25 -0300 Subject: [PATCH 172/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0427bbc85..99d46bcfd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.4" +version = "0.46.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From a40c0ffc515a63cc073bc7007f092141266f5d37 Mon Sep 17 00:00:00 2001 From: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:55:42 +0100 Subject: [PATCH 173/423] Docs admonitions for wrapping DelaunayTriangulation.jl (#936) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Downstream * Update Project.toml Co-authored-by: Júlio Hoffimann * Adhere to note format in the project --------- Co-authored-by: Júlio Hoffimann --- src/discretization/delaunay.jl | 5 +++++ src/tesselation/delaunay.jl | 5 +++++ src/tesselation/voronoi.jl | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl index ec71b4053..b93b01432 100644 --- a/src/discretization/delaunay.jl +++ b/src/discretization/delaunay.jl @@ -12,6 +12,11 @@ Optionally, specify the random number generator `rng`. * Cheng et al. 2012. [Delaunay Mesh Generation] (https://people.eecs.berkeley.edu/~jrs/meshbook.html) + +### Notes + +Wraps DelaunayTriangulation.jl. For any internal errors, file an issue at +[DelaunayTriangulation.jl](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/issues/new) """ struct DelaunayTriangulation{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod rng::RNG diff --git a/src/tesselation/delaunay.jl b/src/tesselation/delaunay.jl index 601fae160..52903d4cc 100644 --- a/src/tesselation/delaunay.jl +++ b/src/tesselation/delaunay.jl @@ -12,6 +12,11 @@ Optionally, specify the random number generator `rng`. * Cheng et al. 2012. [Delaunay Mesh Generation] (https://people.eecs.berkeley.edu/~jrs/meshbook.html) + +### Notes + +Wraps DelaunayTriangulation.jl. For any internal errors, file an issue at +[DelaunayTriangulation.jl](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/issues/new) """ struct DelaunayTesselation{RNG<:AbstractRNG} <: TesselationMethod rng::RNG diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index e393f9851..6ad3e6f64 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -12,6 +12,11 @@ Optionally, specify the random number generator `rng`. * Cheng et al. 2012. [Delaunay Mesh Generation] (https://people.eecs.berkeley.edu/~jrs/meshbook.html) + +### Notes + +Wraps DelaunayTriangulation.jl. For any internal errors, file an issue at +[DelaunayTriangulation.jl](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/issues/new) """ struct VoronoiTesselation{RNG<:AbstractRNG} <: TesselationMethod rng::RNG From 0f0438cd9e4f090b7d5157d21bc1ab30ac5862df Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:29:36 -0300 Subject: [PATCH 174/423] Preserve the CRS with the `withcrs` function (#938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Preserve the CRS with the 'withcrs' function * Replace LatLon with Mercator in tests * Apply suggestions from code review Co-authored-by: Júlio Hoffimann --------- Co-authored-by: Júlio Hoffimann --- src/boundingboxes.jl | 4 +-- src/domains.jl | 2 +- src/intersections/boxes.jl | 4 +-- src/intersections/planes.jl | 2 +- src/mesh/rectilineargrid.jl | 2 +- src/multigeoms.jl | 2 +- src/partitioning/bisectfraction.jl | 2 +- src/polytopes.jl | 2 +- src/polytopes/hexahedron.jl | 2 +- src/polytopes/ngon.jl | 4 +-- src/polytopes/segment.jl | 2 +- src/polytopes/tetrahedron.jl | 2 +- src/predicates/intersects.jl | 2 +- src/primitives/bezier.jl | 2 +- src/primitives/box.jl | 2 +- src/primitives/circle.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/point.jl | 4 +-- src/refinement/catmullclark.jl | 6 ++--- src/refinement/quad.jl | 4 +-- src/refinement/tri.jl | 2 +- src/sets.jl | 2 +- src/transforms.jl | 2 +- src/transforms/affine.jl | 2 +- src/transforms/overlaps.jl | 2 +- src/transforms/shadow.jl | 2 +- src/utils.jl | 11 +++++--- test/boundingboxes.jl | 12 ++++----- test/domains.jl | 7 +++-- test/intersections.jl | 16 +++++------ test/multigeoms.jl | 12 +++------ test/partitioning.jl | 7 +++-- test/polytopes.jl | 43 +++++++++++------------------- test/predicates.jl | 20 +++++++------- test/primitives.jl | 33 ++++++++++------------- test/refinement.jl | 21 +++++++-------- test/runtests.jl | 2 +- test/sets.jl | 8 +++--- test/sideof.jl | 4 +-- test/testutils.jl | 6 ++--- test/transforms.jl | 14 +++++----- test/winding.jl | 10 +++---- 42 files changed, 131 insertions(+), 161 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index b6bf9f26a..afb367502 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -36,7 +36,7 @@ function boundingbox(r::Ray) v = r(1) - r(0) l = lower.(to(p), v) u = upper.(to(p), v) - Box(withdatum(r, l), withdatum(r, u)) + Box(withcrs(r, l), withcrs(r, u)) end function boundingbox(s::Sphere{Dim}) where {Dim} @@ -96,5 +96,5 @@ function _pboxes(points) @. xmin = min(x, xmin) @. xmax = max(x, xmax) end - Box(withdatum(p, xmin), withdatum(p, xmax)) + Box(withcrs(p, xmin), withcrs(p, xmax)) end diff --git a/src/domains.jl b/src/domains.jl index 5b57d3a61..a51753385 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -107,7 +107,7 @@ function centroid(d::Domain) x = vector.(1:n) w = volume.(1:n) all(iszero, w) && (w = ones(eltype(w), n)) - withdatum(d, sum(w .* x) / sum(w)) + withcrs(d, sum(w .* x) / sum(w)) end """ diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index a32600635..923619b85 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -14,8 +14,8 @@ function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} m2, M2 = to.(extrema(box₂)) # relevant vertices - u = withdatum(box₁, max.(promote(m1, m2)...)) - v = withdatum(box₁, min.(promote(M1, M2)...)) + u = withcrs(box₁, max.(promote(m1, m2)...)) + v = withcrs(box₁, min.(promote(M1, M2)...)) # auxiliary variables δ = v - u diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index a63f65cf8..950daf255 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -22,7 +22,7 @@ function intersection(f, plane1::Plane, plane2::Plane) c2 = (h2 - h1 * n1n2) / (1 - n1n2^2) p1 = (c1 * n1) + (c2 * n2) p2 = p1 + d - return @IT Intersecting Line(withdatum(plane1, p1 * u), withdatum(plane1, p2 * u)) f + return @IT Intersecting Line(withcrs(plane1, p1 * u), withcrs(plane1, p2 * u)) f end end diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 1d8b7677c..40e03c629 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -57,7 +57,7 @@ function centroid(g::RectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) - withdatum(g, (to(p1) + to(p2)) / 2) + withcrs(g, (to(p1) + to(p2)) / 2) end function Base.getindex(g::RectilinearGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 11b83d8a9..498812ae5 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -47,7 +47,7 @@ end function centroid(m::Multi) cs = to.(centroid.(m.geoms)) - withdatum(m, sum(cs) / length(cs)) + withcrs(m, sum(cs) / length(cs)) end rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index 7f90df63c..1786a8b80 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -41,7 +41,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP while iter < maxiter m = (a + b) / 2 - bisectpoint = BisectPointPartition(n, withdatum(domain, m)) + bisectpoint = BisectPointPartition(n, withcrs(domain, m)) subsets, metadata = partitioninds(rng, domain, bisectpoint) g = length(subsets[1]) / nelements(domain) diff --git a/src/polytopes.jl b/src/polytopes.jl index 30d0fcf7e..1f0c79fda 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -243,7 +243,7 @@ nvertices(p::Polytope) = nvertices(typeof(p)) Return the centroid of the `polytope`. """ -centroid(p::Polytope) = withdatum(p, sum(to, vertices(p)) / length(vertices(p))) +centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / length(vertices(p))) """ unique(polytope) diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index 46637e410..a56950673 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -21,7 +21,7 @@ function (h::Hexahedron)(u, v, w) throw(DomainError((u, v, w), "h(u, v, w) is not defined for u, v, w outside [0, 1]³.")) end A1, A2, A4, A3, A5, A6, A8, A7 = to.(h.vertices) - withdatum( + withcrs( h, (1 - u) * (1 - v) * (1 - w) * A1 + u * (1 - v) * (1 - w) * A2 + diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 536ab2016..8e6e00306 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -91,7 +91,7 @@ function (t::Triangle)(u, v) throw(DomainError((u, v), "invalid barycentric coordinates for triangle.")) end v₁, v₂, v₃ = to.(t.vertices) - withdatum(t, v₁ * w + v₂ * u + v₃ * v) + withcrs(t, v₁ * w + v₂ * u + v₃ * v) end # ------------ @@ -104,5 +104,5 @@ function (q::Quadrangle)(u, v) throw(DomainError((u, v), "q(u, v) is not defined for u, v outside [0, 1]².")) end c₀₀, c₀₁, c₁₁, c₁₀ = to.(q.vertices) - withdatum(q, c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v) + withcrs(q, c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v) end diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index 88f059ecd..c44e81a57 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -23,7 +23,7 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] function center(s::Segment) a, b = extrema(s) - withdatum(s, (to(a) + to(b)) / 2) + withcrs(s, (to(a) + to(b)) / 2) end ==(s₁::Segment, s₂::Segment) = s₁.vertices == s₂.vertices diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index 38d1912bf..097b1a0b5 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -22,7 +22,7 @@ function (t::Tetrahedron)(u, v, w) throw(DomainError((u, v, w), "invalid barycentric coordinates for tetrahedron.")) end v₁, v₂, v₃, v₄ = to.(t.vertices) - withdatum(t, v₁ * z + v₂ * u + v₃ * v + v₄ * w) + withcrs(t, v₁ * z + v₂ * u + v₃ * v + v₄ * w) end Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron{Dim}}) where {Dim} = diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 5a8090b59..5d61135b8 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -245,7 +245,7 @@ intersects(m::Multi, c::Chain) = intersects(c, m) # ------------------ # support point in Minkowski difference -minkowskipoint(g₁::Geometry, g₂::Geometry, d) = withdatum(g₁, supportfun(g₁, d) - supportfun(g₂, -d)) +minkowskipoint(g₁::Geometry, g₂::Geometry, d) = withcrs(g₁, supportfun(g₁, d) - supportfun(g₂, -d)) # origin of coordinate system minkowskiorigin(Dim, ℒ) = Point(ntuple(i -> zero(ℒ), Dim)) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 93f8afae5..7b047f8e6 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -109,7 +109,7 @@ function (curve::BezierCurve)(t, ::Horner) end b₀ = bᵢ₋₁ - withdatum(curve, b₀) + withcrs(curve, b₀) end Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve{Dim}}) where {Dim} = BezierCurve(rand(rng, Point{Dim}, 5)) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index a99108312..8f8067abd 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -37,7 +37,7 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -center(b::Box) = withdatum(b, (to(b.max) + to(b.min)) / 2) +center(b::Box) = withcrs(b, (to(b.max) + to(b.min)) / 2) diagonal(b::Box) = norm(b.max - b.min) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 40352ab33..033c74b83 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -32,7 +32,7 @@ function Circle(p1::Point, p2::Point, p3::Point) F = to(p1) ⋅ n⃗ M = transpose([n⃗ v12 v13]) u = [F, m12 ⋅ v12, m13 ⋅ v13] - O = withdatum(p1, uinv(M) * u) + O = withcrs(p1, uinv(M) * u) r = norm(p1 - O) Circle(Plane(O, n⃗), r) end diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 97ae156a2..9198b1a5c 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -63,7 +63,7 @@ top(c::CylinderSurface) = c.top function center(c::CylinderSurface) a = to(c.bot(0, 0)) b = to(c.top(0, 0)) - withdatum(c, (a .+ b) ./ 2) + withcrs(c, (a .+ b) ./ 2) end axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 04ea25c50..02f83644b 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -82,7 +82,7 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withdatum(A, to(A) + v) ++(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withcrs(A, to(A) + v) +(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A + v """ @@ -92,7 +92,7 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withdatum(A, to(A) - v) +-(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withcrs(A, to(A) - v) -(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A - v """ diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index 061b843f6..70969890b 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -34,7 +34,7 @@ function refine(mesh, ::CatmullClark) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - withdatum(mesh, cₒ) + withcrs(mesh, cₒ) end # add midpoints of edges @@ -46,7 +46,7 @@ function refine(mesh, ::CatmullClark) ∑p = sum(to, ps) ∑q = sum(to, qs) M = length(ps) + length(qs) - withdatum(mesh, (∑p + ∑q) / M) + withcrs(mesh, (∑p + ∑q) / M) end # move original vertices @@ -68,7 +68,7 @@ function refine(mesh, ::CatmullClark) sum(to, uv) / 2 end / n - withdatum(mesh, (F + 2R + (n - 3)P) / n) + withcrs(mesh, (F + 2R + (n - 3)P) / n) end # new points in refined mesh diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index 03838cd7b..bc6cfc6e3 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -23,7 +23,7 @@ function refine(mesh, ::QuadRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - withdatum(mesh, cₒ) + withcrs(mesh, cₒ) end # add midpoints of edges @@ -31,7 +31,7 @@ function refine(mesh, ::QuadRefinement) fpts = map(1:nfacets(t)) do edge ps = view(points, ∂₁₀(edge)) cₒ = sum(to, ps) / length(ps) - withdatum(mesh, cₒ) + withcrs(mesh, cₒ) end # original vertices diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index dddff390f..87ab62e7e 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -26,7 +26,7 @@ function refine(mesh, ::TriRefinement) epts = map(1:nelements(t)) do elem ps = view(points, ∂₂₀(elem)) cₒ = sum(to, ps) / length(ps) - withdatum(mesh, cₒ) + withcrs(mesh, cₒ) end # original vertices diff --git a/src/sets.jl b/src/sets.jl index ff9ca5daf..0cb97146e 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -65,4 +65,4 @@ PointSet(points) = PointSet(map(identity, points)) centroid(d::PointSet, ind::Int) = d[ind] -centroid(d::PointSet) = withdatum(d, sum(to, d) / nelements(d)) +centroid(d::PointSet) = withcrs(d, sum(to, d) / nelements(d)) diff --git a/src/transforms.jl b/src/transforms.jl index f3d71cb63..0c36c09ad 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -74,7 +74,7 @@ end applycoord(::CoordinateTransform, x) = x # special treatment for Point -applycoord(t::CoordinateTransform, p::Point) = withdatum(p, applycoord(t, to(p))) +applycoord(t::CoordinateTransform, p::Point) = withcrs(p, applycoord(t, to(p))) # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 657a19f43..ba2abc674 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -55,7 +55,7 @@ end applycoord(t::Affine, v::Vec) = t.A * v -applycoord(t::Affine, p::Point) = withdatum(p, t.A * to(p) + t.b) +applycoord(t::Affine, p::Point) = withcrs(p, t.A * to(p) + t.b) # -------------- # SPECIAL CASES diff --git a/src/transforms/overlaps.jl b/src/transforms/overlaps.jl index 199efa50e..a030507f8 100644 --- a/src/transforms/overlaps.jl +++ b/src/transforms/overlaps.jl @@ -56,6 +56,6 @@ function _overlaps(dim, lims, bbox) max = to(maximum(bbox)) nmin = Vec(ntuple(i -> i == dim ? lmin : min[i], Dim)) nmax = Vec(ntuple(i -> i == dim ? lmax : max[i], Dim)) - Box(withdatum(bbox, nmin), withdatum(bbox, nmax)) + Box(withcrs(bbox, nmin), withcrs(bbox, nmax)) end end diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index 022f2279a..caa51c9d7 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -49,7 +49,7 @@ _sort(dims) = sort(SVector(dims)) _shadow(v::Vec, dims) = v[dims] -_shadow(p::Point, dims) = withdatum(p, to(p)[dims]) +_shadow(p::Point, dims) = withcrs(p, to(p)[dims]) _shadow(::Plane, _) = throw(ArgumentError("Shadow transform doesn't yet support planes")) diff --git a/src/utils.jl b/src/utils.jl index a1b6d04eb..591b86d46 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -43,11 +43,16 @@ function collectat(iter, inds) end """ - withdatum(g, v) + withcrs(g, v) -Point at the end of the vector `v` with the same datum of `g`. +Point at the end of the vector `v` with the same CRS of `g`. """ -withdatum(g::GeometryOrDomain, v::StaticVector) = Point(Cartesian{datum(crs(g))}(Tuple(v))) +function withcrs(g::GeometryOrDomain, v::StaticVector) + C = crs(g) + cart = Cartesian{datum(C)}(Tuple(v)) + ctor = CoordRefSystems.constructor(C) + Point(convert(ctor, cart)) +end """ flat(p) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index c344ba8b2..9002644d9 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -119,12 +119,10 @@ p = ParaboloidSurface(cart(1, 2, 3), T(5), T(4)) @test boundingbox(p) ≈ Box(cart(-4, -3, 3), cart(6, 7, 73 / 16)) - # datum propagation - c = Cartesian{WGS84Latest}(T(-1), T(1)) - r = Ray(Point(c), vector(1, -1)) - @test datum(crs(boundingbox(r))) === WGS84Latest - c = Cartesian{WGS84Latest}(T(0), T(0)) - g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) + # CRS propagation + r = Ray(merc(-1, 1), vector(1, -1)) + @test crs(boundingbox(r)) === crs(r) + g = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) m = convert(SimpleMesh, g) - @test datum(crs(boundingbox(m))) === WGS84Latest + @test crs(boundingbox(m)) === crs(m) end diff --git a/test/domains.jl b/test/domains.jl index 1913f83ab..453972893 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -30,10 +30,9 @@ @test vcat(dom3, dom1) == GeometrySet([collect(dom3); collect(dom1)]) @test vcat(dom1, dom2, dom3) == GeometrySet([collect(dom1); collect(dom2); collect(dom3)]) - # datum propagation - c = Cartesian{WGS84Latest}(T(1), T(1)) - dom = DummyDomain(Point(c)) - @test datum(crs(centroid(dom))) === WGS84Latest + # CRS propagation + dom = DummyDomain(merc(1, 1)) + @test crs(centroid(dom)) === crs(dom) dom = DummyDomain(cart(0, 0)) @test sprint(show, dom) == "3 DummyDomain" diff --git a/test/intersections.jl b/test/intersections.jl index 2730ff1d3..3e110f510 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -765,12 +765,12 @@ @test intersection(p1, p2) |> type == Intersecting @test p1 ∩ p2 == Line(cart(1, 0, 0), cart(1, 1, 0)) - # datum propagation + # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) p1 = Plane(Point(c1), vector(0, 0, 1)) p2 = Plane(Point(c2), vector(1 / sqrt(2), 0, 1 / sqrt(2))) - @test datum(crs(p1 ∩ p2)) === WGS84Latest + @test crs(p1 ∩ p2) === crs(p1) end @testset "Boxes" begin @@ -842,14 +842,10 @@ b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) @inferred someornone(b1, b2) - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(1)) - c3 = Cartesian{WGS84Latest}(T(0.5), T(0.5)) - c4 = Cartesian{WGS84Latest}(T(2), T(2)) - b1 = Box(Point(c1), Point(c2)) - b2 = Box(Point(c3), Point(c4)) - @test datum(crs(b1 ∩ b2)) === WGS84Latest + # CRS propagation + b1 = Box(merc(0, 0), merc(1, 1)) + b2 = Box(merc(0.5, 0.5), merc(2, 2)) + @test crs(b1 ∩ b2) === crs(b1) # Ray-Box intersection b = Box(cart(0, 0, 0), cart(1, 1, 1)) diff --git a/test/multigeoms.jl b/test/multigeoms.jl index c717c9ab6..9c6a8c16b 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -92,13 +92,9 @@ @test Multi([tri, tri]) isa MultiPolygon @test Multi([poly, poly]) isa MultiPolygon - # datum propagation - tuples1 = [T.((0, 0)), T.((1, 0)), T.((1, 1)), T.((0, 1))] - tuples2 = [T.((1, 1)), T.((2, 1)), T.((2, 2)), T.((1, 2))] - points1 = Point.(Cartesian{WGS84Latest}.(tuples1)) - points2 = Point.(Cartesian{WGS84Latest}.(tuples2)) - poly1 = PolyArea(points1) - poly2 = PolyArea(points2) + # CRS propagation + poly1 = PolyArea(merc.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(merc.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) - @test datum(crs(centroid(multi))) === WGS84Latest + @test crs(centroid(multi)) === crs(multi) end diff --git a/test/partitioning.jl b/test/partitioning.jl index 8f95e39e0..a074ae5a2 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -208,11 +208,10 @@ p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) @test p1 == p2 - # datum propagation - c = Cartesian{WGS84Latest}(T(0), T(0)) - g = CartesianGrid((10, 10), Point(c), (T(1), T(1))) + # CRS propagation + g = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - @test datum(crs(first(p))) === WGS84Latest + @test crs(first(p)) === crs(g) end @testset "BallPartition" begin diff --git a/test/polytopes.jl b/test/polytopes.jl index 08fcfd20f..86b3b7a52 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -78,11 +78,9 @@ @test embeddim(s) == 3 @test Meshes.lentype(s) === Meshes.Met{Float64} - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(1)) - s = Segment(Point(c1), Point(c2)) - @test datum(crs(s(T(0)))) === WGS84Latest + # CRS propagation + s = Segment(merc(0, 0), merc(1, 1)) + @test crs(s(T(0))) === crs(s) s = Segment(cart(0, 0), cart(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" @@ -286,11 +284,9 @@ r2 = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test innerangles(r1) ≈ innerangles(r2) - # datum propagation - tuples = [T.((0, 0)), T.((1, 0)), T.((1, 1)), T.((0, 1))] - points = Point.(Cartesian{WGS84Latest}.(tuples)) - r = Ring(points) - @test datum(crs(centroid(r))) === WGS84Latest + # CRS propagation + r = Ring(merc.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test crs(centroid(r)) === crs(r) ri = Ring(cart.([(1, 1), (2, 2), (3, 3)])) ro = Rope(cart.([(1, 1), (2, 2), (3, 3)])) @@ -447,12 +443,9 @@ t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(0)) - c3 = Cartesian{WGS84Latest}(T(0), T(1)) - t = Triangle(Point(c1), Point(c2), Point(c3)) - @test datum(crs(t(T(0), T(0)))) === WGS84Latest + # CRS propagation + t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(t(T(0), T(0))) === crs(t) t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" @@ -522,13 +515,9 @@ @test q(T(1), T(1)) == cart(1, 1, 0) @test q(T(0), T(1)) == cart(0, 1, 1) - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(0)) - c3 = Cartesian{WGS84Latest}(T(1), T(1)) - c4 = Cartesian{WGS84Latest}(T(0), T(1)) - q = Quadrangle(Point(c1), Point(c2), Point(c3), Point(c4)) - @test datum(crs(q(T(0), T(0)))) === WGS84Latest + # CRS propagation + q = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) + @test crs(q(T(0), T(0))) === crs(q) q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" @@ -807,13 +796,13 @@ @test embeddim(t) == 3 @test Meshes.lentype(t) === Meshes.Met{Float64} - # datum propagation + # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) c3 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) c4 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) - @test datum(crs(t(T(0), T(0), T(0)))) === WGS84Latest + @test crs(t(T(0), T(0), T(0))) === crs(t) t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -934,7 +923,7 @@ @test embeddim(h) == 3 @test Meshes.lentype(h) === Meshes.Met{Float64} - # datum propagation + # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) c3 = Cartesian{WGS84Latest}(T(1), T(1), T(0)) @@ -944,7 +933,7 @@ c7 = Cartesian{WGS84Latest}(T(1), T(1), T(1)) c8 = Cartesian{WGS84Latest}(T(0), T(1), T(1)) h = Hexahedron(Point(c1), Point(c2), Point(c3), Point(c4), Point(c5), Point(c6), Point(c7), Point(c8)) - @test datum(crs(h(T(0), T(0), T(0)))) === WGS84Latest + @test crs(h(T(0), T(0), T(0))) === crs(h) h = Hexahedron( cart(0, 0, 0), diff --git a/test/predicates.jl b/test/predicates.jl index 34c613eaa..ec53b3435 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -168,18 +168,18 @@ @test cart(-1, 0, 0) ∉ h @test cart(0, 2, 0) ∉ h - outer = [latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)] - hole1 = [latlon(0.2, 0.2), latlon(0.4, 0.2), latlon(0.4, 0.4), latlon(0.2, 0.4)] - hole2 = [latlon(0.6, 0.2), latlon(0.8, 0.2), latlon(0.8, 0.4), latlon(0.6, 0.4)] + outer = [merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)] + hole1 = [merc(0.2, 0.2), merc(0.4, 0.2), merc(0.4, 0.4), merc(0.2, 0.4)] + hole2 = [merc(0.6, 0.2), merc(0.8, 0.2), merc(0.8, 0.4), merc(0.6, 0.4)] poly = PolyArea([outer, hole1, hole2]) @test all(p ∈ poly for p in outer) - @test latlon(0.5, 0.5) ∈ poly - @test latlon(0.2, 0.6) ∈ poly - @test latlon(1.5, 0.5) ∉ poly - @test latlon(-0.5, 0.5) ∉ poly - @test latlon(0.25, 0.25) ∉ poly - @test latlon(0.75, 0.25) ∉ poly - @test latlon(0.75, 0.75) ∈ poly + @test merc(0.5, 0.5) ∈ poly + @test merc(0.2, 0.6) ∈ poly + @test merc(1.5, 0.5) ∉ poly + @test merc(-0.5, 0.5) ∉ poly + @test merc(0.25, 0.25) ∉ poly + @test merc(0.75, 0.25) ∉ poly + @test merc(0.75, 0.75) ∈ poly end @testset "issubset" begin diff --git a/test/primitives.jl b/test/primitives.jl index 363a0650f..57000a48f 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -167,10 +167,10 @@ @test p ∉ q @test q ∉ p - # datum propagation - c = Cartesian{WGS84Latest}(T(1), T(1)) - @test datum(crs(Point(c) + vector(1, 1))) === WGS84Latest - @test datum(crs(Point(c) - vector(1, 1))) === WGS84Latest + # CRS propagation + p = merc(1, 1) + @test crs(p + vector(1, 1)) === crs(p) + @test crs(p - vector(1, 1)) === crs(p) p = cart(0, 1) @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" @@ -415,12 +415,9 @@ @test embeddim(b3) == 3 @test embeddim(b) == 3 - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(0.5), T(1)) - c3 = Cartesian{WGS84Latest}(T(1), T(1)) - b = BezierCurve(Point(c1), Point(c2), Point(c3)) - @test datum(crs(b(T(0), Horner()))) === WGS84Latest + # CRS propagation + b = BezierCurve(merc(0, 0), merc(0.5, 1), merc(1, 0)) + @test crs(b(T(0), Horner())) === crs(b) b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" @@ -561,11 +558,9 @@ cart(0, 1, 1) ) - # datum propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(1)) - b = Box(Point(c1), Point(c2)) - @test datum(crs(center(b))) === WGS84Latest + # CRS propagation + b = Box(merc(0, 0), merc(1, 1)) + @test crs(center(b)) === crs(b) b = Box(cart(0, 0), cart(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" @@ -901,12 +896,12 @@ @test c isa Circle @test embeddim(c) == 3 - # datum propagation + # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(4), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) c3 = Cartesian{WGS84Latest}(T(0), T(0), T(4)) c = Circle(Point(c1), Point(c2), Point(c3)) - @test datum(crs(c)) === WGS84Latest + @test crs(c) === typeof(c1) p = Plane(cart(0, 0, 0), vector(0, 0, 1)) c = Circle(p, T(2)) @@ -1056,11 +1051,11 @@ @test c isa CylinderSurface @test embeddim(c) == 3 - # datum propagation + # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) c = CylinderSurface(Point(c1), Point(c2), T(1)) - @test datum(crs(center(c))) === WGS84Latest + @test crs(center(c)) === crs(c) c = CylinderSurface(T(1)) @test sprint(show, c) == diff --git a/test/refinement.jl b/test/refinement.jl index 73ebf81b8..5f9a84b17 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -12,11 +12,10 @@ @test_reference "data/trirefine-$T.png" fig end - # datum propagation - c = Cartesian{WGS84Latest}(T(0), T(0)) - grid = CartesianGrid((3, 3), Point(c), (T(1), T(1))) + # CRS propagation + grid = CartesianGrid((3, 3), merc(0, 0), (T(1), T(1))) ref = refine(grid, TriRefinement()) - @test datum(crs(ref)) === WGS84Latest + @test crs(ref) === crs(grid) end @testset "QuadRefinement" begin @@ -35,13 +34,12 @@ @test_reference "data/quadrefine-$T.png" fig end - # datum propagation - tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.25, 0.25)), T.((0.75, 0.25)), T.((0.5, 0.75))] - points = Point.(Cartesian{WGS84Latest}.(tuples)) + # CRS propagation + points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) ref = refine(mesh, QuadRefinement()) - @test datum(crs(ref)) === WGS84Latest + @test crs(ref) === crs(mesh) end @testset "RegularRefinement" begin @@ -114,13 +112,12 @@ @test_reference "data/catmullclark-3-$T.png" fig end - # datum propagation - tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] - points = Point.(Cartesian{WGS84Latest}.(tuples)) + # CRS propagation + points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) ref = refine(mesh, CatmullClark()) - @test datum(crs(ref)) === WGS84Latest + @test crs(ref) === crs(mesh) end @testset "TriSubdivision" begin diff --git a/test/runtests.jl b/test/runtests.jl index 9bd276c83..d8cb30efc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,7 +33,7 @@ include("testutils.jl") cart(args...) = cart(T, args...) -latlon(args...) = latlon(T, args...) +merc(args...) = merc(T, args...) vector(args...) = vector(T, args...) diff --git a/test/sets.jl b/test/sets.jl index 2fd54dbf8..6c85d4c67 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -85,11 +85,9 @@ pset2 = PointSet(p for p in points) @test pset1 == pset2 - # datum propagation - tuples = [T.((0, 0)), T.((1, 0)), T.((0, 1))] - points = Point.(Cartesian{WGS84Latest}.(tuples)) - pset = PointSet(points) - @test datum(crs(centroid(pset))) === WGS84Latest + # CRS propagation + pset = PointSet(merc.([(0, 0), (1, 0), (0, 1)])) + @test crs(centroid(pset)) === crs(pset) pset = PointSet(cart.([(1, 0), (0, 1)])) @test sprint(show, pset) == "2 PointSet" diff --git a/test/sideof.jl b/test/sideof.jl index a1bd2eda0..50a5b5268 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -15,8 +15,8 @@ pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, ON] - p1, p2, p3 = latlon(0.5, 0.5), latlon(1.5, 0.5), latlon(1, 1) - c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) + p1, p2, p3 = merc(0.5, 0.5), merc(1.5, 0.5), merc(1, 1) + c = Ring([merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)]) @test sideof(p1, c) == IN @test sideof(p2, c) == OUT @test sideof(p3, c) == ON diff --git a/test/testutils.jl b/test/testutils.jl index d6627b8a6..0237ec585 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -44,8 +44,8 @@ end cart(T::Type, coords...) = cart(T, coords) cart(T::Type, coords::Tuple) = Point(T.(coords)) -latlon(T::Type, coords...) = latlon(T, coords) -latlon(T::Type, coords::Tuple) = Point(LatLon(T.(coords)...)) +merc(T::Type, coords...) = merc(T, coords) +merc(T::Type, coords::Tuple) = Point(Mercator(T.(coords)...)) vector(T::Type, coords...) = vector(T, coords) vector(T::Type, coords::Tuple) = Vec(T.(coords)) @@ -64,7 +64,7 @@ numconvert(T, x::Quantity{S,D,U}) where {S,D,U} = convert(Quantity{T,D,U}, x) withprecision(_, x) = x withprecision(T, v::Vec) = numconvert.(T, v) -withprecision(T, p::Point) = Meshes.withdatum(p, withprecision(T, to(p))) +withprecision(T, p::Point) = Meshes.withcrs(p, withprecision(T, to(p))) withprecision(T, len::Meshes.Len) = numconvert(T, len) withprecision(T, lens::NTuple{Dim,Meshes.Len}) where {Dim} = numconvert.(T, lens) withprecision(T, geoms::NTuple{Dim,<:Geometry}) where {Dim} = withprecision.(T, geoms) diff --git a/test/transforms.jl b/test/transforms.jl index 70f5381d1..2de2724c6 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -222,11 +222,10 @@ @test r ≈ vector(0, 1) @test TB.revert(f, r, c) ≈ v - # datum propagation + # CRS propagation f = Rotate(Angle2d(T(π / 2))) - c = Cartesian{WGS84Latest}(T(1), T(0)) - p = Point(c) - @test datum(crs(f(p))) === WGS84Latest + p = merc(1, 0) + @test crs(f(p)) === crs(p) end @testset "Translate" begin @@ -602,11 +601,10 @@ @test f.A isa SMatrix @test f.b isa SVector - # datum propagation + # CRS propagation f = Affine(Angle2d(T(π / 2)), T[1, 1]) - c = Cartesian{WGS84Latest}(T(1), T(0)) - p = Point(c) - @test datum(crs(f(p))) === WGS84Latest + p = merc(1, 0) + @test crs(f(p)) === crs(p) # error: A must be a square matrix @test_throws ArgumentError Affine(T[1 1; 2 2; 3 3], T[1, 2]) diff --git a/test/winding.jl b/test/winding.jl index 5ba4f2dfb..467e65e5b 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -13,16 +13,16 @@ # record allocations for cartesian alloccart = @allocated winding(p, c) - p = latlon(0.5, 0.5) - c = Ring([latlon(0, 0), latlon(1, 0), latlon(1, 1), latlon(0, 1)]) + p = merc(0.5, 0.5) + c = Ring([merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)]) @test winding(p, c) ≈ T(1) @test winding(p, reverse(c)) ≈ T(-1) @test winding([p, p], c) ≈ T[1, 1] - # record allocations for latlon - alloclatlon = @allocated winding(p, c) + # record allocations for merc + allocmerc = @allocated winding(p, c) # exact same memory allocations - @test alloccart == alloclatlon + @test alloccart == allocmerc m = boundary(Box(cart(0, 0, 0), cart(2, 2, 2))) @test all(>(0), winding(vertices(m), m)) From 8a3f78ea81f94a52c1c2fd04eabd9840410c70fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 16 Jul 2024 08:53:05 -0300 Subject: [PATCH 175/423] Refactor orientation.jl --- src/orientation.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/orientation.jl b/src/orientation.jl index 2b233b47e..ba4aa6dd1 100644 --- a/src/orientation.jl +++ b/src/orientation.jl @@ -60,10 +60,10 @@ struct WindingOrientation <: OrientationMethod end function orientation(r::Ring{2}, ::WindingOrientation) # pick any segment - x₁, x₂ = r.vertices[1:2] - x̄ = center(Segment(x₁, x₂)) - w̄ = winding(x̄, r) - w = oftype(w̄, 2π) * w̄ - ∠(x₁, x̄, x₂) + p₁, p₂ = vertices(r)[1:2] + p̄ = center(Segment(p₁, p₂)) + w̄ = winding(p̄, r) + w = oftype(w̄, 2π) * w̄ - ∠(p₁, p̄, p₂) isapproxequal(w, oftype(w, π)) ? CCW : CW end From e8e4581750fc346e446ed27e7144e99e0c93e218 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:48:49 -0300 Subject: [PATCH 176/423] Add CRS testset (#939) * [WIP] Add CRS testset * Add the latest tests --- test/crs.jl | 141 ++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 3 + test/testutils.jl | 3 + 3 files changed, 147 insertions(+) create mode 100644 test/crs.jl diff --git a/test/crs.jl b/test/crs.jl new file mode 100644 index 000000000..58f72b46c --- /dev/null +++ b/test/crs.jl @@ -0,0 +1,141 @@ +@testset "CRS" begin + @testset "Projected" begin + g = merc(1, 1) + @test crs(g) <: Mercator{WGS84Latest} + g = Ray(merc(0, 0), vector(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Line(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = BezierCurve(merc(0, 0), merc(1, 1), merc(2, 0)) + @test crs(g) <: Mercator{WGS84Latest} + g = Box(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Ball(merc(0, 0), T(1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Sphere(merc(0, 0), T(1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Segment(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Rope(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Ring(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = PolyArea(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Multi([merc(0, 0), merc(1, 1)]) + @test crs(g) <: Mercator{WGS84Latest} + t1 = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + t2 = Triangle(merc(1, 1), merc(2, 1), merc(1, 2)) + d = GeometrySet([t1, t2]) + @test crs(d) <: Mercator{WGS84Latest} + d = PointSet([merc(0, 0), merc(1, 1)]) + @test crs(d) <: Mercator{WGS84Latest} + d = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) + @test crs(d) <: Mercator{WGS84Latest} + p = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + @test crs(d) <: Mercator{WGS84Latest} + end + + @testset "Geographic" begin + g = latlon(1, 1) + @test crs(g) <: LatLon{WGS84Latest} + g = Ray(latlon(0, 0), vector(1, 1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Line(latlon(0, 0), latlon(1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = BezierCurve(latlon(0, 0), latlon(1, 1), latlon(0, 2)) + @test crs(g) <: LatLon{WGS84Latest} + g = Box(latlon(0, 180), latlon(45, 90)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ball(latlon(0, 0), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Sphere(latlon(0, 0), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ellipsoid((T(3), T(2), T(1)), latlon(0, 0)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Disk(p, T(2)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Circle(p, T(2)) + @test crs(g) <: LatLon{WGS84Latest} + b = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + t = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Cylinder(b, t, T(5)) + @test crs(g) <: LatLon{WGS84Latest} + b = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + t = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = CylinderSurface(b, t, T(5)) + @test crs(g) <: LatLon{WGS84Latest} + g = ParaboloidSurface(latlon(0, 0), T(1), T(2)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + d = Disk(p, T(2)) + a = latlon(90, 0) + g = Cone(d, a) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + d = Disk(p, T(2)) + a = latlon(90, 0) + g = ConeSurface(d, a) + @test crs(g) <: LatLon{WGS84Latest} + pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + db = Disk(pb, T(1)) + pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + dt = Disk(pt, T(2)) + g = Frustum(db, dt) + @test crs(g) <: LatLon{WGS84Latest} + pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + db = Disk(pb, T(1)) + pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + dt = Disk(pt, T(2)) + g = FrustumSurface(db, dt) + @test crs(g) <: LatLon{WGS84Latest} + g = Torus(latlon(0, 0), vector(1, 0, 0), T(2), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Segment(latlon(0, 0), latlon(1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Rope(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ring(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Tetrahedron(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(90, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Hexahedron(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(1, 45), latlon(1, 135), latlon(1, -135), latlon(1, -45)) + @test crs(g) <: LatLon{WGS84Latest} + g = Pyramid(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(90, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Wedge(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(1, 0), latlon(1, 90), latlon(1, -90)) + @test crs(g) <: LatLon{WGS84Latest} + g = Multi([latlon(0, 0), latlon(1, 1)]) + @test crs(g) <: LatLon{WGS84Latest} + t1 = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + t2 = Triangle(latlon(1, 1), latlon(1, 2), latlon(2, 1)) + d = GeometrySet([t1, t2]) + @test crs(d) <: LatLon{WGS84Latest} + d = PointSet([latlon(0, 0), latlon(1, 1)]) + @test crs(d) <: LatLon{WGS84Latest} + d = CartesianGrid((10, 10, 10), latlon(0, 0), (T(1), T(1), T(1))) + @test crs(d) <: LatLon{WGS84Latest} + p = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + @test crs(d) <: LatLon{WGS84Latest} + d = CylindricalTrajectory([latlon(0, 0), latlon(1, 1), latlon(0, 2)]) + @test crs(d) <: LatLon{WGS84Latest} + end +end diff --git a/test/runtests.jl b/test/runtests.jl index d8cb30efc..25aaed02d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -35,6 +35,8 @@ cart(args...) = cart(T, args...) merc(args...) = merc(T, args...) +latlon(args...) = latlon(T, args...) + vector(args...) = vector(T, args...) cartgrid(args...) = cartgrid(T, args...) @@ -57,6 +59,7 @@ testfiles = [ "sets.jl", "mesh.jl", "trajecs.jl", + "crs.jl", "utils.jl", "viewing.jl", "partitioning.jl", diff --git a/test/testutils.jl b/test/testutils.jl index 0237ec585..53b15e097 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -47,6 +47,9 @@ cart(T::Type, coords::Tuple) = Point(T.(coords)) merc(T::Type, coords...) = merc(T, coords) merc(T::Type, coords::Tuple) = Point(Mercator(T.(coords)...)) +latlon(T::Type, coords...) = latlon(T, coords) +latlon(T::Type, coords::Tuple) = Point(LatLon(T.(coords)...)) + vector(T::Type, coords...) = vector(T, coords) vector(T::Type, coords::Tuple) = Vec(T.(coords)) From f1bf6c0d044e93e66917e8f583062aba69ddbe52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:57:35 -0300 Subject: [PATCH 177/423] :robot: Format .jl files (#940) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- test/crs.jl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/crs.jl b/test/crs.jl index 58f72b46c..c3036c4a9 100644 --- a/test/crs.jl +++ b/test/crs.jl @@ -41,7 +41,7 @@ d = SimpleMesh(p, c) @test crs(d) <: Mercator{WGS84Latest} end - + @testset "Geographic" begin g = latlon(1, 1) @test crs(g) <: LatLon{WGS84Latest} @@ -115,7 +115,16 @@ @test crs(g) <: LatLon{WGS84Latest} g = Tetrahedron(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(90, 0)) @test crs(g) <: LatLon{WGS84Latest} - g = Hexahedron(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(1, 45), latlon(1, 135), latlon(1, -135), latlon(1, -45)) + g = Hexahedron( + latlon(0, 45), + latlon(0, 135), + latlon(0, -135), + latlon(0, -45), + latlon(1, 45), + latlon(1, 135), + latlon(1, -135), + latlon(1, -45) + ) @test crs(g) <: LatLon{WGS84Latest} g = Pyramid(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(90, 0)) @test crs(g) <: LatLon{WGS84Latest} From 3952df4c9863ce7d75adb672aa26b9bb68815ba2 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:44:04 -0300 Subject: [PATCH 178/423] Refactor the implementation of the `rand` function (#941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor the implementation of the 'rand' function * Add docstring * Add tests * Apply suggestions * Fix tests * Update documentation * Fix tests * Add documentation * Apply suggestions * Update docs/src/rand.md * Add more tests --------- Co-authored-by: Júlio Hoffimann --- Project.toml | 2 +- docs/Project.toml | 1 + docs/make.jl | 1 + docs/src/algorithms/boundingbox.md | 3 +- docs/src/algorithms/clamping.md | 3 +- docs/src/algorithms/hulls.md | 3 +- docs/src/algorithms/tesselation.md | 5 +- docs/src/domains/sets.md | 5 +- docs/src/geometries/primitives.md | 2 +- docs/src/rand.md | 42 +++++ docs/src/visualization.md | 3 +- src/Meshes.jl | 1 + src/geometries.jl | 8 - src/polytopes/hexahedron.jl | 5 - src/polytopes/ngon.jl | 4 - src/polytopes/polyarea.jl | 4 - src/polytopes/pyramid.jl | 4 - src/polytopes/ring.jl | 10 -- src/polytopes/rope.jl | 4 - src/polytopes/segment.jl | 4 - src/polytopes/tetrahedron.jl | 5 - src/polytopes/wedge.jl | 4 - src/primitives/ball.jl | 5 - src/primitives/bezier.jl | 4 - src/primitives/box.jl | 8 - src/primitives/circle.jl | 2 - src/primitives/cone.jl | 2 - src/primitives/conesurface.jl | 2 - src/primitives/cylinder.jl | 3 - src/primitives/cylindersurface.jl | 3 - src/primitives/disk.jl | 2 - src/primitives/ellipsoid.jl | 6 - src/primitives/frustum.jl | 8 - src/primitives/frustumsurface.jl | 8 - src/primitives/line.jl | 4 - src/primitives/paraboloidsurface.jl | 3 - src/primitives/plane.jl | 2 - src/primitives/point.jl | 4 - src/primitives/ray.jl | 5 - src/primitives/sphere.jl | 5 - src/primitives/torus.jl | 3 - src/rand.jl | 138 +++++++++++++++ test/polytopes.jl | 101 ----------- test/primitives.jl | 128 +------------- test/rand.jl | 260 ++++++++++++++++++++++++++++ test/runtests.jl | 1 + 46 files changed, 463 insertions(+), 367 deletions(-) create mode 100644 docs/src/rand.md create mode 100644 src/rand.jl create mode 100644 test/rand.jl diff --git a/Project.toml b/Project.toml index 99d46bcfd..16d5ea8a0 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.9" +CoordRefSystems = "0.9.10" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/docs/Project.toml b/docs/Project.toml index 4e040dcac..626819324 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,5 +4,6 @@ CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" PlyIO = "42171d58-473b-503a-8d5f-782019eb09ec" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" diff --git a/docs/make.jl b/docs/make.jl index 79fc8f234..732b4f265 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -33,6 +33,7 @@ makedocs( "algorithms/matrices.md" ], "Transforms" => "transforms.md", + "Random" => "rand.md", "Visualization" => "visualization.md", "Input/Output" => "io.md" ], diff --git a/docs/src/algorithms/boundingbox.md b/docs/src/algorithms/boundingbox.md index 614ee86b5..08c960d79 100644 --- a/docs/src/algorithms/boundingbox.md +++ b/docs/src/algorithms/boundingbox.md @@ -2,6 +2,7 @@ ```@example boundingbox using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -10,7 +11,7 @@ boundingbox ``` ```@example boundingbox -pset = PointSet(rand(Point{2}, 100)) +pset = PointSet(rand(Point, 100, crs=Cartesian2D)) bbox = boundingbox(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/algorithms/clamping.md b/docs/src/algorithms/clamping.md index ff23dc0c5..149751b1c 100644 --- a/docs/src/algorithms/clamping.md +++ b/docs/src/algorithms/clamping.md @@ -9,10 +9,11 @@ clamp(::PointSet, ::Box) ```@example clamping using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide # set of 2D points to clamp -points = PointSet(rand(Point{2}, 100)) +points = PointSet(rand(Point, 100, crs=Cartesian2D)) # 2D box defining the clamping boundaries box = Box((0.25, 0.25), (0.75, 0.75)) diff --git a/docs/src/algorithms/hulls.md b/docs/src/algorithms/hulls.md index 2e2fddb4d..f56df606a 100644 --- a/docs/src/algorithms/hulls.md +++ b/docs/src/algorithms/hulls.md @@ -2,6 +2,7 @@ ```@example hull using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -14,7 +15,7 @@ JarvisMarch ``` ```@example hull -pset = PointSet(rand(Point{2}, 100)) +pset = PointSet(rand(Point, 100, crs=Cartesian2D)) chul = convexhull(pset) fig = Mke.Figure(size = (800, 400)) diff --git a/docs/src/algorithms/tesselation.md b/docs/src/algorithms/tesselation.md index 29d448433..383cb8598 100644 --- a/docs/src/algorithms/tesselation.md +++ b/docs/src/algorithms/tesselation.md @@ -2,6 +2,7 @@ ```@example tesselation using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -17,7 +18,7 @@ DelaunayTesselation ``` ```@example tesselation -points = rand(Point{2}, 100) +points = rand(Point, 100, crs=Cartesian2D) mesh = tesselate(points, DelaunayTesselation()) @@ -33,7 +34,7 @@ VoronoiTesselation ``` ```@example tesselation -points = rand(Point{2}, 100) +points = rand(Point, 100, crs=Cartesian2D) mesh = tesselate(points, VoronoiTesselation()) diff --git a/docs/src/domains/sets.md b/docs/src/domains/sets.md index 11caa2ee5..27e83fd68 100644 --- a/docs/src/domains/sets.md +++ b/docs/src/domains/sets.md @@ -2,6 +2,7 @@ ```@example sets using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -13,7 +14,7 @@ GeometrySet ``` ```@example sets -GeometrySet(rand(Ball{3}, 3)) |> viz +GeometrySet(rand(Ball, 3)) |> viz ``` ```@docs @@ -21,5 +22,5 @@ PointSet ``` ```@example sets -PointSet(rand(Point{2}, 100)) |> viz +PointSet(rand(Point, 100, crs=Cartesian2D)) |> viz ``` \ No newline at end of file diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 1eef9f4c6..1aac5ea9e 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -20,7 +20,7 @@ Point ``` ```@example primitives -rand(Point{3}, 100) |> viz +rand(Point, 100) |> viz ``` ```@docs diff --git a/docs/src/rand.md b/docs/src/rand.md new file mode 100644 index 000000000..8f524200e --- /dev/null +++ b/docs/src/rand.md @@ -0,0 +1,42 @@ +# Random + +```@example rand +using Meshes # hide +using Random # hide +using CoordRefSystems # hide +``` + +```@docs +rand(::Type{<:Geometry}) +rand(::Type{<:Geometry}, ::Int) +``` + +Random geometries can be generated using the `rand` function: + +```@example rand +rand(Point) +``` + +By default, the `rand` function uses the `Cartesian3D` CRS (Coordinate Reference System). +It's possible to change the CRS using the `crs` keyword argument: + +```@example rand +rand(Point, crs=Cartesian2D) +``` + +A vector of geometries can be generated by passing the number of elements as the second argument: + +```@example rand +rand(Segment, 5, crs=LatLon) +``` + +For reproducibility purposes, a random number generator can be passed as the first argument in both methods: + +```@example rand +rng = MersenneTwister(123) +rand(rng, Triangle) +``` + +```@example rand +rand(rng, Triangle, 5) +``` diff --git a/docs/src/visualization.md b/docs/src/visualization.md index c3ddb8829..eee154e39 100644 --- a/docs/src/visualization.md +++ b/docs/src/visualization.md @@ -2,6 +2,7 @@ ```@example viz using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -19,7 +20,7 @@ viz! We can visualize a single geometry or multiple geometries in a vector: ```@example viz -triangles = rand(Triangle{2}, 10) +triangles = rand(Triangle, 10, crs=Cartesian2D) viz(triangles, color = 1:10) ``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 4c7e8d43b..30ca0878a 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -118,6 +118,7 @@ include("coarsening.jl") include("transforms.jl") # miscellaneous +include("rand.jl") include("distances.jl") include("supportfun.jl") include("matrices.jl") diff --git a/src/geometries.jl b/src/geometries.jl index 4c9cad244..64b1adf5c 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -60,14 +60,6 @@ bounding box of the `geometry`. """ Base.extrema(g::Geometry) = extrema(boundingbox(g)) -# ------- -# RANDOM -# ------- - -Random.rand(::Type{G}) where {G<:Geometry} = rand(Random.default_rng(), G) -Random.rand(::Type{G}, n::Int) where {G<:Geometry} = rand(Random.default_rng(), G, n) -Random.rand(rng::Random.AbstractRNG, ::Type{G}, n::Int) where {G<:Geometry} = [rand(rng, G) for _ in 1:n] - # ----------- # IO METHODS # ----------- diff --git a/src/polytopes/hexahedron.jl b/src/polytopes/hexahedron.jl index a56950673..25a82ae60 100644 --- a/src/polytopes/hexahedron.jl +++ b/src/polytopes/hexahedron.jl @@ -33,8 +33,3 @@ function (h::Hexahedron)(u, v, w) u * v * w * A8 ) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Hexahedron{Dim}}) where {Dim} = - Hexahedron(ntuple(i -> rand(rng, Point{Dim}), 8)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Hexahedron}) = rand(rng, Hexahedron{3}) diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 8e6e00306..61eaf770b 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -65,10 +65,6 @@ innerangles(ngon::Ngon) = innerangles(boundary(ngon)) signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) -Random.rand(rng::Random.AbstractRNG, ::Type{Ngon{N,Dim}}) where {N,Dim} = Ngon{N}(ntuple(i -> rand(rng, Point{Dim}), N)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Ngon{N}}) where {N} = rand(rng, Ngon{N,3}) - # ---------- # TRIANGLES # ---------- diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 750e1d692..e23b04ad6 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -113,7 +113,3 @@ function Base.show(io::IO, ::MIME"text/plain", p::PolyArea) printelms(io, @view(rings[2:end]), " ") end end - -Random.rand(rng::Random.AbstractRNG, ::Type{PolyArea{Dim}}) where {Dim} = PolyArea(rand(rng, Ring{Dim})) - -Random.rand(rng::Random.AbstractRNG, ::Type{PolyArea}) = rand(rng, PolyArea{3}) diff --git a/src/polytopes/pyramid.jl b/src/polytopes/pyramid.jl index 561861a09..ad7f9c79c 100644 --- a/src/polytopes/pyramid.jl +++ b/src/polytopes/pyramid.jl @@ -15,7 +15,3 @@ nvertices(::Type{<:Pyramid}) = 5 Base.isapprox(p₁::Pyramid, p₂::Pyramid; atol=atol(lentype(p₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(p₁.vertices, p₂.vertices)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid{Dim}}) where {Dim} = Pyramid(ntuple(i -> rand(rng, Point{Dim}), 5)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Pyramid}) = rand(rng, Pyramid{3}) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 0000e7b97..5bba6f611 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -58,16 +58,6 @@ Base.open(r::Ring) = open(Rope(parent(r.vertices))) # do not change which vertex comes first for closed chains Base.reverse!(r::Ring) = (reverse!(@view r.vertices[(begin + 1):end]); r) -function Random.rand(rng::Random.AbstractRNG, ::Type{Ring{Dim}}) where {Dim} - v = rand(rng, Point{Dim}, rand(rng, 3:50)) - while first(v) == last(v) - v = rand(rng, Point{Dim}, rand(rng, 3:50)) - end - Ring(v) -end - -Random.rand(rng::Random.AbstractRNG, ::Type{Ring}) = rand(rng, Ring{3}) - """ innerangles(ring) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index bf8d73440..73392a4ca 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -29,7 +29,3 @@ Base.close(r::Rope) = Ring(r.vertices) Base.open(r::Rope) = r Base.reverse!(r::Rope) = (reverse!(r.vertices); r) - -Random.rand(rng::Random.AbstractRNG, ::Type{Rope{Dim}}) where {Dim} = Rope(rand(rng, Point{Dim}, rand(rng, 2:50))) - -Random.rand(rng::Random.AbstractRNG, ::Type{Rope}) = rand(rng, Rope{3}) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index c44e81a57..aed6a3ba3 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -38,7 +38,3 @@ function (s::Segment)(t) a, b = s.vertices a + t * (b - a) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Segment{Dim}}) where {Dim} = Segment(ntuple(i -> rand(rng, Point{Dim}), 2)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Segment}) = rand(rng, Segment{3}) diff --git a/src/polytopes/tetrahedron.jl b/src/polytopes/tetrahedron.jl index 097b1a0b5..b6aa7cec7 100644 --- a/src/polytopes/tetrahedron.jl +++ b/src/polytopes/tetrahedron.jl @@ -24,8 +24,3 @@ function (t::Tetrahedron)(u, v, w) v₁, v₂, v₃, v₄ = to.(t.vertices) withcrs(t, v₁ * z + v₂ * u + v₃ * v + v₄ * w) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron{Dim}}) where {Dim} = - Tetrahedron(ntuple(i -> rand(rng, Point{Dim}), 4)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Tetrahedron}) = rand(rng, Tetrahedron{3}) diff --git a/src/polytopes/wedge.jl b/src/polytopes/wedge.jl index 71d25374b..1c8766dc5 100644 --- a/src/polytopes/wedge.jl +++ b/src/polytopes/wedge.jl @@ -15,7 +15,3 @@ nvertices(::Type{<:Wedge}) = 6 Base.isapprox(t₁::Wedge, t₂::Wedge; atol=atol(lentype(t₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(t₁.vertices, t₂.vertices)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Wedge{Dim}}) where {Dim} = Wedge(ntuple(i -> rand(rng, Point{Dim}), 6)) - -Random.rand(rng::Random.AbstractRNG, ::Type{Wedge}) = rand(rng, Wedge{3}) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index 276fdb056..b4dff1a0a 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -63,8 +63,3 @@ function (b::Ball{3})(ρ, θ, φ) z = l * cθ c + Vec(x, y, z) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Ball{Dim}}) where {Dim} = - Ball(rand(rng, Point{Dim}), rand(rng, Met{Float64})) - -Random.rand(rng::Random.AbstractRNG, ::Type{Ball}) = rand(rng, Ball{3}) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 7b047f8e6..6bf847747 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -112,10 +112,6 @@ function (curve::BezierCurve)(t, ::Horner) withcrs(curve, b₀) end -Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve{Dim}}) where {Dim} = BezierCurve(rand(rng, Point{Dim}, 5)) - -Random.rand(rng::Random.AbstractRNG, ::Type{BezierCurve}) = rand(rng, BezierCurve{3}) - # ----------- # IO METHODS # ----------- diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 8f8067abd..8758aab1b 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -54,11 +54,3 @@ function (b::Box)(uv...) end b.min + uv .* (b.max - b.min) end - -function Random.rand(rng::Random.AbstractRNG, ::Type{Box{Dim}}) where {Dim} - min = rand(rng, Point{Dim}) - max = min + rand(rng, Vec{Dim,Met{Float64}}) - Box(min, max) -end - -Random.rand(rng::Random.AbstractRNG, ::Type{Box}) = rand(rng, Box{3}) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 033c74b83..331980cde 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -64,5 +64,3 @@ function (c::Circle)(φ) v = ustrip(l * sφ) c.plane(u, v) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Circle}) = Circle(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 60c565ae1..7d525cf40 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -34,5 +34,3 @@ halfangle(c::Cone) = atan(radius(base(c)), height(c)) Base.isapprox(c₁::Cone, c₂::Cone; atol=atol(lentype(c₁)), kwargs...) = isapprox(boundary(c₁), boundary(c₂); atol, kwargs...) - -Random.rand(rng::Random.AbstractRNG, ::Type{Cone}) = Cone(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index f59e14b6c..09a25a85f 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -45,5 +45,3 @@ function (c::ConeSurface)(φ, h) s = Circle(Plane(o, n), r) s(T(φ)) end - -Random.rand(rng::Random.AbstractRNG, ::Type{ConeSurface}) = ConeSurface(rand(rng, Disk), rand(rng, Point{3})) diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index ce3225dba..97b4b83f4 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -98,6 +98,3 @@ function (c::Cylinder)(ρ, φ, z) s = Segment(l ∩ b, l ∩ t) s(T(z)) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Cylinder}) = - Cylinder(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 9198b1a5c..0aded0337 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -123,9 +123,6 @@ function (c::CylinderSurface)(φ, z) o + Q' * to(p) end -Random.rand(rng::Random.AbstractRNG, ::Type{CylinderSurface}) = - CylinderSurface(rand(rng, Plane), rand(rng, Plane), rand(rng, Met{Float64})) - function hasintersectingplanes(c::CylinderSurface) x = c.bot ∩ c.top !isnothing(x) && evaluate(Euclidean(), axis(c), x) < c.radius diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index 61e2c9b77..8aa29c86c 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -45,5 +45,3 @@ function (d::Disk)(ρ, φ) v = ustrip(l * sφ) d.plane(u, v) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Disk}) = Disk(rand(rng, Plane), rand(rng, Met{Float64})) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index ea0d16047..1b7160496 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -56,9 +56,3 @@ function (e::Ellipsoid)(θ, φ) z = r[3] * cθ c + R * Vec(x, y, z) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Ellipsoid}) = Ellipsoid( - (rand(rng, Met{Float64}), rand(rng, Met{Float64}), rand(rng, Met{Float64})), - rand(rng, Point{3}), - rand(rng, QuatRotation) -) diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index d438d6035..df88c49d3 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -40,11 +40,3 @@ axis(f::Frustum) = axis(boundary(f)) Base.isapprox(f₁::Frustum, f₂::Frustum; atol=atol(lentype(f₁)), kwargs...) = isapprox(boundary(f₁), boundary(f₂); atol, kwargs...) - -function Random.rand(rng::Random.AbstractRNG, ::Type{Frustum}) - bottom = rand(rng, Disk) - ax = normal(plane(bottom)) - topplane = Plane(center(bottom) + rand() * ax, ax) - top = Disk(topplane, rand(Met{Float64})) - Frustum(bottom, top) -end diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 4015cb06d..ae59093a6 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -68,11 +68,3 @@ function (f::FrustumSurface)(φ, z) center(bottom(f)) + Q' * p end - -function Random.rand(rng::Random.AbstractRNG, ::Type{FrustumSurface}) - bottom = rand(rng, Disk) - ax = normal(plane(bottom)) - topplane = Plane(center(bottom) + rand() * ax, ax) - top = Disk(topplane, rand(Met{Float64})) - FrustumSurface(bottom, top) -end diff --git a/src/primitives/line.jl b/src/primitives/line.jl index a7ee093fb..0b3aad266 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -24,7 +24,3 @@ Base.isapprox(l₁::Line, l₂::Line; atol=atol(lentype(l₁)), kwargs...) = isapprox(l₁.a, l₂.a; atol, kwargs...) && isapprox(l₁.b, l₂.b; atol, kwargs...) (l::Line)(t) = l.a + t * (l.b - l.a) - -Random.rand(rng::Random.AbstractRNG, ::Type{Line{Dim}}) where {Dim} = Line(rand(rng, Point{Dim}), rand(rng, Point{Dim})) - -Random.rand(rng::Random.AbstractRNG, ::Type{Line}) = rand(rng, Line{3}) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index e58c6f05b..142be918b 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -121,6 +121,3 @@ function (p::ParaboloidSurface)(ρ, θ) z = (x^2 + y^2) / 4f c + Vec(x, y, z) end - -Random.rand(rng::Random.AbstractRNG, ::Type{ParaboloidSurface}) = - ParaboloidSurface(rand(rng, Point{3}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 675e8ba87..5adef2f20 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -49,5 +49,3 @@ Base.isapprox(p₁::Plane, p₂::Plane; atol=atol(lentype(p₁)), kwargs...) = isapproxzero(norm(ucross(normal(p₁), normal(p₂))); atol, kwargs...) (p::Plane)(u, v) = p.p + u * p.u + v * p.v - -Random.rand(rng::Random.AbstractRNG, ::Type{Plane}) = Plane(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}})) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 02f83644b..92c005983 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -126,10 +126,6 @@ See . """ ∠(A::P, B::P, C::P) where {P<:Point} = ∠(A - B, C - B) -Random.rand(rng::Random.AbstractRNG, ::Type{Point{Dim}}) where {Dim} = Point(rand(rng, Cartesian{NoDatum,Dim})) - -Random.rand(rng::Random.AbstractRNG, ::Type{Point}) = rand(rng, Point{3}) - # ----------- # IO METHODS # ----------- diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 05d3c17e3..db2697f74 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -29,8 +29,3 @@ function (r::Ray)(t) end r.p + t * r.v end - -Random.rand(rng::Random.AbstractRNG, ::Type{Ray{Dim}}) where {Dim} = - Ray(rand(rng, Point{Dim}), rand(rng, Vec{Dim,Met{Float64}})) - -Random.rand(rng::Random.AbstractRNG, ::Type{Ray}) = rand(rng, Ray{3}) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index e2b92eebc..3c0cebe41 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -98,8 +98,3 @@ function (s::Sphere{3})(θ, φ) z = r * cθ c + Vec(x, y, z) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Sphere{Dim}}) where {Dim} = - Sphere(rand(rng, Point{Dim}), rand(rng, Met{Float64})) - -Random.rand(rng::Random.AbstractRNG, ::Type{Sphere}) = rand(rng, Sphere{3}) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index 0a9700bdb..d6ee2b334 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -81,6 +81,3 @@ function (t::Torus)(θ, φ) c + Q * Vec(x, y, z) end - -Random.rand(rng::Random.AbstractRNG, ::Type{Torus}) = - Torus(rand(rng, Point{3}), rand(rng, Vec{3,Met{Float64}}), rand(rng, Met{Float64}), rand(rng, Met{Float64})) diff --git a/src/rand.jl b/src/rand.jl new file mode 100644 index 000000000..734d14812 --- /dev/null +++ b/src/rand.jl @@ -0,0 +1,138 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + rand([rng], G, crs=Cartesian3D) + +Generate a random geometry of type `G` with CRS `crs`, +optionally passing a random number generator `rng`. + +# Examples + +```julia +rand(Point) +rand(Triangle) +rand(Point, crs=Cartesian2D) +rand(Triangle, crs=LatLon) +``` +""" +Random.rand(G::Type{<:Geometry}; kwargs...) = rand(Random.default_rng(), G; kwargs...) +Random.rand(rng::Random.AbstractRNG, G::Type{<:Geometry}; crs=Cartesian3D) = _rand(rng, G, crs) + +""" + rand([rng], G, n, crs=Cartesian3D) + +Generate a vector of `n` random geometries of type `G` with CRS `crs`, +optionally passing a random number generator `rng`. + +# Examples + +```julia +rand(Point, 10) +rand(Triangle, 10) +rand(Point, 10, crs=Cartesian2D) +rand(Triangle, 10, crs=LatLon) +``` +""" +Random.rand(G::Type{<:Geometry}, n::Int; kwargs...) = rand(Random.default_rng(), G, n; kwargs...) +Random.rand(rng::Random.AbstractRNG, G::Type{<:Geometry}, n::Int; kwargs...) = [rand(rng, G; kwargs...) for _ in 1:n] + +# ---------------- +# IMPLEMENTATIONS +# ---------------- + +_rand(rng::Random.AbstractRNG, ::Type{Point}, CRS) = Point(rand(rng, CRS)) + +_rand(rng::Random.AbstractRNG, ::Type{Ray}, CRS) = Ray(_rand(rng, Point, CRS), _rvec(rng, CRS)) + +_rand(rng::Random.AbstractRNG, ::Type{Line}, CRS) = Line(_rand(rng, Point, CRS), _rand(rng, Point, CRS)) + +_rand(rng::Random.AbstractRNG, ::Type{BezierCurve}, CRS) = BezierCurve(_rvector(rng, CRS, 5)) + +_rand(rng::Random.AbstractRNG, ::Type{Plane}, CRS) = Plane(_rand(rng, Point, CRS), _rvec(rng, CRS)) + +function _rand(rng::Random.AbstractRNG, ::Type{Box}, CRS) + min = _rand(rng, Point, CRS) + max = min + _rvec(rng, CRS) + Box(min, max) +end + +_rand(rng::Random.AbstractRNG, ::Type{Ball}, CRS) = Ball(_rand(rng, Point, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Sphere}, CRS) = Sphere(_rand(rng, Point, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Ellipsoid}, CRS) = + Ellipsoid((_rlen(rng), _rlen(rng), _rlen(rng)), _rand(rng, Point, CRS), rand(rng, QuatRotation)) + +_rand(rng::Random.AbstractRNG, ::Type{Disk}, CRS) = Disk(_rand(rng, Plane, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Circle}, CRS) = Circle(_rand(rng, Plane, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Cylinder}, CRS) = + Cylinder(_rand(rng, Plane, CRS), _rand(rng, Plane, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{CylinderSurface}, CRS) = + CylinderSurface(_rand(rng, Plane, CRS), _rand(rng, Plane, CRS), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Cone}, CRS) = Cone(_rand(rng, Disk, CRS), _rand(rng, Point, CRS)) + +_rand(rng::Random.AbstractRNG, ::Type{ConeSurface}, CRS) = ConeSurface(_rand(rng, Disk, CRS), _rand(rng, Point, CRS)) + +function _rand(rng::Random.AbstractRNG, ::Type{Frustum}, CRS) + bottom = _rand(rng, Disk, CRS) + ax = normal(plane(bottom)) + topplane = Plane(center(bottom) + rand(rng) * ax, ax) + top = Disk(topplane, _rlen(rng)) + Frustum(bottom, top) +end + +function _rand(rng::Random.AbstractRNG, ::Type{FrustumSurface}, CRS) + bottom = _rand(rng, Disk, CRS) + ax = normal(plane(bottom)) + topplane = Plane(center(bottom) + rand(rng) * ax, ax) + top = Disk(topplane, _rlen(rng)) + FrustumSurface(bottom, top) +end + +_rand(rng::Random.AbstractRNG, ::Type{ParaboloidSurface}, CRS) = + ParaboloidSurface(_rand(rng, Point, CRS), _rlen(rng), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Torus}, CRS) = + Torus(_rand(rng, Point, CRS), _rvec(rng, CRS), _rlen(rng), _rlen(rng)) + +_rand(rng::Random.AbstractRNG, ::Type{Segment}, CRS) = Segment(_rtuple(rng, CRS, 2)) + +_rand(rng::Random.AbstractRNG, ::Type{Rope}, CRS) = Rope(_rvector(rng, CRS, rand(rng, 2:50))) + +function _rand(rng::Random.AbstractRNG, ::Type{Ring}, CRS) + v = _rvector(rng, CRS, rand(rng, 3:50)) + while first(v) == last(v) + v = _rvector(rng, CRS, rand(rng, 3:50)) + end + Ring(v) +end + +_rand(rng::Random.AbstractRNG, ::Type{Ngon{N}}, CRS) where {N} = Ngon{N}(_rtuple(rng, CRS, N)) + +_rand(rng::Random.AbstractRNG, ::Type{PolyArea}, CRS) = PolyArea(_rand(rng, Ring, CRS)) + +_rand(rng::Random.AbstractRNG, ::Type{Tetrahedron}, CRS) = Tetrahedron(_rtuple(rng, CRS, 4)) + +_rand(rng::Random.AbstractRNG, ::Type{Hexahedron}, CRS) = Hexahedron(_rtuple(rng, CRS, 8)) + +_rand(rng::Random.AbstractRNG, ::Type{Pyramid}, CRS) = Pyramid(_rtuple(rng, CRS, 5)) + +_rand(rng::Random.AbstractRNG, ::Type{Wedge}, CRS) = Wedge(_rtuple(rng, CRS, 6)) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_rvector(rng::Random.AbstractRNG, CRS, n) = [_rand(rng, Point, CRS) for _ in 1:n] + +_rtuple(rng::Random.AbstractRNG, CRS, n) = ntuple(_ -> _rand(rng, Point, CRS), n) + +_rvec(rng::Random.AbstractRNG, CRS) = rand(rng, Vec{CoordRefSystems.ndims(CRS),Met{Float64}}) + +_rlen(rng::Random.AbstractRNG) = rand(rng, Met{Float64}) diff --git a/test/polytopes.jl b/test/polytopes.jl index 86b3b7a52..a52670751 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -65,19 +65,6 @@ @test center(s) == Point(xm, xm, xm) @test Meshes.lentype(center(s)) == typeof(xm) - s = rand(Segment{2}) - @test s isa Segment - @test embeddim(s) == 2 - @test Meshes.lentype(s) === Meshes.Met{Float64} - s = rand(Segment{3}) - @test s isa Segment - @test embeddim(s) == 3 - @test Meshes.lentype(s) === Meshes.Met{Float64} - s = rand(Segment) - @test s isa Segment - @test embeddim(s) == 3 - @test Meshes.lentype(s) === Meshes.Met{Float64} - # CRS propagation s = Segment(merc(0, 0), merc(1, 1)) @test crs(s(T(0))) === crs(s) @@ -247,32 +234,6 @@ @test nvertices(ur2) == 17 end - r = rand(Rope{2}) - @test r isa Rope - @test embeddim(r) == 2 - @test Meshes.lentype(r) === Meshes.Met{Float64} - r = rand(Rope{3}) - @test r isa Rope - @test embeddim(r) == 3 - @test Meshes.lentype(r) === Meshes.Met{Float64} - r = rand(Rope) - @test r isa Rope - @test embeddim(r) == 3 - @test Meshes.lentype(r) === Meshes.Met{Float64} - - r = rand(Ring{2}) - @test r isa Ring - @test embeddim(r) == 2 - @test Meshes.lentype(r) === Meshes.Met{Float64} - r = rand(Ring{3}) - @test r isa Ring - @test embeddim(r) == 3 - @test Meshes.lentype(r) === Meshes.Met{Float64} - r = rand(Ring) - @test r isa Ring - @test embeddim(r) == 3 - @test Meshes.lentype(r) === Meshes.Met{Float64} - # issimple benchmark r = Sphere(cart(0, 0), T(1)) |> pointify |> Ring @test issimple(r) @@ -333,19 +294,6 @@ for (i, NGON) in enumerate(NGONS) @test paramdim(NGON) == 2 @test nvertices(NGON) == NVERT[i] - - n = rand(NGON{2}) - @test n isa NGON - @test embeddim(n) == 2 - @test Meshes.lentype(n) === Meshes.Met{Float64} - n = rand(NGON{3}) - @test n isa NGON - @test embeddim(n) == 3 - @test Meshes.lentype(n) === Meshes.Met{Float64} - n = rand(NGON) - @test n isa NGON - @test embeddim(n) == 3 - @test Meshes.lentype(n) === Meshes.Met{Float64} end # error: the number of vertices must be greater than or equal to 3 @@ -719,19 +667,6 @@ poly = PolyArea([outer, hole1, hole2]) @test area(poly) ≈ T(0.92) * u"m^2" - p = rand(PolyArea{2}) - @test p isa PolyArea - @test embeddim(p) == 2 - @test Meshes.lentype(p) === Meshes.Met{Float64} - p = rand(PolyArea{3}) - @test p isa PolyArea - @test embeddim(p) == 3 - @test Meshes.lentype(p) === Meshes.Met{Float64} - p = rand(PolyArea) - @test p isa PolyArea - @test embeddim(p) == 3 - @test Meshes.lentype(p) === Meshes.Met{Float64} - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) @@ -787,15 +722,6 @@ equaltest(t) isapproxtest(t) - t = rand(Tetrahedron{3}) - @test t isa Tetrahedron - @test embeddim(t) == 3 - @test Meshes.lentype(t) === Meshes.Met{Float64} - t = rand(Tetrahedron) - @test t isa Tetrahedron - @test embeddim(t) == 3 - @test Meshes.lentype(t) === Meshes.Met{Float64} - # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) @@ -914,15 +840,6 @@ @test nvertices(m) == 8 @test nelements(m) == 6 - h = rand(Hexahedron{3}) - @test h isa Hexahedron - @test embeddim(h) == 3 - @test Meshes.lentype(h) === Meshes.Met{Float64} - h = rand(Hexahedron) - @test h isa Hexahedron - @test embeddim(h) == 3 - @test Meshes.lentype(h) === Meshes.Met{Float64} - # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) @@ -988,15 +905,6 @@ equaltest(p) isapproxtest(p) - p = rand(Pyramid{3}) - @test p isa Pyramid - @test embeddim(p) == 3 - @test Meshes.lentype(p) === Meshes.Met{Float64} - p = rand(Pyramid) - @test p isa Pyramid - @test embeddim(p) == 3 - @test Meshes.lentype(p) === Meshes.Met{Float64} - p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" if T === Float32 @@ -1035,15 +943,6 @@ equaltest(w) isapproxtest(w) - w = rand(Wedge{3}) - @test w isa Wedge - @test embeddim(w) == 3 - @test Meshes.lentype(w) === Meshes.Met{Float64} - w = rand(Wedge) - @test w isa Wedge - @test embeddim(w) == 3 - @test Meshes.lentype(w) === Meshes.Met{Float64} - w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" if T === Float32 diff --git a/test/primitives.jl b/test/primitives.jl index 57000a48f..8c9871f98 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -45,15 +45,6 @@ @test cart(1, 2, 3) - vector(0, 0, 0) == cart(1, 2, 3) @test cart(2, 3, 4) - vector(2, 1, 0) == cart(0, 2, 4) - @test embeddim(rand(Point{1})) == 1 - @test embeddim(rand(Point{2})) == 2 - @test embeddim(rand(Point{3})) == 3 - @test embeddim(rand(Point)) == 3 - @test Meshes.lentype(rand(Point{1})) == Meshes.Met{Float64} - @test Meshes.lentype(rand(Point{2})) == Meshes.Met{Float64} - @test Meshes.lentype(rand(Point{3})) == Meshes.Met{Float64} - @test Meshes.lentype(rand(Point)) == Meshes.Met{Float64} - @test cart(1) ≈ cart(1 + eps(T)) @test cart(1, 2) ≈ cart(1 + eps(T), T(2)) @test cart(1, 2, 3) ≈ cart(1 + eps(T), T(2), T(3)) @@ -127,9 +118,9 @@ @test measure(cart(1, 2, 3)) == zero(ℳ) # boundary of points is nothing - @test isnothing(boundary(rand(Point{1}))) - @test isnothing(boundary(rand(Point{2}))) - @test isnothing(boundary(rand(Point{3}))) + @test isnothing(boundary(cart(1))) + @test isnothing(boundary(cart(1, 2))) + @test isnothing(boundary(cart(1, 2, 3))) # check broadcasting works as expected @test cart(2, 2) .- [cart(2, 3), cart(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] @@ -234,16 +225,6 @@ r2 = Ray(cart(0, 0, 0), vector(1, 0, 0)) @test r1 == r2 - r2 = rand(Ray{2}) - r3 = rand(Ray{3}) - r = rand(Ray) - @test r2 isa Ray - @test r3 isa Ray - @test r isa Ray - @test embeddim(r2) == 2 - @test embeddim(r3) == 3 - @test embeddim(r) == 3 - r = Ray(cart(0, 0), vector(1, 1)) @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" if T === Float32 @@ -276,16 +257,6 @@ l = Line(cart(0, 0), cart(1, 1)) @test (l(0), l(1)) == (cart(0, 0), cart(1, 1)) - l2 = rand(Line{2}) - l3 = rand(Line{3}) - l = rand(Line) - @test l2 isa Line - @test l3 isa Line - @test l isa Line - @test embeddim(l2) == 2 - @test embeddim(l3) == 3 - @test embeddim(l) == 3 - l = Line(cart(0, 0), cart(1, 1)) @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" if T === Float32 @@ -349,10 +320,6 @@ @test p₂ ∈ p @test p₃ ∈ p - p = rand(Plane) - @test p isa Plane - @test embeddim(p) == 3 - p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) @test sprint(show, p) == "Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, 0.0 m, 0.0 m), v: (0.0 m, 1.0 m, 0.0 m))" @@ -405,16 +372,6 @@ @test t2.time < 5e-4 @test t2.bytes < 100 - b2 = rand(BezierCurve{2}) - b3 = rand(BezierCurve{3}) - b = rand(BezierCurve) - @test b2 isa BezierCurve - @test b3 isa BezierCurve - @test b isa BezierCurve - @test embeddim(b2) == 2 - @test embeddim(b3) == 3 - @test embeddim(b) == 3 - # CRS propagation b = BezierCurve(merc(0, 0), merc(0.5, 1), merc(1, 0)) @test crs(b(T(0), Horner())) === crs(b) @@ -522,19 +479,6 @@ @test b(T(0.0), T(0.0), T(0.0)) == cart(0, 0, 0) @test b(T(1.0), T(1.0), T(1.0)) == cart(10, 20, 30) - b1 = rand(Box{1}) - b2 = rand(Box{2}) - b3 = rand(Box{3}) - b = rand(Box) - @test b1 isa Box - @test b2 isa Box - @test b3 isa Box - @test b isa Box - @test embeddim(b1) == 1 - @test embeddim(b2) == 2 - @test embeddim(b3) == 3 - @test embeddim(b) == 3 - @test_throws AssertionError Box(cart(1), cart(0)) @test_throws AssertionError Box(cart(1, 1), cart(0, 0)) @test_throws AssertionError Box(cart(1, 1, 1), cart(0, 0, 0)) @@ -636,19 +580,6 @@ ps = b.(1, rand(T, 100), rand(T, 100)) all(∈(b), ps) - b1 = rand(Ball{1}) - b2 = rand(Ball{2}) - b3 = rand(Ball{3}) - b = rand(Ball) - @test b1 isa Ball - @test b2 isa Ball - @test b3 isa Ball - @test b isa Ball - @test embeddim(b1) == 1 - @test embeddim(b2) == 2 - @test embeddim(b3) == 3 - @test embeddim(b) == 3 - b = Ball(cart(0, 0), T(1)) @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" if T === Float32 @@ -748,19 +679,6 @@ @test s(T(0), T(0)) ≈ cart(0, 0, 2) @test s(T(0.5), T(0.5)) ≈ cart(-2, 0, 0) - s1 = rand(Sphere{1}) - s2 = rand(Sphere{2}) - s3 = rand(Sphere{3}) - s = rand(Sphere) - @test s1 isa Sphere - @test s2 isa Sphere - @test s3 isa Sphere - @test s isa Sphere - @test embeddim(s1) == 1 - @test embeddim(s2) == 2 - @test embeddim(s3) == 3 - @test embeddim(s) == 3 - s = Sphere(cart(0, 0, 0), T(1)) @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" if T === Float32 @@ -831,10 +749,6 @@ equaltest(d) isapproxtest(d) - d = rand(Disk) - @test d isa Disk - @test embeddim(d) == 3 - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @test sprint(show, d) == @@ -892,10 +806,6 @@ @test c(T(0.75)) ≈ cart(0, -2, 0) @test c(T(1)) ≈ cart(2, 0, 0) - c = rand(Circle) - @test c isa Circle - @test embeddim(c) == 3 - # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(4), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) @@ -979,10 +889,6 @@ @test cart(0, 0, 1.001) ∉ c @test cart(1, 1, 1) ∉ c - c = rand(Cylinder) - @test c isa Cylinder - @test embeddim(c) == 3 - c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) @test sprint(show, c) == "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" @@ -1047,10 +953,6 @@ c = CylinderSurface(1) @test Meshes.lentype(c) == Meshes.Met{Float64} - c = rand(CylinderSurface) - @test c isa CylinderSurface - @test embeddim(c) == 3 - # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) @@ -1127,10 +1029,6 @@ p = ParaboloidSurface(Point(0.0f0, 0.0f0, 0.0f0)) @test Meshes.lentype(p) == Meshes.Met{Float32} - p = rand(ParaboloidSurface) - @test p isa ParaboloidSurface - @test embeddim(p) == 3 - p = ParaboloidSurface(cart(0, 0, 0), T(1), T(1)) @test sprint(show, p) == "ParaboloidSurface(apex: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m, focallength: 1.0 m)" @@ -1176,10 +1074,6 @@ equaltest(c) isapproxtest(c) - c = rand(Cone) - @test c isa Cone - @test embeddim(c) == 3 - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) a = cart(0, 0, 1) @@ -1254,10 +1148,6 @@ equaltest(c) isapproxtest(c) - c = rand(ConeSurface) - @test c isa ConeSurface - @test embeddim(c) == 3 - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) a = cart(0, 0, 1) @@ -1298,9 +1188,6 @@ equaltest(f) isapproxtest(f) - f = rand(Frustum) - @test f isa Frustum - f = Frustum(db, dt) @test cart(0, 0, 0) ∈ f @test cart(0, 0, 10) ∈ f @@ -1349,9 +1236,6 @@ f = FrustumSurface(db, dt) equaltest(f) isapproxtest(f) - - f = rand(FrustumSurface) - @test f isa FrustumSurface end @testset "Torus" begin @@ -1395,12 +1279,6 @@ q = Torus(c₁, c₂, c₃, 1) @test q == t - t = rand(Torus) - @test t isa Torus - @test embeddim(t) == 3 - @test Meshes.lentype(t) == Meshes.Met{Float64} - @test isnothing(boundary(t)) - t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) @test sprint(show, t) == "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" diff --git a/test/rand.jl b/test/rand.jl new file mode 100644 index 000000000..1be23217d --- /dev/null +++ b/test/rand.jl @@ -0,0 +1,260 @@ +@testset "rand" begin + p = rand(Point) + @test p isa Point + @test crs(p) <: Cartesian3D + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(Point, crs=Cartesian2D) + @test p isa Point + @test crs(p) <: Cartesian2D + @test Meshes.lentype(p) === Meshes.Met{Float64} + + r = rand(Ray) + @test r isa Ray + @test crs(r) <: Cartesian3D + @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Ray, crs=Cartesian2D) + @test r isa Ray + @test crs(r) <: Cartesian2D + @test Meshes.lentype(r) === Meshes.Met{Float64} + + l = rand(Line) + @test l isa Line + @test crs(l) <: Cartesian3D + @test Meshes.lentype(l) === Meshes.Met{Float64} + l = rand(Line, crs=Cartesian2D) + @test l isa Line + @test crs(l) <: Cartesian2D + @test Meshes.lentype(l) === Meshes.Met{Float64} + + p = rand(Plane) + @test p isa Plane + @test crs(p) <: Cartesian3D + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(Plane, crs=LatLon) + @test p isa Plane + @test crs(p) <: LatLon + @test Meshes.lentype(p) === Meshes.Met{Float64} + + b = rand(BezierCurve) + @test b isa BezierCurve + @test crs(b) <: Cartesian3D + @test Meshes.lentype(b) === Meshes.Met{Float64} + b = rand(BezierCurve, crs=Cartesian2D) + @test b isa BezierCurve + @test crs(b) <: Cartesian2D + @test Meshes.lentype(b) === Meshes.Met{Float64} + + b = rand(Box) + @test b isa Box + @test crs(b) <: Cartesian3D + @test Meshes.lentype(b) === Meshes.Met{Float64} + b = rand(Box, crs=Cartesian2D) + @test b isa Box + @test crs(b) <: Cartesian2D + @test Meshes.lentype(b) === Meshes.Met{Float64} + + b = rand(Ball) + @test b isa Ball + @test crs(b) <: Cartesian3D + @test Meshes.lentype(b) === Meshes.Met{Float64} + b = rand(Ball, crs=Cartesian2D) + @test b isa Ball + @test crs(b) <: Cartesian2D + @test Meshes.lentype(b) === Meshes.Met{Float64} + + s = rand(Sphere) + @test s isa Sphere + @test crs(s) <: Cartesian3D + @test Meshes.lentype(s) === Meshes.Met{Float64} + s = rand(Sphere, crs=Cartesian2D) + @test s isa Sphere + @test crs(s) <: Cartesian2D + @test Meshes.lentype(s) === Meshes.Met{Float64} + + e = rand(Ellipsoid) + @test e isa Ellipsoid + @test crs(e) <: Cartesian3D + @test Meshes.lentype(e) === Meshes.Met{Float64} + e = rand(Ellipsoid, crs=LatLon) + @test e isa Ellipsoid + @test crs(e) <: LatLon + @test Meshes.lentype(e) === Meshes.Met{Float64} + + d = rand(Disk) + @test d isa Disk + @test crs(d) <: Cartesian3D + @test Meshes.lentype(d) === Meshes.Met{Float64} + d = rand(Disk, crs=LatLon) + @test d isa Disk + @test crs(d) <: LatLon + @test Meshes.lentype(d) === Meshes.Met{Float64} + + c = rand(Circle) + @test c isa Circle + @test crs(c) <: Cartesian3D + @test Meshes.lentype(c) === Meshes.Met{Float64} + c = rand(Circle, crs=LatLon) + @test c isa Circle + @test crs(c) <: LatLon + @test Meshes.lentype(c) === Meshes.Met{Float64} + + c = rand(Cylinder) + @test c isa Cylinder + @test crs(c) <: Cartesian3D + @test Meshes.lentype(c) === Meshes.Met{Float64} + c = rand(Cylinder, crs=LatLon) + @test c isa Cylinder + @test crs(c) <: LatLon + @test Meshes.lentype(c) === Meshes.Met{Float64} + + c = rand(CylinderSurface) + @test c isa CylinderSurface + @test crs(c) <: Cartesian3D + @test Meshes.lentype(c) === Meshes.Met{Float64} + c = rand(CylinderSurface, crs=LatLon) + @test c isa CylinderSurface + @test crs(c) <: LatLon + @test Meshes.lentype(c) === Meshes.Met{Float64} + + p = rand(ParaboloidSurface) + @test p isa ParaboloidSurface + @test crs(p) <: Cartesian3D + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(ParaboloidSurface, crs=LatLon) + @test p isa ParaboloidSurface + @test crs(p) <: LatLon + @test Meshes.lentype(p) === Meshes.Met{Float64} + + c = rand(Cone) + @test c isa Cone + @test crs(c) <: Cartesian3D + @test Meshes.lentype(c) === Meshes.Met{Float64} + c = rand(Cone, crs=LatLon) + @test c isa Cone + @test crs(c) <: LatLon + @test Meshes.lentype(c) === Meshes.Met{Float64} + + c = rand(ConeSurface) + @test c isa ConeSurface + @test crs(c) <: Cartesian3D + @test Meshes.lentype(c) === Meshes.Met{Float64} + c = rand(ConeSurface, crs=LatLon) + @test c isa ConeSurface + @test crs(c) <: LatLon + @test Meshes.lentype(c) === Meshes.Met{Float64} + + f = rand(Frustum) + @test f isa Frustum + @test crs(f) <: Cartesian3D + @test Meshes.lentype(f) === Meshes.Met{Float64} + f = rand(Frustum, crs=LatLon) + @test f isa Frustum + @test crs(f) <: LatLon + @test Meshes.lentype(f) === Meshes.Met{Float64} + + f = rand(FrustumSurface) + @test f isa FrustumSurface + @test crs(f) <: Cartesian3D + @test Meshes.lentype(f) === Meshes.Met{Float64} + f = rand(FrustumSurface, crs=LatLon) + @test f isa FrustumSurface + @test crs(f) <: LatLon + @test Meshes.lentype(f) === Meshes.Met{Float64} + + t = rand(Torus) + @test t isa Torus + @test crs(t) <: Cartesian3D + @test Meshes.lentype(t) === Meshes.Met{Float64} + t = rand(Torus, crs=LatLon) + @test t isa Torus + @test crs(t) <: LatLon + @test Meshes.lentype(t) === Meshes.Met{Float64} + + s = rand(Segment) + @test s isa Segment + @test crs(s) <: Cartesian3D + @test Meshes.lentype(s) === Meshes.Met{Float64} + s = rand(Segment, crs=Cartesian2D) + @test s isa Segment + @test crs(s) <: Cartesian2D + @test Meshes.lentype(s) === Meshes.Met{Float64} + + r = rand(Rope) + @test r isa Rope + @test crs(r) <: Cartesian3D + @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Rope, crs=Cartesian2D) + @test r isa Rope + @test crs(r) <: Cartesian2D + @test Meshes.lentype(r) === Meshes.Met{Float64} + + r = rand(Ring) + @test r isa Ring + @test crs(r) <: Cartesian3D + @test Meshes.lentype(r) === Meshes.Met{Float64} + r = rand(Ring, crs=Cartesian2D) + @test r isa Ring + @test crs(r) <: Cartesian2D + @test Meshes.lentype(r) === Meshes.Met{Float64} + + NGONS = [Triangle, Quadrangle, Pentagon, Hexagon, Heptagon, Octagon, Nonagon, Decagon] + for NGON in NGONS + n = rand(NGON) + @test n isa NGON + @test crs(n) <: Cartesian3D + @test Meshes.lentype(n) === Meshes.Met{Float64} + n = rand(NGON, crs=Cartesian2D) + @test n isa NGON + @test crs(n) <: Cartesian2D + @test Meshes.lentype(n) === Meshes.Met{Float64} + end + + p = rand(PolyArea) + @test p isa PolyArea + @test crs(p) <: Cartesian3D + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(PolyArea, crs=Cartesian2D) + @test p isa PolyArea + @test crs(p) <: Cartesian2D + @test Meshes.lentype(p) === Meshes.Met{Float64} + + t = rand(Tetrahedron) + @test t isa Tetrahedron + @test crs(t) <: Cartesian3D + @test Meshes.lentype(t) === Meshes.Met{Float64} + t = rand(Tetrahedron, crs=LatLon) + @test t isa Tetrahedron + @test crs(t) <: LatLon + @test Meshes.lentype(t) === Meshes.Met{Float64} + + h = rand(Hexahedron) + @test h isa Hexahedron + @test crs(h) <: Cartesian3D + @test Meshes.lentype(h) === Meshes.Met{Float64} + h = rand(Hexahedron, crs=LatLon) + @test h isa Hexahedron + @test crs(h) <: LatLon + @test Meshes.lentype(h) === Meshes.Met{Float64} + + p = rand(Pyramid) + @test p isa Pyramid + @test crs(p) <: Cartesian3D + @test Meshes.lentype(p) === Meshes.Met{Float64} + p = rand(Pyramid, crs=LatLon) + @test p isa Pyramid + @test crs(p) <: LatLon + @test Meshes.lentype(p) === Meshes.Met{Float64} + + w = rand(Wedge) + @test w isa Wedge + @test crs(w) <: Cartesian3D + @test Meshes.lentype(w) === Meshes.Met{Float64} + w = rand(Wedge, crs=LatLon) + @test w isa Wedge + @test crs(w) <: LatLon + @test Meshes.lentype(w) === Meshes.Met{Float64} + + # vector of random geometries + ps = rand(Point, 10) + @test eltype(ps) <: Point +end diff --git a/test/runtests.jl b/test/runtests.jl index 25aaed02d..5c61c15d5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -86,6 +86,7 @@ testfiles = [ "refinement.jl", "coarsening.jl", "transforms.jl", + "rand.jl", "distances.jl", "supportfun.jl", "matrices.jl", From 74ab5338837a903987433f88d67961fad2515afb Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:14:05 -0300 Subject: [PATCH 179/423] Remove `Dim` from types (#942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WIP] Remove 'Dim' from types * Apply suggestions * Fix Dim methods in: geometries.jl, domains.jl * Apply suggestions * Apply suggestions * Apply suggestions * Remove 'Dim' form methods of files: utils.jl, viewing.jl, traversing.jl, predicates.jl * Adjust docstrings * Apply suggestions * Apply suggestions from code review * Add assertdim * Remove 'Dim' from methods in the files: boundary.jl, clamping.jl, measures.jl, orientation.jl, clipping.jl, intersections.jl * Add more assertdim * Fix Dim in docs * More adjustments * Update MeshesMakieExt * Update discretization.jl * Adjust discretization.jl * Remove Dim from tests * Update discretization.jl * Fix code * Fix Primitives testset * Fix Polytopes testset * Fix Domains, SubDomains and Sets testsets * Refactor sampling/regular.jl * Remove more Dim uses in tests * Fix getindex of Grid * Fix Predicates testset * Fix Meshes and Predicates testset * Fix Partitioning and Discretization testset * Fix Repair and viz * Apply suggestions * Fix allocations in 'assertdim' * Fix 'BezierCurve' docstring * Replace 'assertdim' with 'checkdim' --------- Co-authored-by: Júlio Hoffimann --- docs/src/algorithms/clamping.md | 2 +- docs/src/geometries/primitives.md | 6 ++-- ext/MeshesMakieExt.jl | 4 +-- ext/geometryset.jl | 31 +++++++----------- ext/grid.jl | 17 ++++------ ext/grid/cartesian.jl | 4 +-- ext/grid/rectilinear.jl | 4 +-- ext/grid/structured.jl | 4 +-- ext/grid/transformed.jl | 8 +++-- ext/simplemesh.jl | 22 +++++-------- ext/subcartesiangrid.jl | 2 +- src/boundary.jl | 8 +++-- src/boundingboxes.jl | 4 +-- src/clamping.jl | 4 +-- src/clipping/sutherlandhodgman.jl | 2 +- src/coarsening/regular.jl | 20 +++++------ src/discretization.jl | 28 +++++++++------- src/discretization/dehn.jl | 2 +- src/discretization/delaunay.jl | 2 +- src/discretization/fan.jl | 4 +-- src/discretization/held.jl | 2 +- src/discretization/regular.jl | 13 +++++--- src/distances.jl | 14 ++++---- src/domains.jl | 10 +++--- src/geometries.jl | 22 ++++++++----- src/intersections/boxes.jl | 2 +- src/intersections/domains.jl | 4 +-- src/intersections/planes.jl | 6 ++-- src/intersections/polygons.jl | 2 +- src/intersections/rays.jl | 10 +++--- src/intersections/segments.jl | 9 ++--- src/measures.jl | 14 ++++---- src/mesh.jl | 51 ++++++++++++++++++----------- src/mesh/cartesiangrid.jl | 46 +++++++++++--------------- src/mesh/rectilineargrid.jl | 8 ++--- src/mesh/simplemesh.jl | 12 +------ src/mesh/structuredgrid.jl | 10 +++--- src/mesh/transformedmesh.jl | 6 ++-- src/multigeoms.jl | 14 ++++---- src/orientation.jl | 8 +++-- src/partitioning/bisectpoint.jl | 7 ++-- src/polytopes.jl | 21 ++++++------ src/polytopes/ngon.jl | 18 +++++----- src/polytopes/polyarea.jl | 6 ++-- src/polytopes/ring.jl | 10 +++--- src/polytopes/rope.jl | 2 +- src/predicates/in.jl | 6 ++-- src/predicates/intersects.jl | 9 ++--- src/predicates/iscollinear.jl | 3 +- src/predicates/isconvex.jl | 12 ++++--- src/predicates/isperiodic.jl | 6 ++-- src/primitives.jl | 4 +-- src/primitives/ball.jl | 14 ++++---- src/primitives/bezier.jl | 4 +-- src/primitives/box.jl | 12 +++---- src/primitives/circle.jl | 2 +- src/primitives/cone.jl | 4 +-- src/primitives/conesurface.jl | 4 +-- src/primitives/cylinder.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/disk.jl | 2 +- src/primitives/ellipsoid.jl | 6 ++-- src/primitives/frustum.jl | 2 +- src/primitives/frustumsurface.jl | 2 +- src/primitives/line.jl | 6 ++-- src/primitives/paraboloidsurface.jl | 6 ++-- src/primitives/plane.jl | 8 ++--- src/primitives/point.jl | 25 +++++++------- src/primitives/ray.jl | 6 ++-- src/primitives/sphere.jl | 14 ++++---- src/primitives/torus.jl | 15 ++++----- src/projecting.jl | 2 +- src/refinement/regular.jl | 20 ++++++----- src/sampling/homogeneous.jl | 7 ++-- src/sampling/regular.jl | 16 +++++---- src/sets.jl | 6 ++-- src/simplification.jl | 4 ++- src/subdomains.jl | 2 +- src/trajecs.jl | 8 ++--- src/transforms/affine.jl | 6 ++-- src/transforms/repair.jl | 7 ++-- src/transforms/rotate.jl | 6 ++-- src/transforms/scale.jl | 8 +++-- src/transforms/translate.jl | 8 ++--- src/traversing/multigrid.jl | 3 +- src/utils.jl | 17 ++++++++-- src/viewing.jl | 32 +++++++++--------- test/discretization.jl | 2 +- test/domains.jl | 2 +- test/dummy.jl | 8 ++--- test/mesh.jl | 28 ++++++++-------- test/polytopes.jl | 7 +--- test/predicates.jl | 13 ++++---- test/primitives.jl | 4 +-- test/sampling.jl | 22 ++++++------- test/sets.jl | 8 ++--- test/subdomains.jl | 10 +++--- test/testutils.jl | 8 +++-- 98 files changed, 487 insertions(+), 458 deletions(-) diff --git a/docs/src/algorithms/clamping.md b/docs/src/algorithms/clamping.md index 149751b1c..86a17e0f2 100644 --- a/docs/src/algorithms/clamping.md +++ b/docs/src/algorithms/clamping.md @@ -3,7 +3,7 @@ Meshes adds methods to Julia's built-in `clamp` function. The additional methods clamp points to the edges of a box in any number of dimensions. The target points and boxes must have the same number of dimensions and the same numeric type. ```@docs -clamp(::Point{Dim}, ::Box{Dim}) where {Dim} +clamp(::Point, ::Box) clamp(::PointSet, ::Box) ``` diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 1aac5ea9e..0d3eb96b0 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -25,9 +25,9 @@ rand(Point, 100) |> viz ```@docs to(::Point) --(::Point{Dim}, ::Point{Dim}) where {Dim} -+(::Point{Dim}, ::Vec{Dim}) where {Dim} --(::Point{Dim}, ::Vec{Dim}) where {Dim} +-(::Point, ::Point) ++(::Point, ::Vec) +-(::Point, ::Vec) ``` ### Ray diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index c58fbdd85..7a03abd67 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -32,8 +32,8 @@ Makie.@recipe(Viz, object) do scene end # choose between 2D and 3D axis -Makie.args_preferred_axis(::Geometry{Dim}) where {Dim} = Dim === 3 ? Makie.LScene : Makie.Axis -Makie.args_preferred_axis(::Domain{Dim}) where {Dim} = Dim === 3 ? Makie.LScene : Makie.Axis +Makie.args_preferred_axis(g::Geometry) = embeddim(g) === 3 ? Makie.LScene : Makie.Axis +Makie.args_preferred_axis(d::Domain) = embeddim(d) === 3 ? Makie.LScene : Makie.Axis Makie.args_preferred_axis(::AbstractVector{<:Vec{Dim}}) where {Dim} = Dim === 3 ? Makie.LScene : Makie.Axis # color handling diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 46c0a69e2..23915bc3d 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -22,16 +22,9 @@ function Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) inds = Makie.@lift findall(g -> g isa G, $geoms) gvec = Makie.@lift collect(G, $geoms[$inds]) colors = Makie.@lift $colorant isa AbstractVector ? $colorant[$inds] : $colorant - rank = Makie.@lift paramdim(first($gvec)) - if rank[] == 0 - vizgset0D!(plot, gvec, colors) - elseif rank[] == 1 - vizgset1D!(plot, gvec, colors) - elseif rank[] == 2 - vizgset2D!(plot, gvec, colors) - elseif rank[] == 3 - vizgset3D!(plot, gvec, colors) - end + pdim = Makie.@lift paramdim(first($gvec)) + edim = Makie.@lift embeddim(first($gvec)) + vizgset!(plot, Val(pdim[]), Val(edim[]), gvec, colors) end end @@ -56,18 +49,18 @@ end const ObservableVector{T} = Makie.Observable{<:AbstractVector{T}} -function vizgset0D!(plot, geoms, colorant) +function vizgset!(plot, ::Val{0}, ::Val, geoms, colorant) points = Makie.@lift pointify.($geoms) vizmany!(plot, points, colorant) end -function vizgset1D!(plot, geoms, colorant) +function vizgset!(plot, ::Val{1}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) showfacets1D!(plot, geoms) end -function vizgset1D!(plot, geoms::ObservableVector{<:Ray}, colorant) +function vizgset!(plot, ::Val{1}, ::Val, geoms::ObservableVector{<:Ray}, colorant) rset = plot[:object] segmentsize = plot[:segmentsize] @@ -84,15 +77,15 @@ function vizgset1D!(plot, geoms::ObservableVector{<:Ray}, colorant) showfacets1D!(plot, geoms) end -function vizgset2D!(plot, geoms, colorant) +function vizgset!(plot, ::Val{2}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) showfacets2D!(plot, geoms) end -const PolygonLike{Dim} = Union{Polygon{Dim},MultiPolygon{Dim}} +const PolygonLike = Union{Polygon,MultiPolygon} -function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) +function vizgset!(plot, ::Val{2}, ::Val{2}, geoms::ObservableVector{<:PolygonLike}, colorant) showsegments = plot[:showsegments] segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] @@ -109,7 +102,7 @@ function vizgset2D!(plot, geoms::ObservableVector{<:PolygonLike{2}}, colorant) end end -function vizgset3D!(plot, geoms, colorant) +function vizgset!(plot, ::Val{3}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.(boundary.($geoms)) vizmany!(plot, meshes, colorant) end @@ -155,6 +148,6 @@ function asmakie(poly::Polygon) end end -asmakie(p::Point{Dim}) where {Dim} = Makie.Point{Dim}(ustrip.(Tuple(to(p)))) +asmakie(p::Point) = Makie.Point(ustrip.(Tuple(to(p)))) -asmakie(v::Vec{Dim}) where {Dim} = Makie.Vec{Dim}(ustrip.(Tuple(v))) +asmakie(v::Vec) = Makie.Vec(ustrip.(Tuple(v))) diff --git a/ext/grid.jl b/ext/grid.jl index d8ed0847a..d12373a1a 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -3,18 +3,15 @@ # ------------------------------------------------------------------ function Makie.plot!(plot::Viz{<:Tuple{Grid}}) - grid = plot[:object][] - Dim = embeddim(grid) - if Dim == 2 - vizgrid2D!(plot) - elseif Dim == 3 - vizgrid3D!(plot) - end + grid = plot[:object] + pdim = Makie.@lift paramdim($grid) + edim = Makie.@lift embeddim($grid) + vizgrid!(plot, Val(pdim[]), Val(edim[])) end -vizgrid2D!(plot) = vizmesh2D!(plot) +vizgrid!(plot, ::Val{2}, ::Val{2}) = vizmesh!(plot, Val(2), Val(2)) -function vizgrid3D!(plot) +function vizgrid!(plot, ::Val{3}, ::Val{3}) grid = plot[:object] color = plot[:color] @@ -25,7 +22,7 @@ function vizgrid3D!(plot) if nv[] == nc[] error("not implemented") else - vizmesh3D!(plot) + vizmesh!(plot, Val(3), Val(3)) end end diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index ad9809edd..da72c628b 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) +function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Val{2}, ::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -61,7 +61,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{CartesianGrid}}) end end -function vizgrid3D!(plot::Viz{<:Tuple{CartesianGrid}}) +function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Val{3}, ::Val{3}) # retrieve parameters grid = plot[:object] color = plot[:color] diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index a83acc073..f388de1a9 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) +function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, ::Val{2}, ::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -33,7 +33,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{RectilinearGrid}}) if nc[] == nv[] # visualize as a simple mesh so that # colors can be specified at vertices - vizmesh2D!(plot) + vizmesh!(plot, Val(2), Val(2)) else # visualize as built-in heatmap sz = Makie.@lift size($grid) diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 020455653..7e35d6bf6 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) +function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, ::Val{2}, ::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -36,7 +36,7 @@ function vizgrid2D!(plot::Viz{<:Tuple{StructuredGrid}}) Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end else - vizmesh2D!(plot) + vizmesh!(plot, Val(2), Val(2)) end end diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 00c8badf3..257fa15f8 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -14,9 +14,11 @@ function isoptimized(t::Affine{2}) isdiag(A) || isrotation(A) end -vizgrid2D!(plot::Viz{<:Tuple{TransformedGrid}}) = transformedgrid!(plot, vizmesh2D!) +vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, ::Val{2}, ::Val{2}) = + transformedgrid!(plot, plot -> vizmesh!(plot, Val(2), Val(2))) -vizgrid3D!(plot::Viz{<:Tuple{TransformedGrid}}) = transformedgrid!(plot, vizmesh3D!) +vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, ::Val{3}, ::Val{3}) = + transformedgrid!(plot, plot -> vizmesh!(plot, Val(3), Val(3))) function transformedgrid!(plot, fallback) tgrid = plot[:object] @@ -32,7 +34,7 @@ function transformedgrid!(plot, fallback) viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) else - fallback(tgrid) + fallback(plot) end end diff --git a/ext/simplemesh.jl b/ext/simplemesh.jl index 16825a490..1e95956fe 100644 --- a/ext/simplemesh.jl +++ b/ext/simplemesh.jl @@ -3,20 +3,14 @@ # ------------------------------------------------------------------ function Makie.plot!(plot::Viz{<:Tuple{SimpleMesh}}) - # retrieve mesh and rank - mesh = plot[:object][] - rank = paramdim(mesh) - - if rank == 1 - vizmesh1D!(plot) - elseif rank == 2 - vizmesh2D!(plot) - elseif rank == 3 - vizmesh3D!(plot) - end + # retrieve mesh and dimensions + mesh = plot[:object] + pdim = Makie.@lift paramdim($mesh) + edim = Makie.@lift embeddim($mesh) + vizmesh!(plot, Val(pdim[]), Val(edim[])) end -function vizmesh1D!(plot) +function vizmesh!(plot, ::Val{1}, ::Val) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -48,7 +42,7 @@ function vizmesh1D!(plot) Makie.lines!(plot, coords, color=colors, linewidth=segmentsize) end -function vizmesh2D!(plot) +function vizmesh!(plot, ::Val{2}, ::Val) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -187,7 +181,7 @@ function vizmesh2D!(plot) end end -function vizmesh3D!(plot) +function vizmesh!(plot, ::Val{3}, ::Val) mesh = plot[:object] color = plot[:color] meshes = Makie.@lift let diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl index 71979a0b2..d999570ec 100644 --- a/ext/subcartesiangrid.jl +++ b/ext/subcartesiangrid.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -const SubCartesianGrid{Dim,CRS} = SubDomain{Dim,CRS,<:CartesianGrid{Dim,CRS}} +const SubCartesianGrid{CRS} = SubDomain{CRS,<:CartesianGrid{CRS}} function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) subgrid = plot[:object] diff --git a/src/boundary.jl b/src/boundary.jl index 182a49ac5..b0bc61130 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -23,16 +23,18 @@ end boundary(::Plane) = nothing -boundary(b::Box{1}) = Multi([minimum(b), maximum(b)]) +boundary(b::Box) = _boundary(b, Val(embeddim(b))) -function boundary(b::Box{2}) +_boundary(b::Box, ::Val{1}) = Multi([minimum(b), maximum(b)]) + +function _boundary(b::Box, ::Val{2}) A = to(minimum(b)) B = to(maximum(b)) v = Point.([(A[1], A[2]), (B[1], A[2]), (B[1], B[2]), (A[1], B[2])]) Ring(v) end -function boundary(b::Box{3}) +function _boundary(b::Box, ::Val{3}) A = to(minimum(b)) B = to(maximum(b)) v = diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index afb367502..e0c764c13 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -39,10 +39,10 @@ function boundingbox(r::Ray) Box(withcrs(r, l), withcrs(r, u)) end -function boundingbox(s::Sphere{Dim}) where {Dim} +function boundingbox(s::Sphere) c = center(s) r = radius(s) - r⃗ = Vec(ntuple(i -> r, Dim)) + r⃗ = Vec(ntuple(i -> r, embeddim(s))) Box(c - r⃗, c + r⃗) end diff --git a/src/clamping.jl b/src/clamping.jl index 5ad5434a6..ab58ea7ca 100644 --- a/src/clamping.jl +++ b/src/clamping.jl @@ -10,11 +10,11 @@ Clamp the coordinates of a [`Point`](@ref) to the edges of a [`Box`](@ref). For each dimension, coordinates outside of the box are moved to the nearest edge of the box. The point and box must have an equal number of dimensions. """ -function Base.clamp(point::Point{Dim}, box::Box{Dim}) where {Dim} +function Base.clamp(point::Point, box::Box) x = to(point) lo = to(minimum(box)) hi = to(maximum(box)) - ntuple(Dim) do i + ntuple(embeddim(point)) do i clamp(x[i], lo[i], hi[i]) end |> Point end diff --git a/src/clipping/sutherlandhodgman.jl b/src/clipping/sutherlandhodgman.jl index 5f2f1e016..819359681 100644 --- a/src/clipping/sutherlandhodgman.jl +++ b/src/clipping/sutherlandhodgman.jl @@ -24,7 +24,7 @@ function clip(poly::Polygon, other::Geometry, method::SutherlandHodgman) isempty(r) ? nothing : PolyArea(r) end -function clip(ring::Ring{Dim}, other::Ring{Dim}, ::SutherlandHodgman) where {Dim} +function clip(ring::Ring, other::Ring, ::SutherlandHodgman) # make sure other ring is CCW occw = orientation(other) == CCW ? other : reverse(other) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 0fa0c29e3..21909594f 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -20,25 +20,25 @@ end RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) -function coarsen(grid::CartesianGrid{Dim}, method::RegularCoarsening) where {Dim} - factors = fitdims(method.factors, Dim) +function coarsen(grid::CartesianGrid, method::RegularCoarsening) + factors = fitdims(method.factors, embeddim(grid)) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end -function coarsen(grid::RectilinearGrid{Datum,Dim}, method::RegularCoarsening) where {Datum,Dim} - factors = fitdims(method.factors, Dim) +function coarsen(grid::RectilinearGrid{Datum}, method::RegularCoarsening) where {Datum} + factors = fitdims(method.factors, embeddim(grid)) dims = size(grid) .+ .!isperiodic(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) + rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) xyzₛ = xyz(grid) - xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], Dim) + xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], embeddim(grid)) RectilinearGrid{Datum}(xyzₜ) end -function coarsen(grid::StructuredGrid{Datum,Dim}, method::RegularCoarsening) where {Datum,Dim} - factors = fitdims(method.factors, Dim) +function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum} + factors = fitdims(method.factors, embeddim(grid)) dims = size(grid) .+ .!isperiodic(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], Dim) + rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) XYZₛ = XYZ(grid) - XYZₜ = ntuple(i -> XYZₛ[i][rngs...], Dim) + XYZₜ = ntuple(i -> XYZₛ[i][rngs...], embeddim(grid)) StructuredGrid{Datum}(XYZₜ) end diff --git a/src/discretization.jl b/src/discretization.jl index 92ad6f052..8000dbd2b 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -91,7 +91,9 @@ end discretize(multi::Multi, method::BoundaryDiscretizationMethod) = mapreduce(geom -> discretize(geom, method), merge, parent(multi)) -function discretizewithin(ring::Ring{3}, method::BoundaryDiscretizationMethod) +discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) = _discretizewithin(ring, Val(embeddim(ring)), method) + +function _discretizewithin(ring::Ring, ::Val{3}, method::BoundaryDiscretizationMethod) # collect vertices to get rid of static containers points = collect(vertices(ring)) @@ -107,13 +109,15 @@ end # DEFAULT METHODS # ---------------- -discretize(geometry) = simplexify(geometry) +discretize(geometry) = _discretize(geometry, Val(embeddim(geometry))) + +_discretize(geometry, ::Val) = simplexify(geometry) -discretize(ball::Ball{2}) = discretize(ball, RegularDiscretization(50)) +_discretize(ball::Ball, ::Val{2}) = discretize(ball, RegularDiscretization(50)) discretize(disk::Disk) = discretize(disk, RegularDiscretization(50)) -discretize(sphere::Sphere{3}) = discretize(sphere, RegularDiscretization(50)) +_discretize(sphere::Sphere, ::Val{3}) = discretize(sphere, RegularDiscretization(50)) discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(50)) @@ -146,9 +150,15 @@ when the `object` has parametric dimension 2. """ function simplexify end -simplexify(geometry) = simplexify(discretize(geometry)) +simplexify(geometry) = _simplexify(geometry, Val(embeddim(geometry))) + +_simplexify(geometry, ::Val) = simplexify(discretize(geometry)) + +_simplexify(box::Box, ::Val{1}) = SimpleMesh(collect(extrema(box)), GridTopology(1)) -simplexify(box::Box{1}) = SimpleMesh(collect(extrema(box)), GridTopology(1)) +_simplexify(box::Box, ::Val{2}) = discretize(box, FanTriangulation()) + +_simplexify(box::Box, ::Val{3}) = discretize(box, Tetrahedralization()) simplexify(seg::Segment) = SimpleMesh(pointify(seg), GridTopology(1)) @@ -164,14 +174,10 @@ end simplexify(bezier::BezierCurve) = discretize(bezier, RegularDiscretization(50)) -simplexify(sphere::Sphere{2}) = discretize(sphere, RegularDiscretization(50)) +_simplexify(sphere::Sphere, ::Val{2}) = discretize(sphere, RegularDiscretization(50)) simplexify(circle::Circle) = discretize(circle, RegularDiscretization(50)) -simplexify(box::Box{2}) = discretize(box, FanTriangulation()) - -simplexify(box::Box{3}) = discretize(box, Tetrahedralization()) - simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? DelaunayTriangulation() : DehnTriangulation()) simplexify(poly::Polyhedron) = discretize(poly, Tetrahedralization()) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index f646b53a7..a39b0eb70 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -21,7 +21,7 @@ with small number of vertices. """ struct DehnTriangulation <: BoundaryDiscretizationMethod end -function discretizewithin(ring::Ring{2}, ::DehnTriangulation) +function _discretizewithin(ring::Ring, ::Val{2}, ::DehnTriangulation) # points on resulting mesh points = collect(vertices(ring)) diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl index b93b01432..70feab3e6 100644 --- a/src/discretization/delaunay.jl +++ b/src/discretization/delaunay.jl @@ -24,7 +24,7 @@ end DelaunayTriangulation(rng=Random.default_rng()) = DelaunayTriangulation(rng) -function discretizewithin(ring::Ring{2}, method::DelaunayTriangulation) +function _discretizewithin(ring::Ring, ::Val{2}, method::DelaunayTriangulation) points = vertices(ring) coords = map(p -> ustrip.(to(p)), points) bnodes = [1:nvertices(ring); 1] diff --git a/src/discretization/fan.jl b/src/discretization/fan.jl index 31f8bb801..7ee608969 100644 --- a/src/discretization/fan.jl +++ b/src/discretization/fan.jl @@ -11,9 +11,9 @@ See [https://en.wikipedia.org/wiki/Fan_triangulation] """ struct FanTriangulation <: BoundaryDiscretizationMethod end -discretizewithin(ring::Ring{2}, ::FanTriangulation) = fan(ring) +_discretizewithin(ring::Ring, ::Val{2}, ::FanTriangulation) = fan(ring) -discretizewithin(ring::Ring{3}, ::FanTriangulation) = fan(ring) +_discretizewithin(ring::Ring, ::Val{3}, ::FanTriangulation) = fan(ring) function fan(ring::Ring) points = collect(vertices(ring)) diff --git a/src/discretization/held.jl b/src/discretization/held.jl index 89d2b5220..9aeff4ff3 100644 --- a/src/discretization/held.jl +++ b/src/discretization/held.jl @@ -33,7 +33,7 @@ end HeldTriangulation(rng=Random.default_rng(); shuffle=true) = HeldTriangulation(rng, shuffle) -function discretizewithin(ring::Ring{2}, method::HeldTriangulation) +function _discretizewithin(ring::Ring, ::Val{2}, method::HeldTriangulation) # helper function to shuffle ears earshuffle!(𝒬) = method.shuffle && shuffle!(method.rng, 𝒬) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index 7d11576f5..8e5a7e90f 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -42,21 +42,24 @@ function wrapgrid(g, m) ps, tg end -perdims(g) = isperiodic(g) -perdims(::Sphere{3}) = (false, true) +perdims(g) = _perdims(g, Val(embeddim(g))) +_perdims(g, ::Val) = isperiodic(g) +_perdims(::Sphere, ::Val{3}) = (false, true) perdims(::Ellipsoid) = (false, true) # ------------------------ # append to grid topology # ------------------------ -appendtopo(g, tg) = tg +appendtopo(g, tg) = _appendtopo(g, Val(embeddim(g)), tg) -appendtopo(::Ball{2}, tg) = _appendcenter(tg) +_appendtopo(g, ::Val, tp) = tp + +_appendtopo(::Ball, ::Val{2}, tg) = _appendcenter(tg) appendtopo(::Disk, tg) = _appendcenter(tg) -appendtopo(::Sphere{3}, tg) = _appendpoles(tg, 2, true) +_appendtopo(::Sphere, ::Val{3}, tg) = _appendpoles(tg, 2, true) appendtopo(::Ellipsoid, tg) = _appendpoles(tg, 2, true) diff --git a/src/distances.jl b/src/distances.jl index fd7cd6798..a2ac5f8a7 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -10,7 +10,7 @@ evaluate(d::PreMetric, g::Geometry, p::Point) = evaluate(d, p, g) Evaluate the Euclidean `distance` between `point` and `line`. """ -function evaluate(::Euclidean, p::Point{Dim}, l::Line{Dim}) where {Dim} +function evaluate(::Euclidean, p::Point, l::Line) a, b = l(0), l(1) u = p - a v = b - a @@ -23,7 +23,7 @@ end Evaluate the minimum Euclidean `distance` between `line₁` and `line₂`. """ -function evaluate(d::Euclidean, l₁::Line{Dim}, l₂::Line{Dim}) where {Dim} +function evaluate(d::Euclidean, l₁::Line, l₂::Line) λ₁, λ₂, r, rₐ = intersectparameters(l₁(0), l₁(1), l₂(0), l₂(1)) if (r == rₐ == 2) || (r == rₐ == 1) # lines intersect or are colinear @@ -40,7 +40,7 @@ end Evaluate pre-metric `distance` between coordinates of `point₁` and `point₂`. """ -function evaluate(d::PreMetric, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} +function evaluate(d::PreMetric, p₁::Point, p₂::Point) u₁ = unit(Meshes.lentype(p₁)) u₂ = unit(Meshes.lentype(p₂)) u = Unitful.promote_unit(u₁, u₂) @@ -53,7 +53,7 @@ end # SPECIAL CASES # -------------- -function evaluate(d::Haversine, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:LatLon}) where {Dim} +function evaluate(d::Haversine, p₁::Point{<:LatLon}, p₂::Point{<:LatLon}) uᵣ = unit(d.radius) # add default unit if necessary u = uᵣ === NoUnits ? u"m" : NoUnits @@ -64,10 +64,10 @@ function evaluate(d::Haversine, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:Lat evaluate(d, v₁, v₂) * u end -evaluate(d::Haversine, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} = +evaluate(d::Haversine, p₁::Point, p₂::Point) = evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) -function evaluate(d::SphericalAngle, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim,<:LatLon}) where {Dim} +function evaluate(d::SphericalAngle, p₁::Point{<:LatLon}, p₂::Point{<:LatLon}) latlon₁ = coords(p₁) latlon₂ = coords(p₂) v₁ = SVector(deg2rad(latlon₁.lon), deg2rad(latlon₁.lat)) @@ -75,5 +75,5 @@ function evaluate(d::SphericalAngle, p₁::Point{Dim,<:LatLon}, p₂::Point{Dim, evaluate(d, v₁, v₂) * u"rad" end -evaluate(d::SphericalAngle, p₁::Point{Dim}, p₂::Point{Dim}) where {Dim} = +evaluate(d::SphericalAngle, p₁::Point, p₂::Point) = evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) diff --git a/src/domains.jl b/src/domains.jl index a51753385..a8a8059f8 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Domain{Dim,CRS} + Domain{CRS} A domain is an indexable collection of geometries (e.g. mesh). """ -abstract type Domain{Dim,CRS} end +abstract type Domain{CRS} end """ element(domain, ind) @@ -60,7 +60,7 @@ Base.vcat(ds::Domain...) = reduce(vcat, ds) Return the number of dimensions of the space where the `domain` is embedded. """ -embeddim(::Type{<:Domain{Dim}}) where {Dim} = Dim +embeddim(::Type{<:Domain{CRS}}) where {CRS} = CoordRefSystems.ndims(CRS) embeddim(d::Domain) = embeddim(typeof(d)) """ @@ -76,7 +76,7 @@ paramdim(d::Domain) = paramdim(first(d)) Return the coordinate reference system (CRS) of the `domain`. """ -crs(::Type{<:Domain{Dim,CRS}}) where {Dim,CRS} = CRS +crs(::Type{<:Domain{CRS}}) where {CRS} = CRS crs(d::Domain) = crs(typeof(d)) """ @@ -84,7 +84,7 @@ crs(d::Domain) = crs(typeof(d)) Return the length type of the `domain`. """ -lentype(::Type{<:Domain{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) +lentype(::Type{<:Domain{CRS}}) where {CRS} = lentype(CRS) lentype(d::Domain) = lentype(typeof(d)) """ diff --git a/src/geometries.jl b/src/geometries.jl index 64b1adf5c..358d5b58d 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -3,11 +3,11 @@ # ------------------------------------------------------------------ """ - Geometry{Dim,CRS} + Geometry{CRS} -A geometry embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. +A geometry with given coordinate reference system `CRS`. """ -abstract type Geometry{Dim,CRS} end +abstract type Geometry{CRS} end Broadcast.broadcastable(g::Geometry) = Ref(g) @@ -16,7 +16,7 @@ Broadcast.broadcastable(g::Geometry) = Ref(g) Return the number of dimensions of the space where the `geometry` is embedded. """ -embeddim(::Type{<:Geometry{Dim}}) where {Dim} = Dim +embeddim(::Type{<:Geometry{CRS}}) where {CRS} = CoordRefSystems.ndims(CRS) embeddim(g::Geometry) = embeddim(typeof(g)) """ @@ -34,7 +34,7 @@ paramdim(g::Geometry) = paramdim(typeof(g)) Return the coordinate reference system (CRS) of the `geometry`. """ -crs(::Type{<:Geometry{Dim,CRS}}) where {Dim,CRS} = CRS +crs(::Type{<:Geometry{CRS}}) where {CRS} = CRS crs(g::Geometry) = crs(typeof(g)) """ @@ -42,7 +42,7 @@ crs(g::Geometry) = crs(typeof(g)) Return the length type of the `geometry`. """ -lentype(::Type{<:Geometry{Dim,CRS}}) where {Dim,CRS} = lentype(CRS) +lentype(::Type{<:Geometry{CRS}}) where {CRS} = lentype(CRS) lentype(g::Geometry) = lentype(typeof(g)) """ @@ -78,6 +78,12 @@ include("multigeoms.jl") # CONVERSIONS # ------------ -Base.convert(::Type{<:Quadrangle}, b::Box{2}) = Quadrangle(vertices(boundary(b))...) +function Base.convert(::Type{<:Quadrangle}, b::Box) + checkdim(b, 2) + Quadrangle(vertices(boundary(b))...) +end -Base.convert(::Type{<:Hexahedron}, b::Box{3}) = Hexahedron(vertices(boundary(b))...) +function Base.convert(::Type{<:Hexahedron}, b::Box) + checkdim(b, 3) + Hexahedron(vertices(boundary(b))...) +end diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index 923619b85..f7419499d 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -8,7 +8,7 @@ # 2. intersect at corner point (CornerTouching -> Point) # 3. intersect at one of the facets (Touching -> Box) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, box₁::Box{Dim}, box₂::Box{Dim}) where {Dim} +function intersection(f, box₁::Box, box₂::Box) # retrieve corner points m1, M1 = to.(extrema(box₁)) m2, M2 = to.(extrema(box₂)) diff --git a/src/intersections/domains.jl b/src/intersections/domains.jl index 99e0bcfd4..d82808540 100644 --- a/src/intersections/domains.jl +++ b/src/intersections/domains.jl @@ -13,9 +13,9 @@ end intersection(f, dom::Domain, pset::PointSet) = intersection(f, Multi(collect(dom)), pset) -function intersection(f, dom₁::Domain{Dim}, dom₂::Domain{Dim}) where {Dim} +function intersection(f, dom₁::Domain, dom₂::Domain) # loop over all geometries - gs = Geometry{Dim}[] + gs = Geometry[] for g₁ in dom₁, g₂ in dom₂ g = g₁ ∩ g₂ isnothing(g) || push!(gs, g) diff --git a/src/intersections/planes.jl b/src/intersections/planes.jl index 950daf255..361cb8995 100644 --- a/src/intersections/planes.jl +++ b/src/intersections/planes.jl @@ -26,7 +26,7 @@ function intersection(f, plane1::Plane, plane2::Plane) end end -const LineLike = Union{Segment{3},Ray{3},Line{3}} +const LineLike = Union{Segment,Ray,Line} # (https://en.wikipedia.org/wiki/Line-plane_intersection) function intersection(f, line::LineLike, plane::Plane) @@ -52,7 +52,7 @@ end # λ < 0 or λ > 1 ⟹ NotIntersecting # λ ≈ 0 or λ ≈ 1 ⟹ Touching # λ > 0 and λ < 1 ⟹ Crossing -function _intersection(f, seg::Segment{3}, λ) +function _intersection(f, seg::Segment, λ) # if λ is approximately 0, set as so to prevent any domain errors if isapproxzero(λ) return @IT Touching seg(0) f @@ -77,7 +77,7 @@ end # λ < 0 ⟹ NotIntersecting # λ ≈ 0 ⟹ Touching # λ > 0 ⟹ Crossing -function _intersection(f, ray::Ray{3}, λ) +function _intersection(f, ray::Ray, λ) # if λ is approximately 0, set as so to prevent any domain errors if isapproxzero(λ) return @IT Touching ray(0) f diff --git a/src/intersections/polygons.jl b/src/intersections/polygons.jl index 930c02f5e..9688ef427 100644 --- a/src/intersections/polygons.jl +++ b/src/intersections/polygons.jl @@ -19,4 +19,4 @@ function intersection(f, poly₁::Polygon, poly₂::Polygon) end end -intersection(f, poly::Polygon{2}, box::Box{2}) = intersection(f, poly, convert(Quadrangle, box)) +intersection(f, poly::Polygon, box::Box) = intersection(f, poly, convert(Quadrangle, box)) diff --git a/src/intersections/rays.jl b/src/intersections/rays.jl index 5489449cf..e9dc1b1df 100644 --- a/src/intersections/rays.jl +++ b/src/intersections/rays.jl @@ -10,7 +10,7 @@ # 4. overlap with aligned vectors (PosOverlapping -> Ray) # 5. overlap with colliding vectors (NegOverlapping -> Segment) # 6. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, ray₁::Ray{Dim}, ray₂::Ray{Dim}) where {Dim} +function intersection(f, ray₁::Ray, ray₂::Ray) a, b = ray₁(0), ray₁(1) c, d = ray₂(0), ray₂(1) @@ -70,7 +70,7 @@ end # 2. intersect at origin of ray (Touching -> Point) # 3. overlap of line and ray (Overlapping -> Ray) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, ray::Ray{Dim}, line::Line{Dim}) where {Dim} +function intersection(f, ray::Ray, line::Line) a, b = ray(0), ray(1) c, d = line(0), line(1) @@ -98,7 +98,7 @@ end # Williams A, Barrus S, Morley R K, et al., 2005. # (https://dl.acm.org/doi/abs/10.1145/1198555.1198748) -function intersection(f, ray::Ray{Dim}, box::Box{Dim}) where {Dim} +function intersection(f, ray::Ray, box::Box) ℒ = lentype(ray) invdir = inv.(ray(1) - ray(0)) lo, up = to.(extrema(box)) @@ -109,7 +109,7 @@ function intersection(f, ray::Ray{Dim}, box::Box{Dim}) where {Dim} tmax = typemax(T) # check for intersection with slabs along with each axis - for i in 1:Dim + for i in 1:embeddim(ray) imin = (lo[i] - orig[i]) * invdir[i] imax = (up[i] - orig[i]) * invdir[i] @@ -142,7 +142,7 @@ end # # Möller, T. & Trumbore, B., 1997. # (https://www.tandfonline.com/doi/abs/10.1080/10867651.1997.10487468) -function intersection(f, ray::Ray{3}, tri::Triangle{3}) +function intersection(f, ray::Ray, tri::Triangle) vs = vertices(tri) o = ray(0) d = ray(1) - ray(0) diff --git a/src/intersections/segments.jl b/src/intersections/segments.jl index feada9de8..e2cfd8f4a 100644 --- a/src/intersections/segments.jl +++ b/src/intersections/segments.jl @@ -9,7 +9,7 @@ # 3. intersect at one endpoint of both segments (CornerTouching -> Point) # 4. overlap of segments (Overlapping -> Segments) # 5. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg₁::Segment{Dim}, seg₂::Segment{Dim}) where {Dim} +function intersection(f, seg₁::Segment, seg₂::Segment) a, b = vertices(seg₁) c, d = vertices(seg₂) @@ -104,7 +104,8 @@ end # 3. intersects at one end point of segment and origin of ray (CornerTouching -> Point) # 4. overlap at more than one point (Overlapping -> Segment) # 5. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg::Segment{Dim}, ray::Ray{Dim}) where {Dim} +function intersection(f, seg::Segment, ray::Ray) + Dim = embeddim(seg) a, b = ray(0), ray(1) c, d = seg(0), seg(1) @@ -173,7 +174,7 @@ end # 2. intersect at an end point of segment (Touching -> Point) # 3. overlap of line and segment (Overlapping -> Segment) # 4. do not overlap nor intersect (NotIntersecting -> Nothing) -function intersection(f, seg::Segment{Dim}, line::Line{Dim}) where {Dim} +function intersection(f, seg::Segment, line::Line) a, b = line(0), line(1) c, d = seg(0), seg(1) @@ -204,7 +205,7 @@ end # Algorithm 4 of Jiménez, J., Segura, R. and Feito, F. 2009. # (https://www.sciencedirect.com/science/article/pii/S0925772109001448?via%3Dihub) -function intersection(f, seg::Segment{3}, tri::Triangle{3}) +function intersection(f, seg::Segment, tri::Triangle) Q1, Q2 = vertices(seg) V1, V2, V3 = vertices(tri) diff --git a/src/measures.jl b/src/measures.jl index 3a04cce60..143afd171 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -28,16 +28,16 @@ measure(p::Plane) = typemax(lentype(p))^2 measure(b::Box) = prod(maximum(b) - minimum(b)) # https://en.wikipedia.org/wiki/Volume_of_an_n-ball -function measure(b::Ball{Dim}) where {Dim} +function measure(b::Ball) T = numtype(lentype(b)) - r, n = radius(b), Dim + r, n = radius(b), embeddim(b) T(π)^T(n / 2) * r^n / gamma(T(n / 2) + 1) end # https://en.wikipedia.org/wiki/N-sphere#Volume_and_surface_area -function measure(s::Sphere{Dim}) where {Dim} +function measure(s::Sphere) T = numtype(lentype(s)) - r, n = radius(s), Dim + r, n = radius(s), embeddim(s) 2 * T(π)^T(n / 2) * r^(n - 1) / gamma(T(n / 2)) end @@ -77,9 +77,11 @@ end measure(s::Segment) = norm(maximum(s) - minimum(s)) -measure(t::Triangle{2}) = abs(signarea(t)) +measure(t::Triangle) = _measure(t, Val(embeddim(t))) -function measure(t::Triangle{3}) +_measure(t::Triangle, ::Val{2}) = abs(signarea(t)) + +function _measure(t::Triangle, ::Val{3}) A, B, C = vertices(t) norm((B - A) × (C - A)) / 2 end diff --git a/src/mesh.jl b/src/mesh.jl index 4711fe26c..aad22f692 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -3,12 +3,11 @@ # ------------------------------------------------------------------ """ - Mesh{Dim,CRS,TP} + Mesh{CRS,TP} -A mesh embedded in a `Dim`-dimensional space with given -coordinate reference system `CRS` and topology of type `TP`. +A mesh with given coordinate reference system `CRS` and topology of type `TP`. """ -abstract type Mesh{Dim,CRS,TP<:Topology} <: Domain{Dim,CRS} end +abstract type Mesh{CRS,TP<:Topology} <: Domain{CRS} end """ vertex(mesh, ind) @@ -139,25 +138,25 @@ function Base.show(io::IO, ::MIME"text/plain", m::Mesh) end """ - Grid{Dim,CRS} + Grid{CRS,Dim} -A grid embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. +A grid with given coordinate reference system `CRS` embedded in a `Dim`-dimensional space. """ -const Grid{Dim,CRS} = Mesh{Dim,CRS,GridTopology{Dim}} +const Grid{CRS,Dim} = Mesh{CRS,GridTopology{Dim}} """ - SubGrid{Dim,CRS} + SubGrid{CRS,Dim} -A view of a grid in a `Dim`-dimensinoal space with given coordinate reference system `CRS`. +A view of a grid with given coordinate reference system `CRS` embedded in a `Dim`-dimensinoal space. """ -const SubGrid{Dim,CRS} = SubDomain{Dim,CRS,<:Grid{Dim,CRS}} +const SubGrid{CRS,Dim} = SubDomain{CRS,<:Grid{CRS,Dim}} """ vertex(grid, ijk) Convert Cartesian index `ijk` to vertex on `grid`. """ -vertex(g::Grid{Dim}, ijk::CartesianIndex{Dim}) where {Dim} = vertex(g, ijk.I) +vertex(g::Grid, ijk::CartesianIndex) = vertex(g, ijk.I) """ xyz(grid) @@ -183,11 +182,11 @@ Base.size(g::Grid) = size(topology(g)) vertex(g::Grid, ind::Int) = vertex(g, CartesianIndices(size(g) .+ 1)[ind]) -vertex(g::Grid{Dim}, ijk::Dims{Dim}) where {Dim} = vertex(g, LinearIndices(size(g) .+ 1)[ijk...]) +vertex(g::Grid, ijk::Dims) = vertex(g, LinearIndices(size(g) .+ 1)[ijk...]) -Base.minimum(g::Grid{Dim}) where {Dim} = vertex(g, ntuple(i -> 1, Dim)) -Base.maximum(g::Grid{Dim}) where {Dim} = vertex(g, size(g) .+ 1) -Base.extrema(g::Grid{Dim}) where {Dim} = minimum(g), maximum(g) +Base.minimum(g::Grid) = vertex(g, ntuple(i -> 1, embeddim(g))) +Base.maximum(g::Grid) = vertex(g, size(g) .+ 1) +Base.extrema(g::Grid) = minimum(g), maximum(g) function element(g::Grid, ind::Int) elem = element(topology(g), ind) @@ -200,19 +199,33 @@ end Base.eltype(g::Grid) = typeof(first(g)) -Base.getindex(g::Grid{Dim}, ijk::Vararg{Int,Dim}) where {Dim} = element(g, LinearIndices(size(g))[ijk...]) +Base.getindex(g::Grid, ind::Int) = element(g, ind) -@propagate_inbounds function Base.getindex(g::Grid{Dim}, ijk::Vararg{Union{UnitRange{Int},Colon,Int},Dim}) where {Dim} +Base.getindex(g::Grid, inds::AbstractVector) = [element(g, ind) for ind in inds] + +Base.getindex(g::Grid, ijk::Int...) = element(g, LinearIndices(size(g))[ijk...]) + +@propagate_inbounds function Base.getindex(g::Grid, ijk...) dims = size(g) - ranges = ntuple(i -> _asrange(dims[i], ijk[i]), Dim) + ranges = ntuple(i -> _asrange(dims[i], ijk[i]), embeddim(g)) getindex(g, CartesianIndices(ranges)) end +function Base.getindex(g::Grid, I::CartesianIndices) + @boundscheck _checkbounds(g, I) + dims = size(I) + odims = size(g) + cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) + inds = vec(LinearIndices(odims .+ 1)[cinds]) + periodic = isperiodic(topology(g)) .&& dims .== odims + SimpleMesh(vertices(g)[inds], GridTopology(dims, periodic)) +end + _asrange(::Int, r::UnitRange{Int}) = r _asrange(d::Int, ::Colon) = 1:d _asrange(::Int, i::Int) = i:i -function _checkbounds(g::Grid{Dim}, I::CartesianIndices{Dim}) where {Dim} +function _checkbounds(g, I) dims = size(g) ranges = I.indices if !all(first(r) ≥ 1 && last(r) ≤ d for (d, r) in zip(dims, ranges)) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index d84cac41c..c34f75c83 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -50,42 +50,34 @@ Create a 1D grid from -1 to 1 with 100 segments: julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` """ -struct CartesianGrid{Dim,C<:CRS,ℒ<:Len} <: Grid{Dim,C} - origin::Point{Dim,C} +struct CartesianGrid{C<:CRS,Dim,ℒ<:Len} <: Grid{C,Dim} + origin::Point{C} spacing::NTuple{Dim,ℒ} offset::Dims{Dim} topology::GridTopology{Dim} function CartesianGrid( - origin::Point{Dim,C}, + origin::Point{C}, spacing::NTuple{Dim,ℒ}, offset::Dims{Dim}, topology::GridTopology{Dim} - ) where {Dim,C<:CRS,ℒ<:Len} + ) where {C<:CRS,Dim,ℒ<:Len} if !all(>(zero(ℒ)), spacing) throw(ArgumentError("spacing must be positive")) end - new{Dim,C,float(ℒ)}(origin, spacing, offset, topology) + new{C,Dim,float(ℒ)}(origin, spacing, offset, topology) end end -CartesianGrid( - origin::Point{Dim}, - spacing::NTuple{Dim,Len}, - offset::Dims{Dim}, - topology::GridTopology{Dim} -) where {Dim} = CartesianGrid(origin, promote(spacing...), offset, topology) +CartesianGrid(origin::Point, spacing::NTuple{Dim,Len}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = + CartesianGrid(origin, promote(spacing...), offset, topology) -CartesianGrid( - origin::Point{Dim}, - spacing::NTuple{Dim,Number}, - offset::Dims{Dim}, - topology::GridTopology{Dim} -) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) +CartesianGrid(origin::Point, spacing::NTuple{Dim,Number}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = + CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) function CartesianGrid( dims::Dims{Dim}, - origin::Point{Dim}, + origin::Point, spacing::NTuple{Dim,Number}, offset::Dims{Dim}=ntuple(i -> 1, Dim) ) where {Dim} @@ -102,26 +94,26 @@ CartesianGrid( offset::Dims{Dim}=ntuple(i -> 1, Dim) ) where {Dim} = CartesianGrid(dims, Point(origin), spacing, offset) -function CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} +function CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} dims = Tuple(ceil.(Int, (finish - start) ./ spacing)) origin = start offset = ntuple(i -> 1, Dim) CartesianGrid(dims, origin, spacing, offset) end -CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,Len}) where {Dim} = +CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,Len}) where {Dim} = CartesianGrid(start, finish, promote(spacing...)) -CartesianGrid(start::Point{Dim}, finish::Point{Dim}, spacing::NTuple{Dim,Number}) where {Dim} = +CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,Number}) where {Dim} = CartesianGrid(start, finish, addunit.(spacing, u"m")) CartesianGrid(start::NTuple{Dim,Number}, finish::NTuple{Dim,Number}, spacing::NTuple{Dim,Number}) where {Dim} = CartesianGrid(Point(start), Point(finish), spacing) -function CartesianGrid(start::Point{Dim}, finish::Point{Dim}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} +function CartesianGrid(start::Point, finish::Point; dims::Dims=ntuple(i -> 100, embeddim(start))) origin = start spacing = Tuple((finish - start) ./ dims) - offset = ntuple(i -> 1, Dim) + offset = ntuple(i -> 1, length(dims)) CartesianGrid(dims, origin, spacing, offset) end @@ -144,13 +136,13 @@ spacing(g::CartesianGrid) = g.spacing offset(g::CartesianGrid) = g.offset -vertex(g::CartesianGrid{Dim}, ijk::Dims{Dim}) where {Dim} = g.origin + Vec((ijk .- g.offset) .* g.spacing) +vertex(g::CartesianGrid, ijk::Dims) = g.origin + Vec((ijk .- g.offset) .* g.spacing) -function xyz(g::CartesianGrid{Dim}) where {Dim} +function xyz(g::CartesianGrid) dims = size(g) spac = spacing(g) orig = to(minimum(g)) - ntuple(Dim) do i + ntuple(embeddim(g)) do i o, s, d = orig[i], spac[i], dims[i] range(start=o, step=s, length=(d + 1)) end @@ -163,7 +155,7 @@ function centroid(g::CartesianGrid, ind::Int) vertex(g, ijk) + Vec(spacing(g) ./ 2) end -function Base.getindex(g::CartesianGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} +function Base.getindex(g::CartesianGrid, I::CartesianIndices) @boundscheck _checkbounds(g, I) dims = size(I) offset = g.offset .- Tuple(first(I)) .+ 1 diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 40e03c629..87f93e478 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -20,7 +20,7 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} +struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Cartesian{Datum,Dim,ℒ},Dim} xyz::NTuple{Dim,V} topology::GridTopology{Dim} @@ -47,7 +47,7 @@ RectilinearGrid{Datum}(xyz...) where {Datum} = RectilinearGrid{Datum}(xyz) RectilinearGrid(args...) = RectilinearGrid{NoDatum}(args...) -vertex(g::RectilinearGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk))) +vertex(g::RectilinearGrid{Datum}, ijk::Dims) where {Datum} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk))) xyz(g::RectilinearGrid) = g.xyz @@ -60,12 +60,12 @@ function centroid(g::RectilinearGrid, ind::Int) withcrs(g, (to(p1) + to(p2)) / 2) end -function Base.getindex(g::RectilinearGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} +function Base.getindex(g::RectilinearGrid{Datum}, I::CartesianIndices) where {Datum} @boundscheck _checkbounds(g, I) dims = size(I) start = Tuple(first(I)) stop = Tuple(last(I)) .+ 1 - xyz = ntuple(i -> g.xyz[i][start[i]:stop[i]], Dim) + xyz = ntuple(i -> g.xyz[i][start[i]:stop[i]], embeddim(g)) RectilinearGrid{Datum}(xyz, GridTopology(dims)) end diff --git a/src/mesh/simplemesh.jl b/src/mesh/simplemesh.jl index 94a2a859b..37c39a65e 100644 --- a/src/mesh/simplemesh.jl +++ b/src/mesh/simplemesh.jl @@ -31,7 +31,7 @@ See also [`Topology`](@ref), [`GridTopology`](@ref), of the mesh to a [`HalfEdgeTopology`](@ref) instead of a [`SimpleTopology`](@ref). """ -struct SimpleMesh{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}},TP<:Topology} <: Mesh{Dim,C,TP} +struct SimpleMesh{C<:CRS,V<:AbstractVector{Point{C}},TP<:Topology} <: Mesh{C,TP} vertices::V topology::TP end @@ -48,13 +48,3 @@ vertex(m::SimpleMesh, ind::Int) = m.vertices[ind] vertices(m::SimpleMesh) = m.vertices nvertices(m::SimpleMesh) = length(m.vertices) - -function Base.getindex(m::SimpleMesh{Dim,<:Any,<:Any,GridTopology{Dim}}, I::CartesianIndices{Dim}) where {Dim} - @boundscheck _checkbounds(m, I) - dims = size(I) - odims = size(m) - cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) - inds = vec(LinearIndices(odims .+ 1)[cinds]) - periodic = isperiodic(topology(m)) .&& dims .== odims - SimpleMesh(m.vertices[inds], GridTopology(dims, periodic)) -end diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index 21f88aad7..b8a512c3c 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -20,7 +20,7 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{Dim,Cartesian{Datum,Dim,ℒ}} +struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{Cartesian{Datum,Dim,ℒ},Dim} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} @@ -44,16 +44,16 @@ StructuredGrid{Datum}(XYZ...) where {Datum} = StructuredGrid{Datum}(XYZ) StructuredGrid(args...) = StructuredGrid{NoDatum}(args...) -vertex(g::StructuredGrid{Datum,Dim}, ijk::Dims{Dim}) where {Datum,Dim} = - Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], Dim))) +vertex(g::StructuredGrid{Datum}, ijk::Dims) where {Datum} = + Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], embeddim(g)))) XYZ(g::StructuredGrid) = g.XYZ -function Base.getindex(g::StructuredGrid{Datum,Dim}, I::CartesianIndices{Dim}) where {Datum,Dim} +function Base.getindex(g::StructuredGrid{Datum}, I::CartesianIndices) where {Datum} @boundscheck _checkbounds(g, I) dims = size(I) cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) - XYZ = ntuple(i -> g.XYZ[i][cinds], Dim) + XYZ = ntuple(i -> g.XYZ[i][cinds], embeddim(g)) StructuredGrid{Datum}(XYZ, GridTopology(dims)) end diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 1c7d735f4..475ec2f88 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,7 +7,7 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{Dim,C<:CRS,TP<:Topology,M<:Mesh{Dim,C,TP},TR<:Transform} <: Mesh{Dim,C,TP} +struct TransformedMesh{C<:CRS,TP<:Topology,M<:Mesh{C,TP},TR<:Transform} <: Mesh{C,TP} mesh::M transform::TR end @@ -24,11 +24,11 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{Dim,CRS,G<:Grid{Dim,CRS},TR} = TransformedMesh{Dim,CRS,GridTopology{Dim},G,TR} +const TransformedGrid{CRS,Dim,G<:Grid{CRS,Dim},TR} = TransformedMesh{CRS,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) -@propagate_inbounds Base.getindex(g::TransformedGrid{Dim}, I::CartesianIndices{Dim}) where {Dim} = +@propagate_inbounds Base.getindex(g::TransformedGrid{CRS,Dim}, I::CartesianIndices{Dim}) where {CRS,Dim} = TransformedGrid(getindex(g.mesh, I), g.transform) function Base.summary(io::IO, g::TransformedGrid) diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 498812ae5..b4b6e7c23 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -15,7 +15,7 @@ multiple polygons as a single entity (e.g. country with islands). - Type aliases are [`MultiPoint`](@ref), [`MultiSegment`](@ref), [`MultiRope`](@ref), [`MultiRing`](@ref), [`MultiPolygon`](@ref). """ -struct Multi{Dim,C<:CRS,G<:Geometry{Dim,C}} <: Geometry{Dim,C} +struct Multi{C<:CRS,G<:Geometry{C}} <: Geometry{C} geoms::Vector{G} end @@ -23,12 +23,12 @@ end Multi(geoms) = Multi(collect(geoms)) # type aliases for convenience -const MultiPoint{Dim,CRS} = Multi{Dim,CRS,<:Point{Dim,CRS}} -const MultiSegment{Dim,CRS} = Multi{Dim,CRS,<:Segment{Dim,CRS}} -const MultiRope{Dim,CRS} = Multi{Dim,CRS,<:Rope{Dim,CRS}} -const MultiRing{Dim,CRS} = Multi{Dim,CRS,<:Ring{Dim,CRS}} -const MultiPolygon{Dim,CRS} = Multi{Dim,CRS,<:Polygon{Dim,CRS}} -const MultiPolyhedron{Dim,CRS} = Multi{Dim,CRS,<:Polyhedron{Dim,CRS}} +const MultiPoint{CRS} = Multi{CRS,<:Point{CRS}} +const MultiSegment{CRS} = Multi{CRS,<:Segment{CRS}} +const MultiRope{CRS} = Multi{CRS,<:Rope{CRS}} +const MultiRing{CRS} = Multi{CRS,<:Ring{CRS}} +const MultiPolygon{CRS} = Multi{CRS,<:Polygon{CRS}} +const MultiPolyhedron{CRS} = Multi{CRS,<:Polyhedron{CRS}} paramdim(m::Multi) = maximum(paramdim, m.geoms) diff --git a/src/orientation.jl b/src/orientation.jl index ba4aa6dd1..fa959d61d 100644 --- a/src/orientation.jl +++ b/src/orientation.jl @@ -42,7 +42,9 @@ function orientation(p::Polygon, method) hasholes(p) ? o : first(o) end -orientation(r::Ring{3}, method) = orientation(proj2D(r), method) +orientation(r::Ring, method) = _orientation(r, Val(embeddim(r)), method) + +_orientation(r::Ring, ::Val{3}, method) = orientation(proj2D(r), method) """ WindingOrientation() @@ -58,7 +60,7 @@ based on the winding number. """ struct WindingOrientation <: OrientationMethod end -function orientation(r::Ring{2}, ::WindingOrientation) +function _orientation(r::Ring, ::Val{2}, ::WindingOrientation) # pick any segment p₁, p₂ = vertices(r)[1:2] p̄ = center(Segment(p₁, p₂)) @@ -80,7 +82,7 @@ based on signed triangular areas. """ struct TriangleOrientation <: OrientationMethod end -function orientation(r::Ring{2}, ::TriangleOrientation) +function _orientation(r::Ring, ::Val{2}, ::TriangleOrientation) v = vertices(r) Δ(i) = signarea(v[1], v[i], v[i + 1]) a = mapreduce(Δ, +, 2:(length(v) - 1)) diff --git a/src/partitioning/bisectpoint.jl b/src/partitioning/bisectpoint.jl index 676502820..356c16ce6 100644 --- a/src/partitioning/bisectpoint.jl +++ b/src/partitioning/bisectpoint.jl @@ -8,14 +8,13 @@ A method for partitioning spatial objects into two half spaces defined by a `normal` direction and a reference `point`. """ -struct BisectPointPartition{Dim,V<:Vec{Dim},P<:Point{Dim}} <: PartitionMethod +struct BisectPointPartition{V<:Vec,P<:Point} <: PartitionMethod normal::V point::P - BisectPointPartition{Dim,V,P}(normal, point) where {Dim,V<:Vec{Dim},P<:Point{Dim}} = new(unormalize(normal), point) + BisectPointPartition{V,P}(normal, point) where {V<:Vec,P<:Point} = new(unormalize(normal), point) end -BisectPointPartition(normal::V, point::P) where {Dim,V<:Vec{Dim},P<:Point{Dim}} = - BisectPointPartition{Dim,V,P}(normal, point) +BisectPointPartition(normal::V, point::P) where {V<:Vec,P<:Point} = BisectPointPartition{V,P}(normal, point) BisectPointPartition(normal::NTuple{Dim}, point::NTuple{Dim}) where {Dim} = BisectPointPartition(Vec(normal), Point(point)) diff --git a/src/polytopes.jl b/src/polytopes.jl index 1f0c79fda..d9129dca3 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -3,14 +3,15 @@ # ------------------------------------------------------------------ """ - Polytope{K,Dim,CRS} + Polytope{K,CRS} We say that a geometry is a K-polytope when it is a collection of "flat" sides that constitute a `K`-dimensional subspace. They are called chain, polygon and -polyhedron respectively for 1D (`K=1`), 2D (`K=2`) and 3D (`K=3`) subspaces, -embedded in a `Dim`-dimensional space with given coordinate reference system `CRS`. +polyhedron respectively for 1D (`K=1`), 2D (`K=2`) and 3D (`K=3`) subspaces. The parameter `K` is also known as the rank or parametric dimension -of the polytope: . +of the polytope (). +The vertices are stored with coordinates in a given coordinate +reference system `CRS`. The term polytope expresses a particular combinatorial structure. A polyhedron, for example, can be decomposed into faces. Each face can then be decomposed into @@ -26,13 +27,13 @@ have (K-1)-polytopes in common. See . - Type aliases are `Chain`, `Polygon`, `Polyhedron`. """ -abstract type Polytope{K,Dim,CRS} <: Geometry{Dim,CRS} end +abstract type Polytope{K,CRS} <: Geometry{CRS} end # heper macro to define polytopes macro polytope(type, K, N) expr = quote - $Base.@__doc__ struct $type{Dim,C<:CRS} <: Polytope{$K,Dim,C} - vertices::NTuple{$N,Point{Dim,C}} + $Base.@__doc__ struct $type{C<:CRS} <: Polytope{$K,C} + vertices::NTuple{$N,Point{C}} end $type(vertices::Vararg{Tuple,$N}) = $type(Point.(vertices)) @@ -46,7 +47,7 @@ end # ------------------- """ - Chain{Dim,CRS} + Chain{CRS} A chain is a 1-polytope, i.e. a polytope with parametric dimension 1. See . @@ -153,7 +154,7 @@ include("polytopes/ring.jl") # --------------------- """ - Polygon{Dim,CRS} + Polygon{CRS} A polygon is a 2-polytope, i.e. a polytope with parametric dimension 2. @@ -192,7 +193,7 @@ include("polytopes/polyarea.jl") # ------------------------ """ - Polyhedron{Dim,CRS} + Polyhedron{CRS} A polyhedron is a 3-polytope, i.e. a polytope with parametric dimension 3. diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 61eaf770b..03e66a367 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -20,9 +20,9 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. - Type aliases are `Triangle`, `Quadrangle`, `Pentagon`, `Hexagon`, `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ -struct Ngon{N,Dim,C<:CRS} <: Polygon{Dim,C} - vertices::NTuple{N,Point{Dim,C}} - function Ngon{N,Dim,C}(vertices) where {N,Dim,C<:CRS} +struct Ngon{N,C<:CRS} <: Polygon{C} + vertices::NTuple{N,Point{C}} + function Ngon{N,C}(vertices) where {N,C<:CRS} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) end @@ -30,11 +30,11 @@ struct Ngon{N,Dim,C<:CRS} <: Polygon{Dim,C} end end -Ngon{N}(vertices::NTuple{N,Point{Dim,C}}) where {N,Dim,C<:CRS} = Ngon{N,Dim,C}(vertices) +Ngon{N}(vertices::NTuple{N,Point{C}}) where {N,C<:CRS} = Ngon{N,C}(vertices) Ngon{N}(vertices::Vararg{P,N}) where {N,P<:Point} = Ngon{N}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,Point{Dim,C}}) where {N,Dim,C<:CRS} = Ngon{N,Dim,C}(vertices) +Ngon(vertices::NTuple{N,Point{C}}) where {N,C<:CRS} = Ngon{N,C}(vertices) Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) @@ -69,14 +69,14 @@ signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) # TRIANGLES # ---------- -function signarea(t::Triangle{2}) +function signarea(t::Triangle) + checkdim(t, 2) v = t.vertices signarea(v[1], v[2], v[3]) end -signarea(::Triangle{3}) = error("signed area only defined for triangles embedded in R², use `area` instead") - -function normal(t::Triangle{3}) +function normal(t::Triangle) + checkdim(t, 3) A, B, C = t.vertices unormalize(ucross((B - A), (C - A))) end diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index e23b04ad6..10dbe9b79 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -24,10 +24,10 @@ in the real world, including issues with: * `degeneracy` - Sometimes data is shared with degenerate rings (e.g. only 2 vertices). """ -struct PolyArea{Dim,C<:CRS,R<:Ring{Dim,C}} <: Polygon{Dim,C} +struct PolyArea{C<:CRS,R<:Ring{C}} <: Polygon{C} rings::Vector{R} - function PolyArea{Dim,C,R}(rings; fix=true) where {Dim,C<:CRS,R<:Ring{Dim,C}} + function PolyArea{C,R}(rings; fix=true) where {C<:CRS,R<:Ring{C}} if isempty(rings) throw(ArgumentError("cannot create PolyArea without rings")) end @@ -57,7 +57,7 @@ struct PolyArea{Dim,C<:CRS,R<:Ring{Dim,C}} <: Polygon{Dim,C} end end -PolyArea(rings::AbstractVector{R}; fix=true) where {Dim,C<:CRS,R<:Ring{Dim,C}} = PolyArea{Dim,C,R}(rings; fix) +PolyArea(rings::AbstractVector{R}; fix=true) where {C<:CRS,R<:Ring{C}} = PolyArea{C,R}(rings; fix) PolyArea(vertices::AbstractVector{<:AbstractVector}; fix=true) = PolyArea([Ring(v) for v in vertices]; fix) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 5bba6f611..3d9d9de95 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -9,10 +9,10 @@ A closed polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Rope`](@ref). """ -struct Ring{Dim,C<:CRS,V<:CircularVector{Point{Dim,C}}} <: Chain{Dim,C} +struct Ring{C<:CRS,V<:CircularVector{Point{C}}} <: Chain{C} vertices::V - function Ring{Dim,C,V}(vertices) where {Dim,C<:CRS,V<:CircularVector{Point{Dim,C}}} + function Ring{C,V}(vertices) where {C<:CRS,V<:CircularVector{Point{C}}} if first(vertices) == last(vertices) && length(vertices) ≥ 2 throw(ArgumentError(""" First and last vertices of `Ring` constructor must be different @@ -24,7 +24,7 @@ struct Ring{Dim,C<:CRS,V<:CircularVector{Point{Dim,C}}} <: Chain{Dim,C} end end -Ring(vertices::CircularVector{Point{Dim,C}}) where {Dim,C<:CRS} = Ring{Dim,C,typeof(vertices)}(vertices) +Ring(vertices::CircularVector{Point{C}}) where {C<:CRS} = Ring{C,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) @@ -65,10 +65,8 @@ Return inner angles of the `ring`. Inner angles are always positive, and unlike `angles` they can be greater than `π`. """ -function innerangles(r::Ring{2}) +function innerangles(r::Ring) # correct sign of angles in case orientation is CW θs = orientation(r) == CW ? -angles(r) : angles(r) [θ > 0 ? 2 * oftype(θ, π) - θ : -θ for θ in θs] end - -innerangles(r::Ring{3}) = innerangles(Ring(proj2D(vertices(r)))) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index 73392a4ca..6f53089e4 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -9,7 +9,7 @@ An open polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Ring`](@ref). """ -struct Rope{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}}} <: Chain{Dim,C} +struct Rope{C<:CRS,V<:AbstractVector{Point{C}}} <: Chain{C} vertices::V end diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 071f3507d..71bb4efa2 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -11,7 +11,7 @@ Base.in(p::Point, g::Geometry) = sideof(p, boundary(g)) == IN Base.in(p₁::Point, p₂::Point) = p₁ == p₂ -function Base.in(p::Point{Dim}, s::Segment{Dim}) where {Dim} +function Base.in(p::Point, s::Segment) # given collinear points (a, b, p), the point p intersects # segment ab if and only if vectors satisfy 0 ≤ ap ⋅ ab ≤ ||ab||² a, b = vertices(s) @@ -34,14 +34,14 @@ Base.in(p::Point, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) Base.in(p::Point, b::Box) = minimum(b) ⪯ p ⪯ maximum(b) -function Base.in(p::Point{Dim}, b::Ball{Dim}) where {Dim} +function Base.in(p::Point, b::Ball) c = center(b) r = radius(b) s = norm(p - c) s < r || isapproxequal(s, r) end -function Base.in(p::Point{Dim}, s::Sphere{Dim}) where {Dim} +function Base.in(p::Point, s::Sphere) c = center(s) r = radius(s) s = norm(p - c) diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 5d61135b8..8e0fdf3b6 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -65,7 +65,8 @@ intersects(c::Chain, g::Geometry) = any(∈(g), vertices(c)) || intersects(c, bo intersects(g::Geometry, c::Chain) = intersects(c, g) -function intersects(g₁::Geometry{Dim}, g₂::Geometry{Dim}) where {Dim} +function intersects(g₁::Geometry, g₂::Geometry) + Dim = embeddim(g₁) ℒ = lentype(g₁) # must have intersection of bounding boxes @@ -121,9 +122,9 @@ make room for the next point. A complete simplex must have `Dim + 1` points. See also [`intersects`](@ref). """ -function gjk! end +gjk!(O::Point, points) = _gjk!(Val(embeddim(O)), O, points) -function gjk!(O::Point{2}, points) +function _gjk!(::Val{2}, O, points) # line segment case if length(points) == 2 B, A = points @@ -151,7 +152,7 @@ function gjk!(O::Point{2}, points) d end -function gjk!(O::Point{3}, points) +function _gjk!(::Val{3}, O, points) # line segment case if length(points) == 2 B, A = points diff --git a/src/predicates/iscollinear.jl b/src/predicates/iscollinear.jl index 210522b4f..3b5a61bb2 100644 --- a/src/predicates/iscollinear.jl +++ b/src/predicates/iscollinear.jl @@ -7,10 +7,11 @@ Tells whether or not the points `A`, `B` and `C` are collinear. """ -function iscollinear(A::Point{Dim}, B::Point{Dim}, C::Point{Dim}) where {Dim} +function iscollinear(A::Point, B::Point, C::Point) # points A, B, C are collinear if and only if the # cross-products for segments AB and AC with respect # to all possible pairs of coordinates are zero + Dim = embeddim(A) AB, AC = B - A, C - A result = true for i in 1:Dim, j in (i + 1):Dim diff --git a/src/predicates/isconvex.jl b/src/predicates/isconvex.jl index 75f14eb5a..c551f65ff 100644 --- a/src/predicates/isconvex.jl +++ b/src/predicates/isconvex.jl @@ -58,7 +58,11 @@ isconvex(::Triangle) = true isconvex(::Tetrahedron) = true -isconvex(p::Polygon{2}) = Set(vertices(convexhull(p))) == Set(vertices(p)) +isconvex(p::Polygon) = _isconvex(p, Val(embeddim(p))) + +_isconvex(p::Polygon, ::Val{2}) = Set(vertices(convexhull(p))) == Set(vertices(p)) + +_isconvex(p::Polygon, ::Val{3}) = isconvex(proj2D(p)) isconvex(m::Multi) = isapproxequal(measure(convexhull(m)), measure(m)) @@ -66,12 +70,10 @@ isconvex(m::Multi) = isapproxequal(measure(convexhull(m)), measure(m)) # OPTIMIZATIONS # -------------- -function isconvex(q::Quadrangle{2}) +# TODO: check dim: 2 +function isconvex(q::Quadrangle) v = vertices(q) d1 = Segment(v[1], v[3]) d2 = Segment(v[2], v[4]) intersects(d1, d2) end - -# temporary workaround in 3D -isconvex(p::Polygon{3}) = isconvex(proj2D(p)) diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index 793a7b3c9..a29c1506f 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -20,11 +20,11 @@ isperiodic(b::BezierCurve) = (first(controls(b)) == last(controls(b)),) isperiodic(::Type{<:Plane}) = (false, false) -isperiodic(::Type{<:Box{Dim}}) where {Dim} = ntuple(i -> false, Dim) +isperiodic(B::Type{<:Box}) = ntuple(i -> false, embeddim(B)) -isperiodic(::Type{<:Ball{Dim}}) where {Dim} = ntuple(i -> i != 1, Dim) +isperiodic(B::Type{<:Ball}) = ntuple(i -> i != 1, embeddim(B)) -isperiodic(::Type{<:Sphere{Dim}}) where {Dim} = ntuple(i -> true, Dim - 1) +isperiodic(S::Type{<:Sphere}) = ntuple(i -> true, embeddim(S) - 1) isperiodic(::Type{<:Ellipsoid}) = (true, true) diff --git a/src/primitives.jl b/src/primitives.jl index 2956641ec..8b7b8b09a 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -3,14 +3,14 @@ # ------------------------------------------------------------------ """ - Primitive{Dim,CRS} + Primitive{CRS} We say that a geometry is a primitive when it can be expressed as a single entity with no parts (a.k.a. atomic). For example, a sphere is a primitive described in terms of a mathematical expression involving a metric and a radius. See . """ -abstract type Primitive{Dim,CRS} <: Geometry{Dim,CRS} end +abstract type Primitive{CRS} <: Geometry{CRS} end function Base.show(io::IO, geom::Primitive) name = prettyname(geom) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index b4dff1a0a..67bfc9ab0 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -9,10 +9,10 @@ A ball with `center` and `radius`. See also [`Sphere`](@ref). """ -struct Ball{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} - center::Point{Dim,C} +struct Ball{C<:CRS,ℒ<:Len} <: Primitive{C} + center::Point{C} radius::ℒ - Ball(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = new{Dim,C,float(ℒ)}(center, radius) + Ball(center::Point{C}, radius::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(center, radius) end Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) @@ -23,7 +23,7 @@ Ball(center::Point) = Ball(center, oneunit(lentype(center))) Ball(center::Tuple) = Ball(Point(center)) -paramdim(::Type{<:Ball{Dim}}) where {Dim} = Dim +paramdim(B::Type{<:Ball}) = embeddim(B) center(b::Ball) = b.center @@ -34,7 +34,9 @@ radius(b::Ball) = b.radius Base.isapprox(b₁::Ball, b₂::Ball; atol=atol(lentype(b₁)), kwargs...) = isapprox(b₁.center, b₂.center; atol, kwargs...) && isapprox(b₁.radius, b₂.radius; atol, kwargs...) -function (b::Ball{2})(ρ, φ) +(b::Ball)(args...) = _ball(Val(embeddim(b)), b, args...) + +function _ball(::Val{2}, b, ρ, φ) T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, φ), "b(ρ, φ) is not defined for ρ, φ outside [0, 1]².")) @@ -48,7 +50,7 @@ function (b::Ball{2})(ρ, φ) c + Vec(x, y) end -function (b::Ball{3})(ρ, θ, φ) +function _ball(::Val{3}, b, ρ, θ, φ) T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, θ, φ), "b(ρ, θ, φ) is not defined for ρ, θ, φ outside [0, 1]³.")) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 6bf847747..3cc5506dd 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -17,10 +17,10 @@ large number of points but less precise, can be used via ## Examples ```julia -BezierCurve(Point2[(0.,0.),(1.,-1.)]) +BezierCurve([(0.,0.),(1.,-1.)]) ``` """ -struct BezierCurve{Dim,C<:CRS,V<:AbstractVector{Point{Dim,C}}} <: Primitive{Dim,C} +struct BezierCurve{C<:CRS,V<:AbstractVector{Point{C}}} <: Primitive{C} controls::V end diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 8758aab1b..fef807585 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -15,21 +15,21 @@ Box(Point(0, 0, 0), Point(1, 1, 1)) Box((0, 0), (1, 1)) ``` """ -struct Box{Dim,C<:CRS} <: Primitive{Dim,C} - min::Point{Dim,C} - max::Point{Dim,C} +struct Box{C<:CRS} <: Primitive{C} + min::Point{C} + max::Point{C} - function Box{Dim,C}(min, max) where {Dim,C<:CRS} + function Box{C}(min, max) where {C<:CRS} assertion(min ⪯ max, "`min` must be less than or equal to `max`") new(min, max) end end -Box(min::Point{Dim,C}, max::Point{Dim,C}) where {Dim,C<:CRS} = Box{Dim,C}(min, max) +Box(min::Point{C}, max::Point{C}) where {C<:CRS} = Box{C}(min, max) Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) -paramdim(::Type{<:Box{Dim}}) where {Dim} = Dim +paramdim(B::Type{<:Box}) = embeddim(B) Base.minimum(b::Box) = b.min diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index 331980cde..be14b5517 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -10,7 +10,7 @@ given `plane` with given `radius`. See also [`Disk`](@ref). """ -struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} +struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} plane::P radius::ℒ Circle(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 7d525cf40..cf30cefee 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -10,9 +10,9 @@ See . See also [`ConeSurface`](@ref). """ -struct Cone{C<:CRS,D<:Disk{C}} <: Primitive{3,C} +struct Cone{C<:CRS,D<:Disk{C}} <: Primitive{C} base::D - apex::Point{3,C} + apex::Point{C} end function Cone(base::Disk{C}, apex::Tuple) where {C<:Cartesian} diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 09a25a85f..7720bf640 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -10,9 +10,9 @@ See . See also [`Cone`](@ref). """ -struct ConeSurface{C<:CRS,D<:Disk{C}} <: Primitive{3,C} +struct ConeSurface{C<:CRS,D<:Disk{C}} <: Primitive{C} base::D - apex::Point{3,C} + apex::Point{C} end function ConeSurface(base::Disk{C}, apex::Tuple) where {C<:Cartesian} diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index 97b4b83f4..56587cdfe 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -24,7 +24,7 @@ Finally, construct a right vertical circular cylinder with given `radius`. See . """ -struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} +struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} bot::P top::P radius::ℒ diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 0aded0337..72f5d9367 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -24,7 +24,7 @@ Finally, construct a right vertical circular cylinder surface with given `radius See . """ -struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} +struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} bot::P top::P radius::ℒ diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index 8aa29c86c..cd6ab9bd9 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -10,7 +10,7 @@ given `plane` with given `radius`. See also [`Circle`](@ref). """ -struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{3,C} +struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} plane::P radius::ℒ Disk(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 1b7160496..a5da18e05 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -7,11 +7,11 @@ A 3D ellipsoid with given `radii`, `center` and `rotation`. """ -struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{3,C} +struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{C} radii::NTuple{3,ℒ} - center::Point{3,C} + center::Point{C} rotation::R - Ellipsoid(radii::NTuple{3,ℒ}, center::Point{3,C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = + Ellipsoid(radii::NTuple{3,ℒ}, center::Point{C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = new{float(ℒ),C,R}(radii, center, rotation) end diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index df88c49d3..fee5523f4 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -10,7 +10,7 @@ See . See also [`FrustumSurface`](@ref). """ -struct Frustum{C<:CRS,D<:Disk{C}} <: Primitive{3,C} +struct Frustum{C<:CRS,D<:Disk{C}} <: Primitive{C} bot::D top::D diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index ae59093a6..9bf6eb4b7 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -10,7 +10,7 @@ See . See also [`Frustum`](@ref). """ -struct FrustumSurface{C<:CRS,D<:Disk{C}} <: Primitive{3,C} +struct FrustumSurface{C<:CRS,D<:Disk{C}} <: Primitive{C} bot::D top::D diff --git a/src/primitives/line.jl b/src/primitives/line.jl index 0b3aad266..ae939f652 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -9,9 +9,9 @@ A line passing through points `a` and `b`. See also [`Segment`](@ref). """ -struct Line{Dim,C<:CRS} <: Primitive{Dim,C} - a::Point{Dim,C} - b::Point{Dim,C} +struct Line{C<:CRS} <: Primitive{C} + a::Point{C} + b::Point{C} end Line(a::Tuple, b::Tuple) = Line(Point(a), Point(b)) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 142be918b..aee3fd528 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -32,11 +32,11 @@ Same as above, but here the apex is at `Apex(0, 0, 0)`. See also . """ -struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{3,C} - apex::Point{3,C} +struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{C} + apex::Point{C} radius::ℒ focallength::ℒ - ParaboloidSurface(apex::Point{3,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = + ParaboloidSurface(apex::Point{C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(apex, radius, focallength) end diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index 5adef2f20..b724ae570 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -13,10 +13,10 @@ defined by non-parallel vectors `u` and `v`. Alternatively specify point `p` and a given normal vector `n` to the plane. """ -struct Plane{C<:CRS,ℒ<:Len} <: Primitive{3,C} - p::Point{3,C} - u::Vec{3,ℒ} - v::Vec{3,ℒ} +struct Plane{C<:CRS,V<:Vec{3}} <: Primitive{C} + p::Point{C} + u::V + v::V end function Plane(p::Point, n::Vec) diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 92c005983..186f98c01 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -33,17 +33,16 @@ Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design algorithms assume a continuous space. The conversion to float avoids `InexactError` and other unexpected results. """ -struct Point{Dim,C<:CRS} <: Primitive{Dim,C} +struct Point{C<:CRS} <: Primitive{C} coords::C - Point(coords::C) where {C<:CRS} = new{CoordRefSystems.ndims(coords),C}(coords) end # convenience constructor Point(coords...) = Point(Cartesian(coords...)) # conversions -Base.convert(::Type{Point{Dim,CRSₜ}}, p::Point{Dim,CRSₛ}) where {Dim,CRSₜ,CRSₛ} = Point(convert(CRSₜ, p.coords)) -Base.convert(::Type{Point{Dim,CRS}}, p::Point{Dim,CRS}) where {Dim,CRS} = p +Base.convert(::Type{Point{CRSₜ}}, p::Point{CRSₛ}) where {CRSₜ,CRSₛ} = Point(convert(CRSₜ, p.coords)) +Base.convert(::Type{Point{CRS}}, p::Point{CRS}) where {CRS} = p paramdim(::Type{<:Point}) = 0 @@ -73,7 +72,7 @@ to(A::Point) = Vec(CoordRefSystems.values(convert(Cartesian, A.coords))) Return the [`Vec`](@ref) associated with the direction from point `B` to point `A`. """ --(A::Point{Dim}, B::Point{Dim}) where {Dim} = to(A) - to(B) +-(A::Point, B::Point) = to(A) - to(B) """ +(A::Point, v::Vec) @@ -82,8 +81,8 @@ from point `B` to point `A`. Return the point at the end of the vector `v` placed at a reference (or start) point `A`. """ -+(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withcrs(A, to(A) + v) -+(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A + v ++(A::Point, v::Vec) = withcrs(A, to(A) + v) ++(v::Vec, A::Point) = A + v """ -(A::Point, v::Vec) @@ -92,8 +91,8 @@ at a reference (or start) point `A`. Return the point at the end of the vector `-v` placed at a reference (or start) point `A`. """ --(A::Point{Dim}, v::Vec{Dim}) where {Dim} = withcrs(A, to(A) - v) --(v::Vec{Dim}, A::Point{Dim}) where {Dim} = A - v +-(A::Point, v::Vec) = withcrs(A, to(A) - v) +-(v::Vec, A::Point) = A - v """ ⪯(A::Point, B::Point) @@ -103,10 +102,10 @@ at a reference (or start) point `A`. Generalized inequality for non-negative orthant Rⁿ₊. """ -⪯(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x ≥ zero(x), B - A) -⪰(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x ≥ zero(x), A - B) -≺(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x > zero(x), B - A) -≻(A::Point{Dim}, B::Point{Dim}) where {Dim} = all(x -> x > zero(x), A - B) +⪯(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) +⪰(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) +≺(A::Point, B::Point) = all(x -> x > zero(x), B - A) +≻(A::Point, B::Point) = all(x -> x > zero(x), A - B) """ ∠(A, B, C) diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index db2697f74..0b11322e5 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -9,9 +9,9 @@ A ray originating at point `p`, pointed in direction `v`. It can be called as `r(t)` with `t > 0` to cast it at `p + t * v`. """ -struct Ray{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} - p::Point{Dim,C} - v::Vec{Dim,ℒ} +struct Ray{C<:CRS,V<:Vec} <: Primitive{C} + p::Point{C} + v::V end Ray(p::Tuple, v::Tuple) = Ray(Point(p), Vec(v)) diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index 3c0cebe41..f19b8f9ee 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -9,10 +9,10 @@ A sphere with `center` and `radius`. See also [`Ball`](@ref). """ -struct Sphere{Dim,C<:CRS,ℒ<:Len} <: Primitive{Dim,C} - center::Point{Dim,C} +struct Sphere{C<:CRS,ℒ<:Len} <: Primitive{C} + center::Point{C} radius::ℒ - Sphere(center::Point{Dim,C}, radius::ℒ) where {Dim,C<:CRS,ℒ<:Len} = new{Dim,C,float(ℒ)}(center, radius) + Sphere(center::Point{C}, radius::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(center, radius) end Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) @@ -60,7 +60,7 @@ end Sphere(p1::Tuple, p2::Tuple, p3::Tuple, p4::Tuple) = Sphere(Point(p1), Point(p2), Point(p3), Point(p4)) -paramdim(::Type{<:Sphere{Dim}}) where {Dim} = Dim - 1 +paramdim(S::Type{<:Sphere}) = embeddim(S) - 1 center(s::Sphere) = s.center @@ -71,7 +71,9 @@ radius(s::Sphere) = s.radius Base.isapprox(s₁::Sphere, s₂::Sphere; atol=atol(lentype(s₁)), kwargs...) = isapprox(s₁.center, s₂.center; atol, kwargs...) && isapprox(s₁.radius, s₂.radius; atol, kwargs...) -function (s::Sphere{2})(φ) +(s::Sphere)(args...) = _sphere(Val(embeddim(s)), s, args...) + +function _sphere(::Val{2}, s, φ) T = numtype(lentype(s)) if (φ < 0 || φ > 1) throw(DomainError(φ, "s(φ) is not defined for φ outside [0, 1].")) @@ -84,7 +86,7 @@ function (s::Sphere{2})(φ) c + Vec(x, y) end -function (s::Sphere{3})(θ, φ) +function _sphere(::Val{3}, s, θ, φ) T = numtype(lentype(s)) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "s(θ, φ) is not defined for θ, φ outside [0, 1]².")) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index d6ee2b334..fe3ac8174 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -9,19 +9,16 @@ A torus centered at `center` with axis of revolution directed by `normal` and with radii `major` and `minor`. """ -struct Torus{C<:CRS,ℒ<:Len} <: Primitive{3,C} - center::Point{3,C} - normal::Vec{3,ℒ} +struct Torus{C<:CRS,V<:Vec{3},ℒ<:Len} <: Primitive{C} + center::Point{C} + normal::V major::ℒ minor::ℒ - Torus(center::Point{3,C}, normal::Vec{3,ℒ}, major::ℒ, minor::ℒ) where {C<:CRS,ℒ<:Len} = - new{C,float(ℒ)}(center, normal, major, minor) + Torus(center::Point{C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,V<:Vec{3},ℒ<:Len} = + new{C,V,float(ℒ)}(center, normal, major, minor) end -function Torus(center::Point, normal::Vec, major::Len, minor::Len) - ℒ = promote_type(eltype(normal), typeof(major), typeof(minor)) - Torus(center, Vec{3,ℒ}(normal), ℒ(major), ℒ(minor)) -end +Torus(center::Point, normal::Vec, major::Len, minor::Len) = Torus(center, normal, promote(major, minor)...) Torus(center::Point, normal::Vec, major, minor) = Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) diff --git a/src/projecting.jl b/src/projecting.jl index 4bfa27b0b..38297b514 100644 --- a/src/projecting.jl +++ b/src/projecting.jl @@ -22,7 +22,7 @@ proj2D(p::PolyArea) = PolyArea(proj2D.(rings(p))) # IMPLEMENTATION # --------------- -proj2D(points::AbstractVector{<:Point{3}}) = proj(points, svdbasis(points)) +proj2D(points::AbstractVector{<:Point}) = proj(points, svdbasis(points)) function proj(points, basis) # retrieve basis diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 0c2f9e1e8..3c229b549 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -20,20 +20,20 @@ end RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors) -function refine(grid::CartesianGrid{Dim}, method::RegularRefinement) where {Dim} - factors = fitdims(method.factors, Dim) +function refine(grid::CartesianGrid, method::RegularRefinement) + factors = fitdims(method.factors, embeddim(grid)) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end -function refine(grid::RectilinearGrid{Datum,Dim}, method::RegularRefinement) where {Datum,Dim} - factors = fitdims(method.factors, Dim) +function refine(grid::RectilinearGrid{Datum}, method::RegularRefinement) where {Datum} + factors = fitdims(method.factors, embeddim(grid)) xyzₛ = xyz(grid) - xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), Dim) + xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), embeddim(grid)) RectilinearGrid{Datum}(xyzₜ) end -function refine(grid::StructuredGrid{Datum,Dim}, method::RegularRefinement) where {Datum,Dim} - factors = fitdims(method.factors, Dim) +function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {Datum} + factors = fitdims(method.factors, embeddim(grid)) XYZ′ = _XYZ(grid, factors) StructuredGrid{Datum}(XYZ′) end @@ -46,7 +46,9 @@ function _refinedims(x, f) x′ end -function _XYZ(grid::StructuredGrid{Datum,2}, factors::Dims{2}) where {Datum} +_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(embeddim(grid)), factors) + +function _XYZ(grid::StructuredGrid, ::Val{2}, factors::Dims{2}) T = numtype(lentype(grid)) fᵢ, fⱼ = factors sᵢ, sⱼ = size(grid) @@ -72,7 +74,7 @@ function _XYZ(grid::StructuredGrid{Datum,2}, factors::Dims{2}) where {Datum} (X, Y) end -function _XYZ(grid::StructuredGrid{Datum,3}, factors::Dims{3}) where {Datum} +function _XYZ(grid::StructuredGrid, ::Val{3}, factors::Dims{3}) T = numtype(lentype(grid)) fᵢ, fⱼ, fₖ = factors sᵢ, sⱼ, sₖ = size(grid) diff --git a/src/sampling/homogeneous.jl b/src/sampling/homogeneous.jl index d10f8d390..c822e5545 100644 --- a/src/sampling/homogeneous.jl +++ b/src/sampling/homogeneous.jl @@ -56,7 +56,10 @@ function sample(rng::AbstractRNG, tetrahedron::Tetrahedron, method::HomogeneousS @error "not implemented" end -function sample(rng::AbstractRNG, ball::Ball{2}, method::HomogeneousSampling) +sample(rng::AbstractRNG, ball::Ball, method::HomogeneousSampling) = + _sample(rng, ball, Val(embeddim(ball)), method) + +function _sample(rng::AbstractRNG, ball::Ball, ::Val{2}, method::HomogeneousSampling) function randpoint() u₁, u₂ = rand(rng, numtype(lentype(ball)), 2) ball(√u₁, u₂) @@ -64,7 +67,7 @@ function sample(rng::AbstractRNG, ball::Ball{2}, method::HomogeneousSampling) (randpoint() for _ in 1:(method.size)) end -function sample(rng::AbstractRNG, ball::Ball{3}, method::HomogeneousSampling) +function _sample(rng::AbstractRNG, ball::Ball, ::Val{3}, method::HomogeneousSampling) function randpoint() u₁, u₂, u₃ = rand(rng, numtype(lentype(ball)), 3) ball(∛u₁, acos(1 - 2u₂) / π, u₃) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index d29b4b558..152ca1bfc 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -37,9 +37,13 @@ function sample(::AbstractRNG, geom::Geometry, method::RegularSampling) Iterators.flatmap(identity, (iᵣ, iₚ)) end -firstoffset(g::Geometry) = ntuple(i -> (n -> zero(n)), paramdim(g)) -lastoffset(g::Geometry) = ntuple(i -> (n -> isperiodic(g)[i] ? inv(n) : zero(n)), paramdim(g)) -extrapoints(::Geometry, sz) = () +firstoffset(g::Geometry) = _firstoffset(g, Val(embeddim(g))) +lastoffset(g::Geometry) = _lastoffset(g, Val(embeddim(g))) +extrapoints(g::Geometry, sz) = _extrapoints(g, Val(embeddim(g)), sz) + +_firstoffset(g::Geometry, ::Val) = ntuple(i -> (n -> zero(n)), paramdim(g)) +_lastoffset(g::Geometry, ::Val) = ntuple(i -> (n -> isperiodic(g)[i] ? inv(n) : zero(n)), paramdim(g)) +_extrapoints(::Geometry, ::Val, sz) = () firstoffset(d::Disk) = (n -> inv(n), firstoffset(boundary(d))...) lastoffset(d::Disk) = (n -> zero(n), lastoffset(boundary(d))...) @@ -49,9 +53,9 @@ firstoffset(b::Ball) = (n -> inv(n), firstoffset(boundary(b))...) lastoffset(b::Ball) = (n -> zero(n), lastoffset(boundary(b))...) extrapoints(b::Ball, sz) = (center(b),) -firstoffset(::Sphere{3}) = (n -> inv(n + 1), n -> zero(n)) -lastoffset(::Sphere{3}) = (n -> inv(n + 1), n -> inv(n)) -extrapoints(s::Sphere{3}, sz) = (s(0, 0), s(1, 0)) +_firstoffset(::Sphere, ::Val{3}) = (n -> inv(n + 1), n -> zero(n)) +_lastoffset(::Sphere, ::Val{3}) = (n -> inv(n + 1), n -> inv(n)) +_extrapoints(s::Sphere, ::Val{3}, sz) = (s(0, 0), s(1, 0)) firstoffset(::Ellipsoid) = (n -> inv(n + 1), n -> zero(n)) lastoffset(::Ellipsoid) = (n -> inv(n + 1), n -> inv(n)) diff --git a/src/sets.jl b/src/sets.jl index 0cb97146e..409069398 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -15,7 +15,7 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` """ -struct GeometrySet{Dim,C<:CRS,G<:Geometry{Dim,C}} <: Domain{Dim,C} +struct GeometrySet{C<:CRS,G<:Geometry{C}} <: Domain{C} geoms::Vector{G} end @@ -37,7 +37,7 @@ Base.vcat(d1::Domain, d2::GeometrySet) = GeometrySet(vcat(collect(d1), d2.geoms) # SPECIAL CASE: POINT SET # ------------------------ -const PointSet{Dim,CRS} = GeometrySet{Dim,CRS,Point{Dim,CRS}} +const PointSet{CRS} = GeometrySet{CRS,Point{CRS}} """ PointSet(points) @@ -55,7 +55,7 @@ julia> PointSet([(1,2,3), (4,5,6)]) julia> PointSet((1,2,3), (4,5,6)) ``` """ -PointSet(points::AbstractVector{Point{Dim,C}}) where {Dim,C<:CRS} = PointSet{Dim,C}(points) +PointSet(points::AbstractVector{Point{C}}) where {C<:CRS} = PointSet{C}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) diff --git a/src/simplification.jl b/src/simplification.jl index 1668d0edd..0f09b5ab3 100644 --- a/src/simplification.jl +++ b/src/simplification.jl @@ -18,7 +18,9 @@ See also [`decimate`](@ref). """ function simplify end -simplify(box::Box{2}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method)) +simplify(box::Box, method::SimplificationMethod) = _simplify(box, Val(embeddim(box)), method) + +_simplify(box::Box, ::Val{2}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method)) simplify(polygon::Polygon, method::SimplificationMethod) = PolyArea([simplify(ring, method) for ring in rings(polygon)]) diff --git a/src/subdomains.jl b/src/subdomains.jl index 0c0c5ae8c..edb599f12 100644 --- a/src/subdomains.jl +++ b/src/subdomains.jl @@ -11,7 +11,7 @@ A partial view of a `domain` containing only the elements at `indices`. """ -struct SubDomain{Dim,C<:CRS,D<:Domain{Dim,C},I<:AbstractVector{Int}} <: Domain{Dim,C} +struct SubDomain{C<:CRS,D<:Domain{C},I<:AbstractVector{Int}} <: Domain{C} domain::D inds::I end diff --git a/src/trajecs.jl b/src/trajecs.jl index 4bc9b93d0..19df53e44 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -7,10 +7,10 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. """ -struct CylindricalTrajectory{C<:CRS,ℒ<:Len} <: Domain{3,C} - centroids::Vector{Point{3,C}} +struct CylindricalTrajectory{C<:CRS,ℒ<:Len} <: Domain{C} + centroids::Vector{Point{C}} radius::ℒ - CylindricalTrajectory(centroids::Vector{Point{3,C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = + CylindricalTrajectory(centroids::Vector{Point{C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(centroids, radius) end @@ -18,7 +18,7 @@ CylindricalTrajectory(centroids, radius::Len) = CylindricalTrajectory(collect(ce CylindricalTrajectory(centroids, radius) = CylindricalTrajectory(centroids, addunit(radius, u"m")) -CylindricalTrajectory(centroids::Vector{P}) where {P<:Point{3}} = CylindricalTrajectory(centroids, oneunit(lentype(P))) +CylindricalTrajectory(centroids::Vector{P}) where {P<:Point} = CylindricalTrajectory(centroids, oneunit(lentype(P))) CylindricalTrajectory(centroids) = CylindricalTrajectory(collect(centroids)) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index ba2abc674..757f06a9e 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -61,9 +61,11 @@ applycoord(t::Affine, p::Point) = withcrs(p, t.A * to(p) + t.b) # SPECIAL CASES # -------------- -applycoord(t::Affine, b::Box{2}) = applycoord(t, convert(Quadrangle, b)) +applycoord(t::Affine, b::Box) = _applycoord(t, b, Val(embeddim(b))) -applycoord(t::Affine, b::Box{3}) = applycoord(t, convert(Hexahedron, b)) +_applycoord(t::Affine, b::Box, ::Val{2}) = applycoord(t, convert(Quadrangle, b)) + +_applycoord(t::Affine, b::Box, ::Val{3}) = applycoord(t, convert(Hexahedron, b)) applycoord(t::Affine, g::CartesianGrid) = TransformedGrid(g, t) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 55431ab1f..bd982bf4a 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -72,8 +72,7 @@ end # OPERATION (7) # -------------- -# HalfEdgeTopology constructor -# performs orientation of faces +# HalfEdgeTopology constructor performs orientation of faces apply(::Repair{7}, mesh::Mesh) = topoconvert(HalfEdgeTopology, mesh), nothing # -------------- @@ -168,7 +167,7 @@ apply(::Repair{10}, poly::Ngon) = poly, nothing revert(::Repair{10}, poly::Ngon, cache) = poly -function _stretch10(g::Geometry{Dim}) where {Dim} +function _stretch10(g::Geometry) T = numtype(lentype(g)) - Stretch(ntuple(i -> one(T) + 10atol(T), Dim)) + Stretch(ntuple(i -> one(T) + 10atol(T), embeddim(g))) end diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index cdcfd37e1..580d85cad 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -67,9 +67,11 @@ applycoord(t::Rotate, v::Vec) = urotapply(t.rot, v) # SPECIAL CASES # -------------- -applycoord(t::Rotate, b::Box{2}) = applycoord(t, convert(Quadrangle, b)) +applycoord(t::Rotate, b::Box) = _applycoord(t, b, Val(embeddim(b))) -applycoord(t::Rotate, b::Box{3}) = applycoord(t, convert(Hexahedron, b)) +_applycoord(t::Rotate, b::Box, ::Val{2}) = applycoord(t, convert(Quadrangle, b)) + +_applycoord(t::Rotate, b::Box, ::Val{3}) = applycoord(t, convert(Hexahedron, b)) applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 3534c3106..67b88a0dc 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -46,11 +46,13 @@ applycoord(t::Scale, v::Vec) = t.factors .* v applycoord(t::Scale, b::Ball) = applycoord(t, discretize(b)) -applycoord(t::Scale, s::Sphere) = applycoord(t, discretize(s)) +applycoord(t::Scale{Dim}, s::Sphere) where {Dim} = _applycoord(t, s, Val(Dim), Val(embeddim(s))) -applycoord(t::Scale{1}, s::Sphere) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) +_applycoord(t::Scale, s::Sphere, ::Val, ::Val) = applycoord(t, discretize(s)) -applycoord(t::Scale{3}, s::Sphere{3}) = Ellipsoid(t.factors .* radius(s), applycoord(t, center(s))) +_applycoord(t::Scale, s::Sphere, ::Val{1}, ::Val) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) + +_applycoord(t::Scale, s::Sphere, ::Val{3}, ::Val{3}) = Ellipsoid(t.factors .* radius(s), applycoord(t, center(s))) applycoord(t::Scale, e::Ellipsoid) = applycoord(t, discretize(e)) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index 1027875bd..30a674ef7 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -37,12 +37,12 @@ applycoord(t::Translate, p::Point) = p + Vec(t.offsets) # SPECIALIZATIONS # ---------------- -apply(t::Translate{Dim}, g::RectilinearGrid{Datum,Dim}) where {Datum,Dim} = - RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], Dim)), nothing +apply(t::Translate, g::RectilinearGrid{Datum}) where {Datum} = + RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], embeddim(g))), nothing revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g)) -apply(t::Translate{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} = - StructuredGrid{Datum}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], Dim)), nothing +apply(t::Translate, g::StructuredGrid{Datum}) where {Datum} = + StructuredGrid{Datum}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], embeddim(g))), nothing revert(t::Translate, g::StructuredGrid, c) = first(apply(inverse(t), g)) diff --git a/src/traversing/multigrid.jl b/src/traversing/multigrid.jl index e73a406db..04a649f6d 100644 --- a/src/traversing/multigrid.jl +++ b/src/traversing/multigrid.jl @@ -10,7 +10,8 @@ the coarsest scale and moves to progressively finer scales. """ struct MultiGridPath <: Path end -function traverse(grid::Grid{Dim}, ::MultiGridPath) where {Dim} +function traverse(grid::Grid, ::MultiGridPath) + Dim = embeddim(grid) dims = size(grid) nelems = prod(dims) linear = LinearIndices(dims) diff --git a/src/utils.jl b/src/utils.jl index 591b86d46..cb24a3bdb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -12,6 +12,15 @@ Throws an `AssertionError(msg)` if `cond` is `false`. """ assertion(cond, msg) = cond || throw(AssertionError(msg)) +""" + checkdim(geom, dim) + +Throws an `ArgumentError` if the `embeddim` of the geometry `geom` +is different than the specified dimension `dim`. +""" +checkdim(geom, dim) = + embeddim(geom) ≠ dim && throw(ArgumentError("geometry must be embedded in $dim-dimensional space")) + """ constructor(G) @@ -69,7 +78,8 @@ flat(c::CRS) = convert(Cartesian, c) Compute signed area of triangle formed by points `A`, `B` and `C`. """ -function signarea(A::Point{2}, B::Point{2}, C::Point{2}) +function signarea(A::Point, B::Point, C::Point) + checkdim(A, 2) ((B - A) × (C - A)) / 2 end @@ -104,7 +114,8 @@ using the singular value decomposition (SVD). See . """ -function svdbasis(p::AbstractVector{<:Point{3}}) +function svdbasis(p::AbstractVector{<:Point}) + checkdim(first(p), 3) ℒ = lentype(eltype(p)) X = reduce(hcat, to.(p)) μ = sum(X, dims=2) / size(X, 2) @@ -146,7 +157,7 @@ calculated in order to identify the intersection type: - No intersection and parallel: r == 1, rₐ == 2 - No intersection, skew lines: r == 2, rₐ == 3 """ -function intersectparameters(a::Point{Dim}, b::Point{Dim}, c::Point{Dim}, d::Point{Dim}) where {Dim} +function intersectparameters(a::Point, b::Point, c::Point, d::Point) A = ustrip.([(b - a) (c - d)]) y = ustrip.(c - a) T = eltype(A) diff --git a/src/viewing.jl b/src/viewing.jl index ce56a69d1..8987216e3 100644 --- a/src/viewing.jl +++ b/src/viewing.jl @@ -28,7 +28,7 @@ that intersect with the `geometry`. """ indices(domain::Domain, geometry::Geometry) = findall(intersects(geometry), domain) -function indices(grid::Grid{Dim}, point::Point{Dim}) where {Dim} +function indices(grid::Grid, point::Point) point ∉ grid && return Int[] # grid properties @@ -46,35 +46,33 @@ function indices(grid::Grid{Dim}, point::Point{Dim}) where {Dim} [LinearIndices(dims)[coords...]] end -function indices(grid::Grid{2}, poly::Polygon{2}) +function indices(grid::Grid, chain::Chain) dims = size(grid) - mask = zeros(Int, dims) - cpoly = poly ∩ boundingbox(grid) - isnothing(cpoly) && return Int[] + mask = falses(dims) - for (i, triangle) in enumerate(simplexify(cpoly)) - _fill!(mask, grid, i, triangle) + for segment in segments(chain) + p₁, p₂ = vertices(segment) + _bresenham!(mask, grid, true, p₁, p₂) end # convert to linear indices - LinearIndices(dims)[mask .> 0] + LinearIndices(dims)[mask] end -function indices(grid::Grid{2}, chain::Chain{2}) +function indices(grid::Grid, poly::Polygon) dims = size(grid) - mask = falses(dims) + mask = zeros(Int, dims) + cpoly = poly ∩ boundingbox(grid) + isnothing(cpoly) && return Int[] - for segment in segments(chain) - p₁, p₂ = vertices(segment) - _bresenham!(mask, grid, true, p₁, p₂) + for (i, triangle) in enumerate(simplexify(cpoly)) + _fill!(mask, grid, i, triangle) end # convert to linear indices - LinearIndices(dims)[mask] + LinearIndices(dims)[mask .> 0] end -indices(domain::Domain, multi::Multi) = mapreduce(geom -> indices(domain, geom), vcat, parent(multi)) |> unique - function indices(grid::CartesianGrid, box::Box) # grid properties or = minimum(grid) @@ -95,6 +93,8 @@ function indices(grid::CartesianGrid, box::Box) LinearIndices(sz)[range] |> vec end +indices(domain::Domain, multi::Multi) = mapreduce(geom -> indices(domain, geom), vcat, parent(multi)) |> unique + # utils function _fill!(mask, grid, val, triangle) v = vertices(triangle) diff --git a/test/discretization.jl b/test/discretization.jl index 5981e8989..079790c33 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -390,7 +390,7 @@ poly = PolyArea(cart.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) mesh = discretize(poly, RegularDiscretization(50)) - @test mesh isa SubGrid{2} + @test mesh isa SubGrid grid = parent(mesh) @test grid isa CartesianGrid @test eltype(mesh) <: Quadrangle diff --git a/test/domains.jl b/test/domains.jl index 453972893..c2bab01aa 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -10,7 +10,7 @@ dom = DummyDomain(cart(0, 0)) @test dom[begin] == Ball(cart(1, 1), T(1)) @test dom[end] == Ball(cart(3, 3), T(1)) - @test eltype(dom) <: Ball{2} + @test eltype(dom) <: Ball @test length(dom) == 3 @test keys(dom) == 1:3 @test collect(dom) == [Ball(cart(i, i), T(1)) for i in 1:3] diff --git a/test/dummy.jl b/test/dummy.jl index b4478451e..66ef9acb6 100644 --- a/test/dummy.jl +++ b/test/dummy.jl @@ -1,12 +1,12 @@ # dummy type implementing the Domain trait -struct DummyDomain{Dim,C<:CRS} <: Domain{Dim,C} - origin::Point{Dim,C} +struct DummyDomain{C<:CRS} <: Domain{C} + origin::Point{C} end -function Meshes.element(domain::DummyDomain{Dim}, ind::Int) where {Dim} +function Meshes.element(domain::DummyDomain, ind::Int) ℒ = Meshes.lentype(domain) T = Unitful.numtype(ℒ) - c = domain.origin + Vec(ntuple(i -> T(ind) * unit(ℒ), Dim)) + c = domain.origin + Vec(ntuple(i -> T(ind) * unit(ℒ), embeddim(domain))) r = oneunit(ℒ) Ball(c, r) end diff --git a/test/mesh.jl b/test/mesh.jl index e1e15205d..49a3694c7 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -10,7 +10,7 @@ @test extrema(grid) == (cart(0), cart(100)) @test spacing(grid) == (T(1) * u"m",) @test nelements(grid) == 100 - @test eltype(grid) <: Segment{1} + @test eltype(grid) <: Segment @test measure(grid) ≈ T(100) * u"m" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @@ -27,7 +27,7 @@ @test extrema(grid) == (cart(0, 0), cart(200, 100)) @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle @test measure(grid) ≈ T(200 * 100) * u"m^2" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @@ -44,7 +44,7 @@ @test extrema(grid) == (cart(0, 0, 0), cart(200, 100, 50)) @test spacing(grid) == (T(1) * u"m", T(1) * u"m", T(1) * u"m") @test nelements(grid) == 200 * 100 * 50 - @test eltype(grid) <: Hexahedron{3} + @test eltype(grid) <: Hexahedron @test measure(grid) ≈ T(200 * 100 * 50) * u"m^3" @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) @@ -69,7 +69,7 @@ @test maximum(grid) == cart(1.0, 1.0) @test spacing(grid) == (T(2 / 200) * u"m", T(2 / 100) * u"m") @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) @test embeddim(grid) == 3 @@ -81,7 +81,7 @@ @test extrema(grid) == (cart(0, 0, 0), cart(100, 50, 25)) @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") @test nelements(grid) == 20 * 10 * 5 - @test eltype(grid) <: Hexahedron{3} + @test eltype(grid) <: Hexahedron @test vertices(grid[1]) == ( cart(0, 0, 0), cart(5, 0, 0), @@ -104,7 +104,7 @@ @test maximum(grid) == cart(10.0, 10.0) @test spacing(grid) == (T(1) * u"m", T(1) * u"m") @test nelements(grid) == 10 * 10 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle # mixed units grid = CartesianGrid((10, 10), (T(0) * u"m", T(0) * u"cm"), (T(100) * u"cm", T(1) * u"m")) @@ -167,7 +167,7 @@ @test centroid(grid, 2) == cart(1.5, 0.5) @test centroid(grid, 200 * 100) == cart(199.5, 99.5) @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle @test grid[1] == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test grid[2] == Quadrangle(cart(1, 0), cart(2, 0), cart(2, 1), cart(1, 1)) @@ -257,7 +257,7 @@ @test maximum(grid) == cart(1, 1) @test extrema(grid) == (cart(0, 0), cart(1, 1)) @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle @test measure(grid) ≈ T(1) * u"m^2" @test centroid(grid, 1) ≈ cart(0.1, 0.05) @test centroid(grid[1]) ≈ cart(0.1, 0.05) @@ -383,7 +383,7 @@ @test maximum(grid) == cart(1, 1) @test extrema(grid) == (cart(0, 0), cart(1, 1)) @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle{2} + @test eltype(grid) <: Quadrangle @test measure(grid) ≈ T(1) * u"m^2" @test centroid(grid, 1) ≈ cart(0.1, 0.05) @test centroid(grid[1]) ≈ cart(0.1, 0.05) @@ -526,7 +526,7 @@ for i in 1:length(triangles) @test mesh[i] == triangles[i] end - @test eltype(mesh) <: Triangle{2} + @test eltype(mesh) <: Triangle @test measure(mesh) ≈ T(1) * u"m^2" @test area(mesh) ≈ T(1) * u"m^2" @test extrema(mesh) == (cart(0, 0), cart(1, 1)) @@ -535,17 +535,17 @@ coords = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(coords, SimpleTopology(connec)) - @test eltype(mesh) <: Triangle{2} + @test eltype(mesh) <: Triangle @test topology(mesh) isa SimpleTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 mesh = SimpleMesh(coords, connec) - @test eltype(mesh) <: Triangle{2} + @test eltype(mesh) <: Triangle @test topology(mesh) isa SimpleTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 mesh = SimpleMesh(coords, connec, relations=true) - @test eltype(mesh) <: Triangle{2} + @test eltype(mesh) <: Triangle @test topology(mesh) isa HalfEdgeTopology @test nvertices(mesh) == 5 @test nelements(mesh) == 4 @@ -565,7 +565,7 @@ for i in 1:length(elms) @test mesh[i] == elms[i] end - @test eltype(mesh) <: Polygon{2} + @test eltype(mesh) <: Polygon # test for https://github.com/JuliaGeometry/Meshes.jl/issues/177 points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) diff --git a/test/polytopes.jl b/test/polytopes.jl index a52670751..d186a1fd9 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -240,11 +240,6 @@ @test @elapsed(issimple(r)) < 0.02 @test @allocated(issimple(r)) < 950000 - # innerangles in 3D is obtained via projection - r1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - r2 = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) - @test innerangles(r1) ≈ innerangles(r2) - # CRS propagation r = Ring(merc.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test crs(centroid(r)) === crs(r) @@ -389,7 +384,7 @@ @test isapprox(norm(normal(t)), oneunit(ℳ)) t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - @test_throws ErrorException("signed area only defined for triangles embedded in R², use `area` instead") signarea(t) + @test_throws ArgumentError signarea(t) # CRS propagation t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) diff --git a/test/predicates.jl b/test/predicates.jl index ec53b3435..6abd726a1 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -139,13 +139,12 @@ @testset "isperiodic" begin # primitives - @test isperiodic(Box{1}) == (false,) - @test isperiodic(Box{2}) == (false, false) - @test isperiodic(Box{3}) == (false, false, false) - @test isperiodic(Ball{2}) == (false, true) - @test isperiodic(Ball{3}) == (false, true, true) - @test isperiodic(Sphere{2}) == (true,) - @test isperiodic(Sphere{3}) == (true, true) + @test isperiodic(Box{Cartesian2D}) == (false, false) + @test isperiodic(Box{Cartesian3D}) == (false, false, false) + @test isperiodic(Ball{Cartesian2D}) == (false, true) + @test isperiodic(Ball{Cartesian3D}) == (false, true, true) + @test isperiodic(Sphere{Cartesian2D}) == (true,) + @test isperiodic(Sphere{Cartesian3D}) == (true, true) @test isperiodic(Ellipsoid) == (true, true) @test isperiodic(Cylinder) == (false, true, false) @test isperiodic(CylinderSurface) == (true, false) diff --git a/test/primitives.jl b/test/primitives.jl index 8c9871f98..aa64bcc1e 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -28,7 +28,7 @@ @test cart(1) - cart(1) == vector(0) @test cart(1, 2) - cart(1, 1) == vector(0, 1) @test cart(1, 2, 3) - cart(1, 1, 1) == vector(0, 1, 2) - @test_throws MethodError cart(1, 2) - cart(1, 2, 3) + @test_throws DimensionMismatch cart(1, 2) - cart(1, 2, 3) @test cart(1) + vector(0) == cart(1) @test cart(2) + vector(2) == cart(4) @@ -36,7 +36,7 @@ @test cart(2, 3) + vector(2, 1) == cart(4, 4) @test cart(1, 2, 3) + vector(0, 0, 0) == cart(1, 2, 3) @test cart(2, 3, 4) + vector(2, 1, 0) == cart(4, 4, 4) - @test_throws MethodError cart(1, 2) + vector(1, 2, 3) + @test_throws DimensionMismatch cart(1, 2) + vector(1, 2, 3) @test cart(1) - vector(0) == cart(1) @test cart(2) - vector(2) == cart(0) diff --git a/test/sampling.jl b/test/sampling.jl index 135912f44..d46bb3c25 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -310,24 +310,24 @@ @testset "HomogeneousSampling" begin s = Segment(cart(0, 0), cart(1, 0)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in to.(ps)) @test all(coords[2] == zero(ℳ) for coords in to.(ps)) s = Segment(cart(0, 0), cart(0, 1)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(coords[1] == zero(ℳ) for coords in to.(ps)) @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) s = Segment(cart(0, 0), cart(1, 1)) ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) c = Rope(cart(0, 0), cart(1, 0), cart(0, 1), cart(1, 1)) ps = sample(c, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all( coords[1] + coords[2] == oneunit(ℳ) || (zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) && coords[2] ∈ [zero(ℳ), oneunit(ℳ)]) for coords in to.(ps) @@ -335,22 +335,22 @@ t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) ps = sample(t, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(t), ps) q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) ps = sample(q, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(q), ps) b = Ball(cart(10, 10), T(3)) ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(b), ps) b = Ball(cart(10, 10, 10), T(10)) ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa Point{3} + @test first(ps) isa Point @test all(∈(b), ps) poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @@ -363,10 +363,10 @@ connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) mesh = SimpleMesh(points, connec) ps = sample(mesh, HomogeneousSampling(400)) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(mesh), ps) ps = sample(mesh, HomogeneousSampling(400, 1:nelements(mesh))) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(mesh), ps) end @@ -382,7 +382,7 @@ mesh = SimpleMesh(points, connec) ps = sample(mesh, MinDistanceSampling(0.2)) n = length(ps) - @test first(ps) isa Point{2} + @test first(ps) isa Point @test all(∈(mesh), ps) @test all(norm(ps[i] - ps[j]) ≥ T(0.2) * u"m" for i in 1:n for j in (i + 1):n) diff --git a/test/sets.jl b/test/sets.jl index 6c85d4c67..f6e1d6304 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -23,7 +23,7 @@ push!(geoms, Segment(cart(1, 0), cart(1, 1))) push!(geoms, Segment(cart(1, 1), cart(0, 0))) gset = GeometrySet(geoms) - @test eltype(gset) <: Segment{2} + @test eltype(gset) <: Segment # conversion grid = cartgrid(10, 10) @@ -39,21 +39,21 @@ @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: Point{1} + @test eltype(pset) <: Point pset = PointSet(randpoint2(100)) @test embeddim(pset) == 2 @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: Point{2} + @test eltype(pset) <: Point pset = PointSet(randpoint3(100)) @test embeddim(pset) == 3 @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ @test nelements(pset) == 100 - @test eltype(pset) <: Point{3} + @test eltype(pset) <: Point pset1 = PointSet([cart(1, 2, 3), cart(4, 5, 6)]) pset2 = PointSet(cart(1, 2, 3), cart(4, 5, 6)) diff --git a/test/subdomains.jl b/test/subdomains.jl index 9f7a95ae9..5cb7fc5fc 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -40,7 +40,7 @@ # view of view stores the correct domain g = cartgrid(10, 10) v = view(view(g, 11:20), 1:3) - @test v isa SubGrid{2} + @test v isa SubGrid @test v[1] == g[11] @test v[2] == g[12] @test v[3] == g[13] @@ -58,7 +58,7 @@ # concatenation with same parent g = cartgrid(10, 10) vg = vcat(view(g, 50:70), view(g, 10:30)) - @test vg isa SubGrid{2} + @test vg isa SubGrid @test vg == view(g, [50:70; 10:30]) # concatenation with different parents g1 = cartgrid(10, 10) @@ -74,9 +74,9 @@ v1 = view(d1, 1:500000) v2 = view(d2, 1:500000000) v3 = view(d3, [1, 3]) - @test eltype(v1) <: Quadrangle{2} - @test eltype(v2) <: Hexahedron{3} - @test eltype(v3) <: Primitive{2} + @test eltype(v1) <: Quadrangle + @test eltype(v2) <: Hexahedron + @test eltype(v3) <: Primitive # show pset = PointSet(cart.(1:100, 1:100)) diff --git a/test/testutils.jl b/test/testutils.jl index 53b15e097..a7027b7b4 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -85,7 +85,9 @@ function equaltest(g) @test g == withprecision(Float32, g) end -function isapproxtest(g::Geometry{1}) +isapproxtest(g::Geometry) = _isapproxtest(g, Val(embeddim(g))) + +function _isapproxtest(g::Geometry, ::Val{1}) τ64 = Meshes.atol(Float64) * u"m" τ32 = Meshes.atol(Float32) * u"m" g64 = withprecision(Float64, g) @@ -94,7 +96,7 @@ function isapproxtest(g::Geometry{1}) @test isapprox(g, Translate(τ32)(g32), atol=1.1τ32) end -function isapproxtest(g::Geometry{2}) +function _isapproxtest(g::Geometry, ::Val{2}) τ64 = Meshes.atol(Float64) * u"m" τ32 = Meshes.atol(Float32) * u"m" g64 = withprecision(Float64, g) @@ -105,7 +107,7 @@ function isapproxtest(g::Geometry{2}) @test isapprox(g, Translate(0u"m", τ32)(g32), atol=1.1τ32) end -function isapproxtest(g::Geometry{3}) +function _isapproxtest(g::Geometry, ::Val{3}) τ64 = Meshes.atol(Float64) * u"m" τ32 = Meshes.atol(Float32) * u"m" g64 = withprecision(Float64, g) From fcc47f900db44920674af05f8ed594359e573fbb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:32:38 -0300 Subject: [PATCH 180/423] :robot: Format .jl files (#946) Co-authored-by: eliascarv <73039601+eliascarv@users.noreply.github.com> --- src/discretization.jl | 3 ++- src/sampling/homogeneous.jl | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index 8000dbd2b..ab6c3f0b0 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -91,7 +91,8 @@ end discretize(multi::Multi, method::BoundaryDiscretizationMethod) = mapreduce(geom -> discretize(geom, method), merge, parent(multi)) -discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) = _discretizewithin(ring, Val(embeddim(ring)), method) +discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) = + _discretizewithin(ring, Val(embeddim(ring)), method) function _discretizewithin(ring::Ring, ::Val{3}, method::BoundaryDiscretizationMethod) # collect vertices to get rid of static containers diff --git a/src/sampling/homogeneous.jl b/src/sampling/homogeneous.jl index c822e5545..5a68d5e1b 100644 --- a/src/sampling/homogeneous.jl +++ b/src/sampling/homogeneous.jl @@ -56,8 +56,7 @@ function sample(rng::AbstractRNG, tetrahedron::Tetrahedron, method::HomogeneousS @error "not implemented" end -sample(rng::AbstractRNG, ball::Ball, method::HomogeneousSampling) = - _sample(rng, ball, Val(embeddim(ball)), method) +sample(rng::AbstractRNG, ball::Ball, method::HomogeneousSampling) = _sample(rng, ball, Val(embeddim(ball)), method) function _sample(rng::AbstractRNG, ball::Ball, ::Val{2}, method::HomogeneousSampling) function randpoint() From 87974a33a87f62cb7cb32dda645bb0528efc6044 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:35:22 -0300 Subject: [PATCH 181/423] Remove 'Dim' from 'getindex' of 'TransformedGrid' (#949) --- src/mesh/transformedmesh.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 475ec2f88..93515e709 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -28,7 +28,7 @@ const TransformedGrid{CRS,Dim,G<:Grid{CRS,Dim},TR} = TransformedMesh{CRS,GridTop TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) -@propagate_inbounds Base.getindex(g::TransformedGrid{CRS,Dim}, I::CartesianIndices{Dim}) where {CRS,Dim} = +@propagate_inbounds Base.getindex(g::TransformedGrid, I::CartesianIndices) = TransformedGrid(getindex(g.mesh, I), g.transform) function Base.summary(io::IO, g::TransformedGrid) From 0c9e1cc9bc96c9bd841a11ca808af0df951e2b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 20 Jul 2024 09:43:28 -0300 Subject: [PATCH 182/423] Rename Overlaps -> Within --- docs/src/transforms.md | 6 ++--- src/Meshes.jl | 2 +- src/transforms.jl | 2 +- src/transforms/{overlaps.jl => within.jl} | 22 +++++++++--------- test/transforms.jl | 28 +++++++++++------------ 5 files changed, 30 insertions(+), 30 deletions(-) rename src/transforms/{overlaps.jl => within.jl} (66%) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index cd26b86e4..e2838bedc 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -176,15 +176,15 @@ viz(fig[1,2], disk) fig ``` -## Overlaps +## Within ```@docs -Overlaps +Within ``` ```@example transforms grid = CartesianGrid(10, 10) -subgrid = grid |> Overlaps(x=(1.5, 6.5), y=(3.5, 8.5)) +subgrid = grid |> Within(x=(1.5, 6.5), y=(3.5, 8.5)) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) diff --git a/src/Meshes.jl b/src/Meshes.jl index 30ca0878a..badd5dbc4 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -521,7 +521,7 @@ export Proj, LengthUnit, Shadow, - Overlaps, + Within, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 0c36c09ad..d2ac1d04c 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -98,7 +98,7 @@ include("transforms/stdcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") -include("transforms/overlaps.jl") +include("transforms/within.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/overlaps.jl b/src/transforms/within.jl similarity index 66% rename from src/transforms/overlaps.jl rename to src/transforms/within.jl index a030507f8..ba47ae7a3 100644 --- a/src/transforms/overlaps.jl +++ b/src/transforms/within.jl @@ -3,32 +3,32 @@ # ------------------------------------------------------------------ """ - Overlaps(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) + Within(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) -Retain the domain geometries that overlap with `x` limits [`xmax`,`xmax`], +Retain the domain geometries that intersect with `x` limits [`xmax`,`xmax`], `y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units (default to meters). ## Examples ```julia -Overlaps(x=(2, 4)) -Overlaps(x=(1u"km", 3u"km")) -Overlaps(y=(1.2, 1.8), z=(2.4, 3.0)) +Within(x=(2, 4)) +Within(x=(1u"km", 3u"km")) +Within(y=(1.2, 1.8), z=(2.4, 3.0)) ``` """ -struct Overlaps{X,Y,Z} <: GeometricTransform +struct Within{X,Y,Z} <: GeometricTransform x::X y::Y z::Z end -Overlaps(; x=nothing, y=nothing, z=nothing) = - Overlaps(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) +Within(; x=nothing, y=nothing, z=nothing) = + Within(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) -parameters(t::Overlaps) = (; x=t.x, y=t.y, z=t.z) +parameters(t::Within) = (; x=t.x, y=t.y, z=t.z) -function preprocess(t::Overlaps, d::Domain) +function preprocess(t::Within, d::Domain) bbox = boundingbox(d) bbox₁ = _overlaps(1, t.x, bbox) bbox₂ = _overlaps(2, t.y, bbox₁) @@ -36,7 +36,7 @@ function preprocess(t::Overlaps, d::Domain) indices(d, bbox₃) end -function apply(t::Overlaps, d::Domain) +function apply(t::Within, d::Domain) inds = preprocess(t, d) n = view(d, inds) n, nothing diff --git a/test/transforms.jl b/test/transforms.jl index 2de2724c6..8f4c85d82 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1651,21 +1651,21 @@ @test r == SimpleMesh(f.(vertices(d)), topology(d)) end - @testset "Overlaps" begin - @test !isaffine(Overlaps(x=(T(2), T(4)))) - @test !TB.isrevertible(Overlaps(x=(T(2), T(4)))) - @test !TB.isinvertible(Overlaps(x=(T(2), T(4)))) - @test TB.parameters(Overlaps(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) - @test TB.parameters(Overlaps(y=(T(2) * u"km", T(4) * u"km"))) == + @testset "Within" begin + @test !isaffine(Within(x=(T(2), T(4)))) + @test !TB.isrevertible(Within(x=(T(2), T(4)))) + @test !TB.isinvertible(Within(x=(T(2), T(4)))) + @test TB.parameters(Within(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) + @test TB.parameters(Within(y=(T(2) * u"km", T(4) * u"km"))) == (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) - @test TB.parameters(Overlaps(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) - @test_throws ArgumentError Overlaps(x=(T(2) * u"°", T(4) * u"°")) + @test TB.parameters(Within(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) + @test_throws ArgumentError Within(x=(T(2) * u"°", T(4) * u"°")) # --------- # POINTSET # --------- - f = Overlaps(x=(T(1.5), T(3.5))) + f = Within(x=(T(1.5), T(3.5))) d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) r, c = TB.apply(f, d) @test r == PointSet([cart(2, 1), cart(3, 1)]) @@ -1674,7 +1674,7 @@ # GEOMETRYSET # ------------ - f = Overlaps(x=(T(1.5), T(3.5))) + f = Within(x=(T(1.5), T(3.5))) t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) t2 = t1 |> Translate(T(2), T(2)) t3 = t2 |> Translate(T(2), T(2)) @@ -1686,7 +1686,7 @@ # CARTESIANGRID # -------------- - f = Overlaps(z=(T(1.5), T(4.5))) + f = Within(z=(T(1.5), T(4.5))) d = cartgrid(10, 10, 10) r, c = TB.apply(f, d) @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) @@ -1695,7 +1695,7 @@ # RECTILINEARGRID # ---------------- - f = Overlaps(y=(T(3.5), T(6.5))) + f = Within(y=(T(3.5), T(6.5))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) @@ -1704,7 +1704,7 @@ # STRUCTUREDGRID # --------------- - f = Overlaps(x=(T(5.5), T(8.5))) + f = Within(x=(T(5.5), T(8.5))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) @@ -1713,7 +1713,7 @@ # SIMPLEMESH # ----------- - f = Overlaps(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) + f = Within(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) d = convert(SimpleMesh, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) From 5f02da758b0900060e86aef57b72f4e1e38a57ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 20 Jul 2024 10:42:01 -0300 Subject: [PATCH 183/423] Fix #944: remove unreliable time test --- test/primitives.jl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/primitives.jl b/test/primitives.jl index aa64bcc1e..064aae705 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -364,14 +364,6 @@ @test boundary(b) == Multi([cart(0, 0), cart(1, 1)]) @test perimeter(b) == zero(ℳ) - rng = StableRNG(123) - b = BezierCurve(cart.(randn(rng, 100), randn(rng, 100))) - t1 = @timed b(T(0.2)) - t2 = @timed b(T(0.2), Horner()) - @test t1.time < 5e-4 - @test t2.time < 5e-4 - @test t2.bytes < 100 - # CRS propagation b = BezierCurve(merc(0, 0), merc(0.5, 1), merc(1, 0)) @test crs(b(T(0), Horner())) === crs(b) From 460d4f91780170f69ac777e231a9fcd72ec5d5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sun, 21 Jul 2024 10:08:06 -0300 Subject: [PATCH 184/423] Move matrices.md to meshes.md --- docs/make.jl | 3 +- docs/src/algorithms/matrices.md | 62 ------------------------------- docs/src/domains/meshes.md | 65 ++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 65 deletions(-) delete mode 100644 docs/src/algorithms/matrices.md diff --git a/docs/make.jl b/docs/make.jl index 732b4f265..f0caeee6c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -29,8 +29,7 @@ makedocs( "algorithms/orientation.md", "algorithms/neighborsearch.md", "algorithms/boundingbox.md", - "algorithms/hulls.md", - "algorithms/matrices.md" + "algorithms/hulls.md" ], "Transforms" => "transforms.md", "Random" => "rand.md", diff --git a/docs/src/algorithms/matrices.md b/docs/src/algorithms/matrices.md deleted file mode 100644 index aaeaa794e..000000000 --- a/docs/src/algorithms/matrices.md +++ /dev/null @@ -1,62 +0,0 @@ -# Matrices - -```@example matrices -using Meshes # hide -import CairoMakie as Mke # hide -``` - -## Laplace - -```@docs -laplacematrix -``` - -```@example matrices -grid = CartesianGrid(10, 10) - -laplacematrix(grid, kind = :uniform) -``` - -```@example matrices -points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] -connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) -mesh = SimpleMesh(points, connec) - -laplacematrix(mesh, kind = :cotangent) -``` - -## Measure - -```@docs -measurematrix -``` - -```@example matrices -grid = CartesianGrid(10, 10) - -measurematrix(grid) -``` - -```@example matrices -points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] -connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) -mesh = SimpleMesh(points, connec) - -measurematrix(mesh) -``` - -## Adjacency - -```@docs -adjacencymatrix -``` - -```@example matrices -grid = CartesianGrid(10, 10) - -adjacencymatrix(grid) -``` - -```@example matrices -adjacencymatrix(grid, rank = 0) -``` diff --git a/docs/src/domains/meshes.md b/docs/src/domains/meshes.md index 6dfa14cbe..ace1bed3f 100644 --- a/docs/src/domains/meshes.md +++ b/docs/src/domains/meshes.md @@ -95,7 +95,8 @@ Coboundary Adjacency ``` -### Examples +Consider the following examples with the [`Boundary`](@ref) and +[`Coboundary`](@ref) relations defined for the [`HalfEdgeTopology`](@ref): ```@example meshes # global vector of 2D points @@ -126,3 +127,65 @@ topo = convert(HalfEdgeTopology, topology(mesh)) # show n-gons that share edge 3 𝒞₁₂(3) ``` + +## Matrices + +Based on topological relations, we can extract matrices that +are widely used in applications such as [`laplacematrix`](@ref), +and [`adjacencymatrix`](@ref). + +### Laplace + +```@docs +laplacematrix +``` + +```@example meshes +grid = CartesianGrid(10, 10) + +laplacematrix(grid, kind = :uniform) +``` + +```@example meshes +points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] +connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) +mesh = SimpleMesh(points, connec) + +laplacematrix(mesh, kind = :cotangent) +``` + +### Measure + +```@docs +measurematrix +``` + +```@example meshes +grid = CartesianGrid(10, 10) + +measurematrix(grid) +``` + +```@example meshes +points = [(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)] +connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) +mesh = SimpleMesh(points, connec) + +measurematrix(mesh) +``` + +### Adjacency + +```@docs +adjacencymatrix +``` + +```@example meshes +grid = CartesianGrid(10, 10) + +adjacencymatrix(grid) +``` + +```@example meshes +adjacencymatrix(grid, rank = 0) +``` From e61f0acd4b9b08b2e37d06fce2f7dfa6f3f7f613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 22 Jul 2024 20:18:57 -0300 Subject: [PATCH 185/423] Fix isperiodic for Ball, Sphere and Ellipsoid --- src/discretization/regular.jl | 7 +------ src/predicates/isperiodic.jl | 6 +++--- test/predicates.jl | 6 +++--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index 8e5a7e90f..bf68710a1 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -35,18 +35,13 @@ end function wrapgrid(g, m) sz = fitdims(m.sizes, paramdim(g)) - pd = perdims(g) + pd = isperiodic(g) np = @. sz + !pd ps = sample(g, RegularSampling(np)) tg = GridTopology(sz, pd) ps, tg end -perdims(g) = _perdims(g, Val(embeddim(g))) -_perdims(g, ::Val) = isperiodic(g) -_perdims(::Sphere, ::Val{3}) = (false, true) -perdims(::Ellipsoid) = (false, true) - # ------------------------ # append to grid topology # ------------------------ diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index a29c1506f..974a47e53 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -22,11 +22,11 @@ isperiodic(::Type{<:Plane}) = (false, false) isperiodic(B::Type{<:Box}) = ntuple(i -> false, embeddim(B)) -isperiodic(B::Type{<:Ball}) = ntuple(i -> i != 1, embeddim(B)) +isperiodic(B::Type{<:Ball}) = ntuple(i -> i == embeddim(B), embeddim(B)) -isperiodic(S::Type{<:Sphere}) = ntuple(i -> true, embeddim(S) - 1) +isperiodic(S::Type{<:Sphere}) = ntuple(i -> i == embeddim(S) - 1, embeddim(S) - 1) -isperiodic(::Type{<:Ellipsoid}) = (true, true) +isperiodic(::Type{<:Ellipsoid}) = (false, true) isperiodic(::Type{<:Disk}) = (false, true) diff --git a/test/predicates.jl b/test/predicates.jl index 6abd726a1..46ba1e25d 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -142,10 +142,10 @@ @test isperiodic(Box{Cartesian2D}) == (false, false) @test isperiodic(Box{Cartesian3D}) == (false, false, false) @test isperiodic(Ball{Cartesian2D}) == (false, true) - @test isperiodic(Ball{Cartesian3D}) == (false, true, true) + @test isperiodic(Ball{Cartesian3D}) == (false, false, true) @test isperiodic(Sphere{Cartesian2D}) == (true,) - @test isperiodic(Sphere{Cartesian3D}) == (true, true) - @test isperiodic(Ellipsoid) == (true, true) + @test isperiodic(Sphere{Cartesian3D}) == (false, true) + @test isperiodic(Ellipsoid) == (false, true) @test isperiodic(Cylinder) == (false, true, false) @test isperiodic(CylinderSurface) == (true, false) @test isperiodic(ParaboloidSurface) == (false, true) From 71cd70d374d04a28d5cae02ee3d909f490676d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 23 Jul 2024 07:15:55 -0300 Subject: [PATCH 186/423] Fix docstring of GridTopology --- src/topologies/grid.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/topologies/grid.jl b/src/topologies/grid.jl index e5db134a5..2408e5f5c 100644 --- a/src/topologies/grid.jl +++ b/src/topologies/grid.jl @@ -14,7 +14,6 @@ to aperiodic dimensions. ```julia julia> GridTopology((10,20)) # 10x20 elements in a grid julia> GridTopology((10,20), (true,false)) # cylinder topology -julia> GridTopology((10,20), (true,true)) # sphere topology ``` """ struct GridTopology{D} <: Topology From 29a6856d0138333959bfc5913573e4200bba3f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 23 Jul 2024 09:24:51 -0300 Subject: [PATCH 187/423] Fix Review `orientation` in 3D #943 --- docs/src/algorithms/orientation.md | 3 -- src/Meshes.jl | 3 -- src/discretization/held.jl | 2 +- src/orientation.jl | 72 +++++------------------------- test/polytopes.jl | 25 +++-------- 5 files changed, 19 insertions(+), 86 deletions(-) diff --git a/docs/src/algorithms/orientation.md b/docs/src/algorithms/orientation.md index 818ee33f6..0f674ee01 100644 --- a/docs/src/algorithms/orientation.md +++ b/docs/src/algorithms/orientation.md @@ -3,7 +3,4 @@ ```@docs OrientationType orientation -OrientationMethod -WindingOrientation -TriangleOrientation ``` \ No newline at end of file diff --git a/src/Meshes.jl b/src/Meshes.jl index badd5dbc4..f23ddd359 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -401,9 +401,6 @@ export RIGHT, # orientation - OrientationMethod, - WindingOrientation, - TriangleOrientation, orientation, OrientationType, CW, diff --git a/src/discretization/held.jl b/src/discretization/held.jl index 9aeff4ff3..8f1e1420f 100644 --- a/src/discretization/held.jl +++ b/src/discretization/held.jl @@ -38,7 +38,7 @@ function _discretizewithin(ring::Ring, ::Val{2}, method::HeldTriangulation) earshuffle!(𝒬) = method.shuffle && shuffle!(method.rng, 𝒬) # input ring - O = orientation(ring, TriangleOrientation()) + O = orientation(ring) ℛ = O == CCW ? ring : reverse(ring) # standardize coordinates diff --git a/src/orientation.jl b/src/orientation.jl index fa959d61d..28c9b462b 100644 --- a/src/orientation.jl +++ b/src/orientation.jl @@ -14,77 +14,27 @@ Possible values are `CW` and `CCW`. end """ - OrientationMethod - -A method for finding the orientation of rings and polygons. -""" -abstract type OrientationMethod end - -""" - orientation(geom, [method]) + orientation(geom) Returns the orientation of the geometry `geom` as either counter-clockwise (CCW) or clockwise (CW). - -Optionally, specify the orientation `method`. - -See also [`WindingOrientation`](@ref), -[`TriangleOrientation`](@ref). """ function orientation end -orientation(p::Polygon) = orientation(p, WindingOrientation()) - -orientation(r::Ring) = orientation(r, WindingOrientation()) - -function orientation(p::Polygon, method) - o = [orientation(ring, method) for ring in rings(p)] +function orientation(p::Polygon) + o = [orientation(ring) for ring in rings(p)] hasholes(p) ? o : first(o) end -orientation(r::Ring, method) = _orientation(r, Val(embeddim(r)), method) - -_orientation(r::Ring, ::Val{3}, method) = orientation(proj2D(r), method) - -""" - WindingOrientation() - -A method for finding the orientatino of rings and polygons -based on the winding number. - -## References - -* Balbes, R. and Siegel, J. 1990. [A robust method for calculating - the simplicity and orientation of planar polygons] - (https://www.sciencedirect.com/science/article/abs/pii/0167839691900198) -""" -struct WindingOrientation <: OrientationMethod end - -function _orientation(r::Ring, ::Val{2}, ::WindingOrientation) - # pick any segment - p₁, p₂ = vertices(r)[1:2] - p̄ = center(Segment(p₁, p₂)) - w̄ = winding(p̄, r) - w = oftype(w̄, 2π) * w̄ - ∠(p₁, p̄, p₂) - isapproxequal(w, oftype(w, π)) ? CCW : CW -end - -""" - TriangleOrientation() - -A method for finding the orientation of rings and polygons -based on signed triangular areas. +orientation(r::Ring) = _orientation(r, Val(embeddim(r))) -## References - -* Held, M. 1998. [FIST: Fast Industrial-Strength Triangulation of Polygons] - (https://link.springer.com/article/10.1007/s00453-001-0028-4) -""" -struct TriangleOrientation <: OrientationMethod end +_orientation(r::Ring, ::Val{3}) = _orientation(proj2D(r), Val(2)) -function _orientation(r::Ring, ::Val{2}, ::TriangleOrientation) +function _orientation(r::Ring, ::Val{2}) + ℒ = lentype(r) v = vertices(r) - Δ(i) = signarea(v[1], v[i], v[i + 1]) - a = mapreduce(Δ, +, 2:(length(v) - 1)) - a ≥ zero(a) ? CCW : CW + n = nvertices(r) + A(i) = signarea(v[1], v[i], v[i + 1]) + Σ = sum(A, 2:(n - 1), init=zero(ℒ)^2) + Σ ≥ zero(Σ) ? CCW : CW end diff --git a/test/polytopes.jl b/test/polytopes.jl index d186a1fd9..c613dbd22 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -527,9 +527,7 @@ @test issimple(poly) @test boundary(poly) == first(rings(poly)) @test nvertices(poly) == 30 - for algo in [WindingOrientation(), TriangleOrientation()] - @test orientation(poly, algo) == CCW - end + @test orientation(poly) == CCW @test unique(poly) == poly end @@ -542,9 +540,7 @@ @test issimple(poly) @test boundary(poly) == first(rings(poly)) @test nvertices(poly) == 120 - for algo in [WindingOrientation(), TriangleOrientation()] - @test orientation(poly, algo) == CCW - end + @test orientation(poly) == CCW @test unique(poly) == poly end @@ -559,11 +555,9 @@ @test boundary(poly) == Multi(rs) @test nvertices(first(rs)) < 30 @test all(nvertices.(rs[2:end]) .< 18) - for algo in [WindingOrientation(), TriangleOrientation()] - orients = orientation(poly, algo) - @test orients[1] == CCW - @test all(orients[2:end] .== CW) - end + o = orientation(poly) + @test o[1] == CCW + @test all(o[2:end] .== CW) @test unique(poly) == poly end @@ -573,14 +567,9 @@ nb = nvertices(b) np = nvertices.(rings(poly)) @test nb ≥ sum(np) - # triangle orientation always works even + # orientation always works even # in the presence of self-intersections - @test orientation(b, TriangleOrientation()) == CCW - # winding orientation is only suitable - # for simple polygonal chains - # if issimple(b) - # @test orientation(b, WindingOrientation()) == CCW - # end + @test orientation(b) == CCW end # test uniqueness From 073fc6871d6ee6c8aa1a26223f49e8266c00890c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 23 Jul 2024 11:57:14 -0300 Subject: [PATCH 188/423] Improve docs --- docs/src/algorithms/orientation.md | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/src/algorithms/orientation.md b/docs/src/algorithms/orientation.md index 0f674ee01..a12e83150 100644 --- a/docs/src/algorithms/orientation.md +++ b/docs/src/algorithms/orientation.md @@ -1,6 +1,42 @@ # Orientation +```@example orientation +using Meshes # hide +import CairoMakie as Mke # hide +``` + +Many geometric processing algorithms for 2D geometries +rely on the concept of [`orientation`](@ref), which is +illustrated below. + ```@docs OrientationType orientation -``` \ No newline at end of file +``` + +For polygons without holes, the function returns the +orientation of the boundary, which is a [`Ring`](@ref): + +```@example orientation +tri = Triangle((0, 0), (1, 0), (0, 1)) + +orientation(tri) +``` + +```@example orientation +tri = Triangle((0, 0), (0, 1), (1, 0)) + +orientation(tri) +``` + +For polygons with holes, the function returns a +vector with the orientation of all constituent rings: + +```@example orientation +outer = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)] +hole1 = [(0.2,0.2),(0.4,0.2),(0.4,0.4),(0.2,0.4)] +hole2 = [(0.6,0.2),(0.8,0.2),(0.8,0.4),(0.6,0.4)] +poly = PolyArea([outer, hole1, hole2]) + +orientation(poly) +``` From 9704a3fca4159ee081bb917966b56637fcad8ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 23 Jul 2024 15:11:39 -0300 Subject: [PATCH 189/423] Implement partial order (#951) --- src/Meshes.jl | 5 +---- src/intersections/boxes.jl | 2 +- src/predicates/in.jl | 2 +- src/primitives/box.jl | 2 +- src/primitives/point.jl | 16 ++++++++-------- test/primitives.jl | 16 ++++++++-------- test/sampling.jl | 4 ++-- 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index f23ddd359..568901f2b 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -34,6 +34,7 @@ import Random import Base: sort import Base: ==, ! import Base: +, -, * +import Base: <, >, ≤, ≥ import StatsBase: sample import Distances: evaluate import NearestNeighbors: MinkowskiMetric @@ -182,10 +183,6 @@ export sides, diagonal, focallength, - ⪯, - ≺, - ⪰, - ≻, # polytopes Polytope, diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index f7419499d..a415a7e04 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -28,7 +28,7 @@ function intersection(f, box₁::Box, box₂::Box) elseif all(<(τ), δ̄) return @IT CornerTouching u f elseif any(<(τ), δ̄) && (δ == δ̄ || δ == -δ̄) - return @IT Touching (u ⪯ v ? Box(u, v) : Box(v, u)) f + return @IT Touching (u ≤ v ? Box(u, v) : Box(v, u)) f else return @IT NotIntersecting nothing f end diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 71bb4efa2..0b384f0e7 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -32,7 +32,7 @@ Base.in(p::Point, c::Chain) = any(s -> p ∈ s, segments(c)) Base.in(p::Point, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) -Base.in(p::Point, b::Box) = minimum(b) ⪯ p ⪯ maximum(b) +Base.in(p::Point, b::Box) = minimum(b) ≤ p ≤ maximum(b) function Base.in(p::Point, b::Ball) c = center(b) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index fef807585..b5e080854 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -20,7 +20,7 @@ struct Box{C<:CRS} <: Primitive{C} max::Point{C} function Box{C}(min, max) where {C<:CRS} - assertion(min ⪯ max, "`min` must be less than or equal to `max`") + assertion(min ≤ max, "`min` must be less than or equal to `max`") new(min, max) end end diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 186f98c01..a5c9b303b 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -95,17 +95,17 @@ at a reference (or start) point `A`. -(v::Vec, A::Point) = A - v """ - ⪯(A::Point, B::Point) - ⪰(A::Point, B::Point) - ≺(A::Point, B::Point) - ≻(A::Point, B::Point) + ≤(A::Point, B::Point) + ≥(A::Point, B::Point) + <(A::Point, B::Point) + >(A::Point, B::Point) Generalized inequality for non-negative orthant Rⁿ₊. """ -⪯(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) -⪰(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) -≺(A::Point, B::Point) = all(x -> x > zero(x), B - A) -≻(A::Point, B::Point) = all(x -> x > zero(x), A - B) +≤(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) +≥(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) +<(A::Point, B::Point) = all(x -> x > zero(x), B - A) +>(A::Point, B::Point) = all(x -> x > zero(x), A - B) """ ∠(A, B, C) diff --git a/test/primitives.jl b/test/primitives.jl index 064aae705..2bb7db6e5 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -100,14 +100,14 @@ @test p2 isa P # generalized inequality - @test cart(1, 1) ⪯ cart(1, 1) - @test !(cart(1, 1) ≺ cart(1, 1)) - @test cart(1, 2) ⪯ cart(3, 4) - @test cart(1, 2) ≺ cart(3, 4) - @test cart(1, 1) ⪰ cart(1, 1) - @test !(cart(1, 1) ≻ cart(1, 1)) - @test cart(3, 4) ⪰ cart(1, 2) - @test cart(3, 4) ≻ cart(1, 2) + @test cart(1, 1) ≤ cart(1, 1) + @test !(cart(1, 1) < cart(1, 1)) + @test cart(1, 2) ≤ cart(3, 4) + @test cart(1, 2) < cart(3, 4) + @test cart(1, 1) ≥ cart(1, 1) + @test !(cart(1, 1) > cart(1, 1)) + @test cart(3, 4) ≥ cart(1, 2) + @test cart(3, 4) > cart(1, 2) # center and centroid @test Meshes.center(cart(1, 1)) == cart(1, 1) diff --git a/test/sampling.jl b/test/sampling.jl index d46bb3c25..89fc59875 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -357,7 +357,7 @@ poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, HomogeneousSampling(100)) - @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) + @test all(p -> (cart(0, 0) ≤ p ≤ cart(1, 1)) || (cart(1, 1) ≤ p ≤ cart(2, 2)), ps) points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) @@ -375,7 +375,7 @@ poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, MinDistanceSampling(0.1)) - @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) + @test all(p -> (cart(0, 0) ≤ p ≤ cart(1, 1)) || (cart(1, 1) ≤ p ≤ cart(2, 2)), ps) points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) From 5c8f810df25b137c762cb48dfc0419cb37c15baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 24 Jul 2024 15:23:20 -0300 Subject: [PATCH 190/423] Add support for non-Euclidean manifolds (#947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add manifolds.jl * Cleanup imports * Add M paramters to all primitives * Flip C<>M in primitives.jl * Add M to polytopes.jl * Add M to multigeoms.jl * Add M to domains.jl * Additional fixes * More fixes * Fix meshes * Delay addition of deps * Add constructors for 'Point' * Apply suggestions * Update Primitives * Update Polytopes * Update src/primitives/paraboloidsurface.jl Co-authored-by: Júlio Hoffimann * Apply suggestions * Update 'axis' function * Update Domains * Add partial order for Ellipsoid manifold * Improve docstring for Box * Fix tests * Apply suggestions * Update tests * Update aliases * Add more type parameter restrictions * Update MeshesMakieExt * Apply suggestions * Apply suggestions * Apply suggestions * Apply suggestions * Update 'vizsubdom!' fallback * Update exports * Fix viz * Fix docs * Fix tests * Fix viz --------- Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Co-authored-by: Elias Carvalho --- ext/MeshesMakieExt.jl | 4 +- ext/fallbacks.jl | 6 +- ext/geometryset.jl | 47 +++++++-------- ext/grid.jl | 25 +++----- ext/grid/cartesian.jl | 4 +- ext/grid/rectilinear.jl | 4 +- ext/grid/structured.jl | 4 +- ext/grid/transformed.jl | 8 +-- ext/{simplemesh.jl => mesh.jl} | 16 ++++-- ext/subcartesiangrid.jl | 43 -------------- ext/subdomain.jl | 89 +++++++++++++++++++++++++++++ src/Meshes.jl | 9 +++ src/distances.jl | 28 ++++----- src/domains.jl | 24 +++++--- src/geometries.jl | 21 +++++-- src/manifolds.jl | 19 ++++++ src/mesh.jl | 22 +++---- src/mesh/cartesiangrid.jl | 10 ++-- src/mesh/rectilineargrid.jl | 2 +- src/mesh/simplemesh.jl | 2 +- src/mesh/structuredgrid.jl | 2 +- src/mesh/transformedmesh.jl | 6 +- src/multigeoms.jl | 14 ++--- src/polytopes.jl | 31 ++++++---- src/polytopes/ngon.jl | 10 ++-- src/polytopes/polyarea.jl | 16 +++--- src/polytopes/ring.jl | 6 +- src/polytopes/rope.jl | 2 +- src/primitives.jl | 4 +- src/primitives/ball.jl | 6 +- src/primitives/bezier.jl | 2 +- src/primitives/box.jl | 28 ++++++--- src/primitives/circle.jl | 2 +- src/primitives/cone.jl | 4 +- src/primitives/conesurface.jl | 4 +- src/primitives/cylinder.jl | 2 +- src/primitives/cylindersurface.jl | 2 +- src/primitives/disk.jl | 2 +- src/primitives/ellipsoid.jl | 8 +-- src/primitives/frustum.jl | 2 +- src/primitives/frustumsurface.jl | 2 +- src/primitives/line.jl | 6 +- src/primitives/paraboloidsurface.jl | 13 +++-- src/primitives/plane.jl | 4 +- src/primitives/point.jl | 28 +++++++-- src/primitives/ray.jl | 4 +- src/primitives/sphere.jl | 6 +- src/primitives/torus.jl | 8 +-- src/sets.jl | 6 +- src/subdomains.jl | 10 +++- src/trajecs.jl | 8 +-- test/boundingboxes.jl | 2 +- test/dummy.jl | 4 +- test/predicates.jl | 12 ++-- 54 files changed, 394 insertions(+), 259 deletions(-) rename ext/{simplemesh.jl => mesh.jl} (92%) delete mode 100644 ext/subcartesiangrid.jl create mode 100644 ext/subdomain.jl create mode 100644 src/manifolds.jl diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 7a03abd67..ac98d2511 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -43,10 +43,10 @@ include("colors.jl") include("utils.jl") # viz recipes +include("mesh.jl") include("grid.jl") -include("simplemesh.jl") -include("subcartesiangrid.jl") include("geometryset.jl") +include("subdomain.jl") include("vector.jl") include("fallbacks.jl") diff --git a/ext/fallbacks.jl b/ext/fallbacks.jl index 334defcb3..f266ca395 100644 --- a/ext/fallbacks.jl +++ b/ext/fallbacks.jl @@ -9,14 +9,12 @@ Makie.plottype(::AbstractVector{<:Vec}) = Viz{<:Tuple{AbstractVector{<:Vec}}} Makie.convert_arguments(::Type{<:Viz}, geom::Geometry) = (GeometrySet([geom]),) Makie.convert_arguments(::Type{<:Viz}, domain::Domain) = (GeometrySet(collect(domain)),) -Makie.convert_arguments(::Type{<:Viz}, mesh::Mesh) = (convert(SimpleMesh, mesh),) Makie.convert_arguments(::Type{<:Viz}, vec::Vec) = ([vec],) # skip conversion for these types +Makie.convert_arguments(::Type{<:Viz}, mesh::Mesh) = (mesh,) Makie.convert_arguments(::Type{<:Viz}, gset::GeometrySet) = (gset,) -Makie.convert_arguments(::Type{<:Viz}, mesh::SimpleMesh) = (mesh,) -Makie.convert_arguments(::Type{<:Viz}, grid::Grid) = (grid,) -Makie.convert_arguments(::Type{<:Viz}, grid::SubCartesianGrid) = (grid,) +Makie.convert_arguments(::Type{<:Viz}, subdom::SubDomain) = (subdom,) Makie.convert_arguments(::Type{<:Viz}, vecs::AbstractVector{<:Vec}) = (vecs,) # vector of geometries for convenience diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 23915bc3d..0bd387394 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -22,45 +22,42 @@ function Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) inds = Makie.@lift findall(g -> g isa G, $geoms) gvec = Makie.@lift collect(G, $geoms[$inds]) colors = Makie.@lift $colorant isa AbstractVector ? $colorant[$inds] : $colorant + M = Makie.@lift manifold(first($gvec)) pdim = Makie.@lift paramdim(first($gvec)) edim = Makie.@lift embeddim(first($gvec)) - vizgset!(plot, Val(pdim[]), Val(edim[]), gvec, colors) + vizgset!(plot, M[], Val(pdim[]), Val(edim[]), gvec, colors) end end -function Makie.plot!(plot::Viz{<:Tuple{PointSet}}) - pset = plot[:object] - color = plot[:color] - alpha = plot[:alpha] - colormap = plot[:colormap] - colorrange = plot[:colorrange] - pointsize = plot[:pointsize] - - # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - - # get geometries and coordinates - geoms = Makie.@lift parent($pset) - coords = Makie.@lift map(g -> ustrip.(to(g)), $geoms) +const ObservableVector{T} = Makie.Observable{<:AbstractVector{T}} - # visualize point set - Makie.scatter!(plot, coords, color=colorant, markersize=pointsize, overdraw=true) +function vizgset!(plot, ::Type{<:🌐}, pdim::Val, edim::Val, geoms, colorant) + @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." + vizgset!(plot, 𝔼, pdim, edim, geoms, colorant) end -const ObservableVector{T} = Makie.Observable{<:AbstractVector{T}} - -function vizgset!(plot, ::Val{0}, ::Val, geoms, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms, colorant) points = Makie.@lift pointify.($geoms) vizmany!(plot, points, colorant) end -function vizgset!(plot, ::Val{1}, ::Val, geoms, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms::ObservableVector{<:Point}, colorant) + pointsize = plot[:pointsize] + + # get coordinates + coords = Makie.@lift map(p -> ustrip.(to(p)), $geoms) + + # visualize points + Makie.scatter!(plot, coords, color=colorant, markersize=pointsize, overdraw=true) +end + +function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) showfacets1D!(plot, geoms) end -function vizgset!(plot, ::Val{1}, ::Val, geoms::ObservableVector{<:Ray}, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms::ObservableVector{<:Ray}, colorant) rset = plot[:object] segmentsize = plot[:segmentsize] @@ -77,7 +74,7 @@ function vizgset!(plot, ::Val{1}, ::Val, geoms::ObservableVector{<:Ray}, coloran showfacets1D!(plot, geoms) end -function vizgset!(plot, ::Val{2}, ::Val, geoms, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) showfacets2D!(plot, geoms) @@ -85,7 +82,7 @@ end const PolygonLike = Union{Polygon,MultiPolygon} -function vizgset!(plot, ::Val{2}, ::Val{2}, geoms::ObservableVector{<:PolygonLike}, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val{2}, geoms::ObservableVector{<:PolygonLike}, colorant) showsegments = plot[:showsegments] segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] @@ -102,7 +99,7 @@ function vizgset!(plot, ::Val{2}, ::Val{2}, geoms::ObservableVector{<:PolygonLik end end -function vizgset!(plot, ::Val{3}, ::Val, geoms, colorant) +function vizgset!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.(boundary.($geoms)) vizmany!(plot, meshes, colorant) end diff --git a/ext/grid.jl b/ext/grid.jl index d12373a1a..ba74a9ae0 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -4,36 +4,27 @@ function Makie.plot!(plot::Viz{<:Tuple{Grid}}) grid = plot[:object] + M = Makie.@lift manifold($grid) pdim = Makie.@lift paramdim($grid) edim = Makie.@lift embeddim($grid) - vizgrid!(plot, Val(pdim[]), Val(edim[])) + vizgrid!(plot, M[], Val(pdim[]), Val(edim[])) end -vizgrid!(plot, ::Val{2}, ::Val{2}) = vizmesh!(plot, Val(2), Val(2)) - -function vizgrid!(plot, ::Val{3}, ::Val{3}) - grid = plot[:object] - color = plot[:color] - - # number of vertices and colors - nv = Makie.@lift nvertices($grid) - nc = Makie.@lift $color isa AbstractVector ? length($color) : 1 - - if nv[] == nc[] - error("not implemented") - else - vizmesh!(plot, Val(3), Val(3)) - end +function vizgrid!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) + @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." + vizgrid!(plot, 𝔼, pdim, edim) end +vizgrid!(plot, M::Type{<:𝔼}, pdim::Val, edim::Val) = vizmesh!(plot, M, pdim, edim) + # ---------------- # SPECIALIZATIONS # ---------------- include("grid/cartesian.jl") include("grid/rectilinear.jl") -include("grid/transformed.jl") include("grid/structured.jl") +include("grid/transformed.jl") # helper functions to create a minimum number # of line segments within Cartesian/Rectilinear grid diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index da72c628b..8120c50c4 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Val{2}, ::Val{2}) +function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{2}, ::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -61,7 +61,7 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Val{2}, ::Val{2}) end end -function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Val{3}, ::Val{3}) +function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{3}, ::Val{3}) # retrieve parameters grid = plot[:object] color = plot[:color] diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index f388de1a9..32b862c48 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, ::Val{2}, ::Val{2}) +function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -33,7 +33,7 @@ function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, ::Val{2}, ::Val{2}) if nc[] == nv[] # visualize as a simple mesh so that # colors can be specified at vertices - vizmesh!(plot, Val(2), Val(2)) + vizmesh!(plot, M, pdim, edim) else # visualize as built-in heatmap sz = Makie.@lift size($grid) diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 7e35d6bf6..912a96176 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, ::Val{2}, ::Val{2}) +function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) grid = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -36,7 +36,7 @@ function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, ::Val{2}, ::Val{2}) Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) end else - vizmesh!(plot, Val(2), Val(2)) + vizmesh!(plot, M, pdim, edim) end end diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 257fa15f8..43743bd1d 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -14,11 +14,11 @@ function isoptimized(t::Affine{2}) isdiag(A) || isrotation(A) end -vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, ::Val{2}, ::Val{2}) = - transformedgrid!(plot, plot -> vizmesh!(plot, Val(2), Val(2))) +vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) = + transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) -vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, ::Val{3}, ::Val{3}) = - transformedgrid!(plot, plot -> vizmesh!(plot, Val(3), Val(3))) +vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{3}, edim::Val{3}) = + transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) function transformedgrid!(plot, fallback) tgrid = plot[:object] diff --git a/ext/simplemesh.jl b/ext/mesh.jl similarity index 92% rename from ext/simplemesh.jl rename to ext/mesh.jl index 1e95956fe..4714dbdfd 100644 --- a/ext/simplemesh.jl +++ b/ext/mesh.jl @@ -2,15 +2,21 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function Makie.plot!(plot::Viz{<:Tuple{SimpleMesh}}) +function Makie.plot!(plot::Viz{<:Tuple{Mesh}}) # retrieve mesh and dimensions mesh = plot[:object] + M = Makie.@lift manifold($mesh) pdim = Makie.@lift paramdim($mesh) edim = Makie.@lift embeddim($mesh) - vizmesh!(plot, Val(pdim[]), Val(edim[])) + vizmesh!(plot, M[], Val(pdim[]), Val(edim[])) end -function vizmesh!(plot, ::Val{1}, ::Val) +function vizmesh!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) + @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." + vizmesh!(plot, 𝔼, pdim, edim) +end + +function vizmesh!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -42,7 +48,7 @@ function vizmesh!(plot, ::Val{1}, ::Val) Makie.lines!(plot, coords, color=colors, linewidth=segmentsize) end -function vizmesh!(plot, ::Val{2}, ::Val) +function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) mesh = plot[:object] color = plot[:color] alpha = plot[:alpha] @@ -181,7 +187,7 @@ function vizmesh!(plot, ::Val{2}, ::Val) end end -function vizmesh!(plot, ::Val{3}, ::Val) +function vizmesh!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val) mesh = plot[:object] color = plot[:color] meshes = Makie.@lift let diff --git a/ext/subcartesiangrid.jl b/ext/subcartesiangrid.jl deleted file mode 100644 index d999570ec..000000000 --- a/ext/subcartesiangrid.jl +++ /dev/null @@ -1,43 +0,0 @@ -# ------------------------------------------------------------------ -# Licensed under the MIT License. See LICENSE in the project root. -# ------------------------------------------------------------------ - -const SubCartesianGrid{CRS} = SubDomain{CRS,<:CartesianGrid{CRS}} - -function Makie.plot!(plot::Viz{<:Tuple{SubCartesianGrid}}) - subgrid = plot[:object] - color = plot[:color] - alpha = plot[:alpha] - colormap = plot[:colormap] - colorrange = plot[:colorrange] - - # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - - # retrieve grid paramaters - gparams = Makie.@lift let - grid = parent($subgrid) - dim = embeddim(grid) - sp = ustrip.(spacing(grid)) - - # coordinates of centroids - coord(e) = ustrip.(to(centroid(e))) - coords = [coord(e) .+ sp ./ 2 for e in $subgrid] - - # rectangle marker - marker = Makie.Rect{dim}(-1 .* sp, sp) - - # enable shading in 3D - shading = dim == 3 ? Makie.FastShading : Makie.NoShading - - coords, marker, shading - end - - # unpack observable parameters - coords = Makie.@lift $gparams[1] - marker = Makie.@lift $gparams[2] - shading = Makie.@lift $gparams[3] - - # all geometries are equal, use mesh scatter - Makie.meshscatter!(plot, coords, marker=marker, markersize=1, color=colorant, shading=shading) -end diff --git a/ext/subdomain.jl b/ext/subdomain.jl new file mode 100644 index 000000000..452f6b59e --- /dev/null +++ b/ext/subdomain.jl @@ -0,0 +1,89 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +function Makie.plot!(plot::Viz{<:Tuple{SubDomain}}) + subdom = plot[:object] + M = Makie.@lift manifold($subdom) + pdim = Makie.@lift paramdim($subdom) + edim = Makie.@lift embeddim($subdom) + vizsubdom!(plot, M[], Val(pdim[]), Val(edim[])) +end + +function vizsubdom!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) + @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." + vizsubdom!(plot, 𝔼, pdim, edim) +end + +function vizsubdom!(plot, ::Type{<:𝔼}, ::Val, ::Val) + subdom = plot[:object] + color = plot[:color] + alpha = plot[:alpha] + colormap = plot[:colormap] + colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + showpoints = plot[:showpoints] + pointcolor = plot[:pointcolor] + pointsize = plot[:pointsize] + + # construct the geometry set + gset = Makie.@lift GeometrySet(collect($subdom)) + + # forward attributes + viz!( + plot, + gset; + color, + alpha, + colormap, + colorrange, + showsegments, + segmentcolor, + segmentsize, + showpoints, + pointcolor, + pointsize + ) +end + +const SubCartesianGrid{M,CRS} = SubDomain{M,CRS,<:CartesianGrid} + +function vizsubdom!(plot::Viz{<:Tuple{SubCartesianGrid}}, ::Type{<:𝔼}, ::Val, ::Val) + subgrid = plot[:object] + color = plot[:color] + alpha = plot[:alpha] + colormap = plot[:colormap] + colorrange = plot[:colorrange] + + # process color spec into colorant + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + + # retrieve grid paramaters + gparams = Makie.@lift let + grid = parent($subgrid) + dim = embeddim(grid) + sp = ustrip.(spacing(grid)) + + # coordinates of centroids + coord(e) = ustrip.(to(centroid(e))) + coords = [coord(e) .+ sp ./ 2 for e in $subgrid] + + # rectangle marker + marker = Makie.Rect{dim}(-1 .* sp, sp) + + # enable shading in 3D + shading = dim == 3 ? Makie.FastShading : Makie.NoShading + + coords, marker, shading + end + + # unpack observable parameters + coords = Makie.@lift $gparams[1] + marker = Makie.@lift $gparams[2] + shading = Makie.@lift $gparams[3] + + # all geometries are equal, use mesh scatter + Makie.meshscatter!(plot, coords, marker=marker, markersize=1, color=colorant, shading=shading) +end diff --git a/src/Meshes.jl b/src/Meshes.jl index 568901f2b..3315fe9dc 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -60,6 +60,9 @@ include("tolerances.jl") # basic vector type include("vectors.jl") +# manifold types +include("manifolds.jl") + # geometries include("geometries.jl") @@ -135,11 +138,16 @@ export ⋅, ×, + # manifolds + 𝔼, + 🌐, + # geometries Geometry, embeddim, paramdim, crs, + manifold, center, centroid, @@ -277,6 +285,7 @@ export embeddim, paramdim, crs, + manifold, element, nelements, diff --git a/src/distances.jl b/src/distances.jl index a2ac5f8a7..64b799005 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -53,27 +53,27 @@ end # SPECIAL CASES # -------------- -function evaluate(d::Haversine, p₁::Point{<:LatLon}, p₂::Point{<:LatLon}) +evaluate(d::Haversine, p₁::Point, p₂::Point) = _evaluate(d, coords(p₁), coords(p₂)) + +function _evaluate(d::Haversine, coords₁::LatLon, coords₂::LatLon) uᵣ = unit(d.radius) # add default unit if necessary u = uᵣ === NoUnits ? u"m" : NoUnits - latlon₁ = coords(p₁) - latlon₂ = coords(p₂) - v₁ = SVector(latlon₁.lon, latlon₁.lat) - v₂ = SVector(latlon₂.lon, latlon₂.lat) + v₁ = SVector(coords₁.lon, coords₁.lat) + v₂ = SVector(coords₂.lon, coords₂.lat) evaluate(d, v₁, v₂) * u end -evaluate(d::Haversine, p₁::Point, p₂::Point) = - evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) +_evaluate(d::Haversine, coords₁::CRS, coords₂::CRS) = + _evaluate(d, convert(LatLon, coords₁), convert(LatLon, coords₂)) + +evaluate(d::SphericalAngle, p₁::Point, p₂::Point) = _evaluate(d, coords(p₁), coords(p₂)) -function evaluate(d::SphericalAngle, p₁::Point{<:LatLon}, p₂::Point{<:LatLon}) - latlon₁ = coords(p₁) - latlon₂ = coords(p₂) - v₁ = SVector(deg2rad(latlon₁.lon), deg2rad(latlon₁.lat)) - v₂ = SVector(deg2rad(latlon₂.lon), deg2rad(latlon₂.lat)) +function _evaluate(d::SphericalAngle, coords₁::LatLon, coords₂::LatLon) + v₁ = SVector(deg2rad(coords₁.lon), deg2rad(coords₁.lat)) + v₂ = SVector(deg2rad(coords₂.lon), deg2rad(coords₂.lat)) evaluate(d, v₁, v₂) * u"rad" end -evaluate(d::SphericalAngle, p₁::Point, p₂::Point) = - evaluate(d, Point(convert(LatLon, coords(p₁))), Point(convert(LatLon, coords(p₂)))) +_evaluate(d::SphericalAngle, coords₁::CRS, coords₂::CRS) = + _evaluate(d, convert(LatLon, coords₁), convert(LatLon, coords₂)) diff --git a/src/domains.jl b/src/domains.jl index a8a8059f8..4b852c289 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -3,11 +3,13 @@ # ------------------------------------------------------------------ """ - Domain{CRS} + Domain{M,CRS} -A domain is an indexable collection of geometries (e.g. mesh). +A domain is an indexable collection of geometries (e.g. mesh) +in a given manifold `M` with point coordinates specified in a +coordinate reference system `CRS`. """ -abstract type Domain{CRS} end +abstract type Domain{M<:AbstractManifold,C<:CRS} end """ element(domain, ind) @@ -60,7 +62,7 @@ Base.vcat(ds::Domain...) = reduce(vcat, ds) Return the number of dimensions of the space where the `domain` is embedded. """ -embeddim(::Type{<:Domain{CRS}}) where {CRS} = CoordRefSystems.ndims(CRS) +embeddim(::Type{<:Domain{M,CRS}}) where {M,CRS} = CoordRefSystems.ndims(CRS) embeddim(d::Domain) = embeddim(typeof(d)) """ @@ -76,15 +78,23 @@ paramdim(d::Domain) = paramdim(first(d)) Return the coordinate reference system (CRS) of the `domain`. """ -crs(::Type{<:Domain{CRS}}) where {CRS} = CRS +crs(::Type{<:Domain{M,CRS}}) where {M,CRS} = CRS crs(d::Domain) = crs(typeof(d)) +""" + manifold(domain) + +Return the manifold where the `domain` is defined. +""" +manifold(::Type{<:Domain{M,CRS}}) where {M,CRS} = M +manifold(d::Domain) = manifold(typeof(d)) + """ lentype(domain) Return the length type of the `domain`. """ -lentype(::Type{<:Domain{CRS}}) where {CRS} = lentype(CRS) +lentype(::Type{<:Domain{M,CRS}}) where {M,CRS} = lentype(CRS) lentype(d::Domain) = lentype(typeof(d)) """ @@ -147,10 +157,10 @@ end # IMPLEMENTATIONS # ---------------- -include("subdomains.jl") include("sets.jl") include("mesh.jl") include("trajecs.jl") +include("subdomains.jl") # ------------ # CONVERSIONS diff --git a/src/geometries.jl b/src/geometries.jl index 358d5b58d..cdf10717e 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -3,11 +3,12 @@ # ------------------------------------------------------------------ """ - Geometry{CRS} + Geometry{M,CRS} -A geometry with given coordinate reference system `CRS`. +A geometry in a given manifold `M` with point coordinates specified +in a coordinate reference system `CRS`. """ -abstract type Geometry{CRS} end +abstract type Geometry{M<:AbstractManifold,C<:CRS} end Broadcast.broadcastable(g::Geometry) = Ref(g) @@ -16,7 +17,7 @@ Broadcast.broadcastable(g::Geometry) = Ref(g) Return the number of dimensions of the space where the `geometry` is embedded. """ -embeddim(::Type{<:Geometry{CRS}}) where {CRS} = CoordRefSystems.ndims(CRS) +embeddim(::Type{<:Geometry{M,CRS}}) where {M,CRS} = CoordRefSystems.ndims(CRS) embeddim(g::Geometry) = embeddim(typeof(g)) """ @@ -34,15 +35,23 @@ paramdim(g::Geometry) = paramdim(typeof(g)) Return the coordinate reference system (CRS) of the `geometry`. """ -crs(::Type{<:Geometry{CRS}}) where {CRS} = CRS +crs(::Type{<:Geometry{M,CRS}}) where {M,CRS} = CRS crs(g::Geometry) = crs(typeof(g)) +""" + manifold(geometry) + +Return the manifold where the `geometry` is defined. +""" +manifold(::Type{<:Geometry{M,CRS}}) where {M,CRS} = M +manifold(g::Geometry) = manifold(typeof(g)) + """ lentype(geometry) Return the length type of the `geometry`. """ -lentype(::Type{<:Geometry{CRS}}) where {CRS} = lentype(CRS) +lentype(::Type{<:Geometry{M,CRS}}) where {M,CRS} = lentype(CRS) lentype(g::Geometry) = lentype(typeof(g)) """ diff --git a/src/manifolds.jl b/src/manifolds.jl new file mode 100644 index 000000000..f7e515a44 --- /dev/null +++ b/src/manifolds.jl @@ -0,0 +1,19 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +abstract type AbstractManifold end + +""" + 𝔼{Dim} + +Euclidean manifold with dimension `Dim`. +""" +abstract type 𝔼{Dim} <: AbstractManifold end + +""" + 🌐 + +Ellipsoid manifold for geodesic geometry. +""" +abstract type 🌐 <: AbstractManifold end diff --git a/src/mesh.jl b/src/mesh.jl index aad22f692..ea107cca7 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -3,11 +3,13 @@ # ------------------------------------------------------------------ """ - Mesh{CRS,TP} + Mesh{M,CRS,TP} -A mesh with given coordinate reference system `CRS` and topology of type `TP`. +A mesh of geometries in a given manifold `M` with point coordinates specified +in a coordinate reference system `CRS`. Unlike a general domain, a mesh has a +well-defined topology `TP`. """ -abstract type Mesh{CRS,TP<:Topology} <: Domain{CRS} end +abstract type Mesh{M<:AbstractManifold,C<:CRS,TP<:Topology} <: Domain{M,C} end """ vertex(mesh, ind) @@ -138,18 +140,12 @@ function Base.show(io::IO, ::MIME"text/plain", m::Mesh) end """ - Grid{CRS,Dim} + Grid{M,CRS,Dim} -A grid with given coordinate reference system `CRS` embedded in a `Dim`-dimensional space. +A grid of geometries in a given manifold `M` with points coordinates specified +in a coordinate reference system `CRS`, which is embedded in `Dim` dimensions. """ -const Grid{CRS,Dim} = Mesh{CRS,GridTopology{Dim}} - -""" - SubGrid{CRS,Dim} - -A view of a grid with given coordinate reference system `CRS` embedded in a `Dim`-dimensinoal space. -""" -const SubGrid{CRS,Dim} = SubDomain{CRS,<:Grid{CRS,Dim}} +const Grid{M<:AbstractManifold,C<:CRS,Dim} = Mesh{M,C,GridTopology{Dim}} """ vertex(grid, ijk) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index c34f75c83..89ff2190f 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -50,22 +50,22 @@ Create a 1D grid from -1 to 1 with 100 segments: julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` """ -struct CartesianGrid{C<:CRS,Dim,ℒ<:Len} <: Grid{C,Dim} - origin::Point{C} +struct CartesianGrid{C<:CRS,Mₚ<:AbstractManifold,Dim,ℒ<:Len} <: Grid{𝔼{Dim},C,Dim} + origin::Point{Mₚ,C} spacing::NTuple{Dim,ℒ} offset::Dims{Dim} topology::GridTopology{Dim} function CartesianGrid( - origin::Point{C}, + origin::Point{Mₚ,C}, spacing::NTuple{Dim,ℒ}, offset::Dims{Dim}, topology::GridTopology{Dim} - ) where {C<:CRS,Dim,ℒ<:Len} + ) where {C<:CRS,Mₚ<:AbstractManifold,Dim,ℒ<:Len} if !all(>(zero(ℒ)), spacing) throw(ArgumentError("spacing must be positive")) end - new{C,Dim,float(ℒ)}(origin, spacing, offset, topology) + new{C,Mₚ,Dim,float(ℒ)}(origin, spacing, offset, topology) end end diff --git a/src/mesh/rectilineargrid.jl b/src/mesh/rectilineargrid.jl index 87f93e478..d8cb57c3f 100644 --- a/src/mesh/rectilineargrid.jl +++ b/src/mesh/rectilineargrid.jl @@ -20,7 +20,7 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{Cartesian{Datum,Dim,ℒ},Dim} +struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim} xyz::NTuple{Dim,V} topology::GridTopology{Dim} diff --git a/src/mesh/simplemesh.jl b/src/mesh/simplemesh.jl index 37c39a65e..ca966424a 100644 --- a/src/mesh/simplemesh.jl +++ b/src/mesh/simplemesh.jl @@ -31,7 +31,7 @@ See also [`Topology`](@ref), [`GridTopology`](@ref), of the mesh to a [`HalfEdgeTopology`](@ref) instead of a [`SimpleTopology`](@ref). """ -struct SimpleMesh{C<:CRS,V<:AbstractVector{Point{C}},TP<:Topology} <: Mesh{C,TP} +struct SimpleMesh{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}},TP<:Topology} <: Mesh{M,C,TP} vertices::V topology::TP end diff --git a/src/mesh/structuredgrid.jl b/src/mesh/structuredgrid.jl index b8a512c3c..24e0ecfc3 100644 --- a/src/mesh/structuredgrid.jl +++ b/src/mesh/structuredgrid.jl @@ -20,7 +20,7 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{Cartesian{Datum,Dim,ℒ},Dim} +struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim} XYZ::NTuple{Dim,A} topology::GridTopology{Dim} diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 93515e709..753716ac0 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,8 +7,8 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{C<:CRS,TP<:Topology,M<:Mesh{C,TP},TR<:Transform} <: Mesh{C,TP} - mesh::M +struct TransformedMesh{M<:AbstractManifold,C<:CRS,TP<:Topology,MS<:Mesh{M,C,TP},TR<:Transform} <: Mesh{M,C,TP} + mesh::MS transform::TR end @@ -24,7 +24,7 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{CRS,Dim,G<:Grid{CRS,Dim},TR} = TransformedMesh{CRS,GridTopology{Dim},G,TR} +const TransformedGrid{M<:AbstractManifold,C<:CRS,Dim,G<:Grid{M,C,Dim},TR} = TransformedMesh{M,C,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) diff --git a/src/multigeoms.jl b/src/multigeoms.jl index b4b6e7c23..169a38efb 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -15,7 +15,7 @@ multiple polygons as a single entity (e.g. country with islands). - Type aliases are [`MultiPoint`](@ref), [`MultiSegment`](@ref), [`MultiRope`](@ref), [`MultiRing`](@ref), [`MultiPolygon`](@ref). """ -struct Multi{C<:CRS,G<:Geometry{C}} <: Geometry{C} +struct Multi{M<:AbstractManifold,C<:CRS,G<:Geometry{M,C}} <: Geometry{M,C} geoms::Vector{G} end @@ -23,12 +23,12 @@ end Multi(geoms) = Multi(collect(geoms)) # type aliases for convenience -const MultiPoint{CRS} = Multi{CRS,<:Point{CRS}} -const MultiSegment{CRS} = Multi{CRS,<:Segment{CRS}} -const MultiRope{CRS} = Multi{CRS,<:Rope{CRS}} -const MultiRing{CRS} = Multi{CRS,<:Ring{CRS}} -const MultiPolygon{CRS} = Multi{CRS,<:Polygon{CRS}} -const MultiPolyhedron{CRS} = Multi{CRS,<:Polyhedron{CRS}} +const MultiPoint{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Point{M,C}} +const MultiSegment{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Segment{M,C}} +const MultiRope{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Rope{M,C}} +const MultiRing{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Ring{M,C}} +const MultiPolygon{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Polygon{M,C}} +const MultiPolyhedron{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Polyhedron{M,C}} paramdim(m::Multi) = maximum(paramdim, m.geoms) diff --git a/src/polytopes.jl b/src/polytopes.jl index d9129dca3..4bc45111d 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -3,15 +3,13 @@ # ------------------------------------------------------------------ """ - Polytope{K,CRS} + Polytope{K,M,CRS} We say that a geometry is a K-polytope when it is a collection of "flat" sides that constitute a `K`-dimensional subspace. They are called chain, polygon and polyhedron respectively for 1D (`K=1`), 2D (`K=2`) and 3D (`K=3`) subspaces. The parameter `K` is also known as the rank or parametric dimension of the polytope (). -The vertices are stored with coordinates in a given coordinate -reference system `CRS`. The term polytope expresses a particular combinatorial structure. A polyhedron, for example, can be decomposed into faces. Each face can then be decomposed into @@ -27,18 +25,31 @@ have (K-1)-polytopes in common. See . - Type aliases are `Chain`, `Polygon`, `Polyhedron`. """ -abstract type Polytope{K,CRS} <: Geometry{CRS} end +abstract type Polytope{K,M<:AbstractManifold,C<:CRS} <: Geometry{M,C} end # heper macro to define polytopes macro polytope(type, K, N) - expr = quote - $Base.@__doc__ struct $type{C<:CRS} <: Polytope{$K,C} - vertices::NTuple{$N,Point{C}} + structexpr = if K == 3 + quote + struct $type{C<:CRS,Mₚ<:AbstractManifold} <: Polytope{$K,𝔼{3},C} + vertices::NTuple{$N,Point{Mₚ,C}} + end + end + else + quote + struct $type{M<:AbstractManifold,C<:CRS} <: Polytope{$K,M,C} + vertices::NTuple{$N,Point{M,C}} + end end + end + + expr = quote + $Base.@__doc__ $structexpr $type(vertices::Vararg{Tuple,$N}) = $type(Point.(vertices)) $type(vertices::Vararg{P,$N}) where {P<:Point} = $type(vertices) end + esc(expr) end @@ -47,7 +58,7 @@ end # ------------------- """ - Chain{CRS} + Chain{M,CRS} A chain is a 1-polytope, i.e. a polytope with parametric dimension 1. See . @@ -154,7 +165,7 @@ include("polytopes/ring.jl") # --------------------- """ - Polygon{CRS} + Polygon{M,CRS} A polygon is a 2-polytope, i.e. a polytope with parametric dimension 2. @@ -193,7 +204,7 @@ include("polytopes/polyarea.jl") # ------------------------ """ - Polyhedron{CRS} + Polyhedron{M,CRS} A polyhedron is a 3-polytope, i.e. a polytope with parametric dimension 3. diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 03e66a367..27f2a41c3 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -20,9 +20,9 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. - Type aliases are `Triangle`, `Quadrangle`, `Pentagon`, `Hexagon`, `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ -struct Ngon{N,C<:CRS} <: Polygon{C} - vertices::NTuple{N,Point{C}} - function Ngon{N,C}(vertices) where {N,C<:CRS} +struct Ngon{N,M<:AbstractManifold,C<:CRS} <: Polygon{M,C} + vertices::NTuple{N,Point{M,C}} + function Ngon{N,M,C}(vertices) where {N,M<:AbstractManifold,C<:CRS} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) end @@ -30,11 +30,11 @@ struct Ngon{N,C<:CRS} <: Polygon{C} end end -Ngon{N}(vertices::NTuple{N,Point{C}}) where {N,C<:CRS} = Ngon{N,C}(vertices) +Ngon{N}(vertices::NTuple{N,Point{M,C}}) where {N,M<:AbstractManifold,C<:CRS} = Ngon{N,M,C}(vertices) Ngon{N}(vertices::Vararg{P,N}) where {N,P<:Point} = Ngon{N}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,Point{C}}) where {N,C<:CRS} = Ngon{N,C}(vertices) +Ngon(vertices::NTuple{N,Point{M,C}}) where {N,M<:AbstractManifold,C<:CRS} = Ngon{N,M,C}(vertices) Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 10dbe9b79..4fac1a9cb 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -24,10 +24,10 @@ in the real world, including issues with: * `degeneracy` - Sometimes data is shared with degenerate rings (e.g. only 2 vertices). """ -struct PolyArea{C<:CRS,R<:Ring{C}} <: Polygon{C} - rings::Vector{R} +struct PolyArea{M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} <: Polygon{M,C} + rings::V - function PolyArea{C,R}(rings; fix=true) where {C<:CRS,R<:Ring{C}} + function PolyArea{M,C,R,V}(rings; fix=true) where {M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} if isempty(rings) throw(ArgumentError("cannot create PolyArea without rings")) end @@ -43,10 +43,9 @@ struct PolyArea{C<:CRS,R<:Ring{C}} <: Polygon{C} # fix degeneracy if nvertices(outer) == 2 - v = vertices(outer) - A, B = v[1], v[2] - M = center(Segment(A, B)) - outer = Ring(A, M, B) + A, B = vertices(outer) + P = center(Segment(A, B)) + outer = Ring(A, P, B) end inners = filter(r -> nvertices(r) > 2, inners) @@ -57,7 +56,8 @@ struct PolyArea{C<:CRS,R<:Ring{C}} <: Polygon{C} end end -PolyArea(rings::AbstractVector{R}; fix=true) where {C<:CRS,R<:Ring{C}} = PolyArea{C,R}(rings; fix) +PolyArea(rings::V; fix=true) where {M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} = + PolyArea{M,C,R,V}(rings; fix) PolyArea(vertices::AbstractVector{<:AbstractVector}; fix=true) = PolyArea([Ring(v) for v in vertices]; fix) diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 3d9d9de95..54ef6bcd8 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -9,10 +9,10 @@ A closed polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Rope`](@ref). """ -struct Ring{C<:CRS,V<:CircularVector{Point{C}}} <: Chain{C} +struct Ring{M<:AbstractManifold,C<:CRS,V<:CircularVector{Point{M,C}}} <: Chain{M,C} vertices::V - function Ring{C,V}(vertices) where {C<:CRS,V<:CircularVector{Point{C}}} + function Ring{M,C,V}(vertices) where {M<:AbstractManifold,C<:CRS,V<:CircularVector{Point{M,C}}} if first(vertices) == last(vertices) && length(vertices) ≥ 2 throw(ArgumentError(""" First and last vertices of `Ring` constructor must be different @@ -24,7 +24,7 @@ struct Ring{C<:CRS,V<:CircularVector{Point{C}}} <: Chain{C} end end -Ring(vertices::CircularVector{Point{C}}) where {C<:CRS} = Ring{C,typeof(vertices)}(vertices) +Ring(vertices::CircularVector{Point{M,C}}) where {M<:AbstractManifold,C<:CRS} = Ring{M,C,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index 6f53089e4..a346d057d 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -9,7 +9,7 @@ An open polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Ring`](@ref). """ -struct Rope{C<:CRS,V<:AbstractVector{Point{C}}} <: Chain{C} +struct Rope{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Chain{M,C} vertices::V end diff --git a/src/primitives.jl b/src/primitives.jl index 8b7b8b09a..a1a3638f6 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -3,14 +3,14 @@ # ------------------------------------------------------------------ """ - Primitive{CRS} + Primitive{M,CRS} We say that a geometry is a primitive when it can be expressed as a single entity with no parts (a.k.a. atomic). For example, a sphere is a primitive described in terms of a mathematical expression involving a metric and a radius. See . """ -abstract type Primitive{CRS} <: Geometry{CRS} end +abstract type Primitive{M<:AbstractManifold,C<:CRS} <: Geometry{M,C} end function Base.show(io::IO, geom::Primitive) name = prettyname(geom) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index 67bfc9ab0..144420c20 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -9,10 +9,10 @@ A ball with `center` and `radius`. See also [`Sphere`](@ref). """ -struct Ball{C<:CRS,ℒ<:Len} <: Primitive{C} - center::Point{C} +struct Ball{M<:AbstractManifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} + center::Point{M,C} radius::ℒ - Ball(center::Point{C}, radius::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(center, radius) + Ball(center::Point{M,C}, radius::ℒ) where {M<:AbstractManifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) end Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 3cc5506dd..722200f2d 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -20,7 +20,7 @@ large number of points but less precise, can be used via BezierCurve([(0.,0.),(1.,-1.)]) ``` """ -struct BezierCurve{C<:CRS,V<:AbstractVector{Point{C}}} <: Primitive{C} +struct BezierCurve{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Primitive{M,C} controls::V end diff --git a/src/primitives/box.jl b/src/primitives/box.jl index b5e080854..ce5559ffd 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -5,27 +5,39 @@ """ Box(min, max) -An axis-aligned box with `min` and `max` corners. -See . +A (geodesic) box with `min` and `max` points on a given manifold. ## Examples +Construct a 3D box using points with Cartesian coordinates: + +```julia +Box((0, 0, 0), (1, 1, 1)) +``` + +Likewise, construct a 2D box on the plane: + ```julia -Box(Point(0, 0, 0), Point(1, 1, 1)) Box((0, 0), (1, 1)) ``` + +Construct a geodesic box on the ellipsoid: + +```julia +Box(Point(LatLon(0, 0)), Point(LatLon(1, 1))) +``` """ -struct Box{C<:CRS} <: Primitive{C} - min::Point{C} - max::Point{C} +struct Box{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} + min::Point{M,C} + max::Point{M,C} - function Box{C}(min, max) where {C<:CRS} + function Box{M,C}(min, max) where {M<:AbstractManifold,C<:CRS} assertion(min ≤ max, "`min` must be less than or equal to `max`") new(min, max) end end -Box(min::Point{C}, max::Point{C}) where {C<:CRS} = Box{C}(min, max) +Box(min::Point{M,C}, max::Point{M,C}) where {M<:AbstractManifold,C<:CRS} = Box{M,C}(min, max) Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) diff --git a/src/primitives/circle.jl b/src/primitives/circle.jl index be14b5517..81a42af01 100644 --- a/src/primitives/circle.jl +++ b/src/primitives/circle.jl @@ -10,7 +10,7 @@ given `plane` with given `radius`. See also [`Disk`](@ref). """ -struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} +struct Circle{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{𝔼{3},C} plane::P radius::ℒ Circle(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index cf30cefee..777a25e81 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -10,9 +10,9 @@ See . See also [`ConeSurface`](@ref). """ -struct Cone{C<:CRS,D<:Disk{C}} <: Primitive{C} +struct Cone{C<:CRS,D<:Disk{C},Mₚ<:AbstractManifold} <: Primitive{𝔼{3},C} base::D - apex::Point{C} + apex::Point{Mₚ,C} end function Cone(base::Disk{C}, apex::Tuple) where {C<:Cartesian} diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 7720bf640..872a1a838 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -10,9 +10,9 @@ See . See also [`Cone`](@ref). """ -struct ConeSurface{C<:CRS,D<:Disk{C}} <: Primitive{C} +struct ConeSurface{C<:CRS,D<:Disk{C},Mₚ<:AbstractManifold} <: Primitive{𝔼{3},C} base::D - apex::Point{C} + apex::Point{Mₚ,C} end function ConeSurface(base::Disk{C}, apex::Tuple) where {C<:Cartesian} diff --git a/src/primitives/cylinder.jl b/src/primitives/cylinder.jl index 56587cdfe..55302dc27 100644 --- a/src/primitives/cylinder.jl +++ b/src/primitives/cylinder.jl @@ -24,7 +24,7 @@ Finally, construct a right vertical circular cylinder with given `radius`. See . """ -struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} +struct Cylinder{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{𝔼{3},C} bot::P top::P radius::ℒ diff --git a/src/primitives/cylindersurface.jl b/src/primitives/cylindersurface.jl index 72f5d9367..b2ef64639 100644 --- a/src/primitives/cylindersurface.jl +++ b/src/primitives/cylindersurface.jl @@ -24,7 +24,7 @@ Finally, construct a right vertical circular cylinder surface with given `radius See . """ -struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} +struct CylinderSurface{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{𝔼{3},C} bot::P top::P radius::ℒ diff --git a/src/primitives/disk.jl b/src/primitives/disk.jl index cd6ab9bd9..a084c4698 100644 --- a/src/primitives/disk.jl +++ b/src/primitives/disk.jl @@ -10,7 +10,7 @@ given `plane` with given `radius`. See also [`Circle`](@ref). """ -struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{C} +struct Disk{C<:CRS,P<:Plane{C},ℒ<:Len} <: Primitive{𝔼{3},C} plane::P radius::ℒ Disk(plane::P, radius::ℒ) where {C<:CRS,P<:Plane{C},ℒ<:Len} = new{C,P,float(ℒ)}(plane, radius) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index a5da18e05..b60a01f84 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -7,12 +7,12 @@ A 3D ellipsoid with given `radii`, `center` and `rotation`. """ -struct Ellipsoid{ℒ<:Len,C<:CRS,R} <: Primitive{C} +struct Ellipsoid{C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} <: Primitive{𝔼{3},C} radii::NTuple{3,ℒ} - center::Point{C} + center::Point{Mₚ,C} rotation::R - Ellipsoid(radii::NTuple{3,ℒ}, center::Point{C}, rotation::R) where {ℒ<:Len,C<:CRS,R} = - new{float(ℒ),C,R}(radii, center, rotation) + Ellipsoid(radii::NTuple{3,ℒ}, center::Point{Mₚ,C}, rotation::R) where{C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} = + new{C,Mₚ,R,float(ℒ)}(radii, center, rotation) end Ellipsoid(radii::Tuple, center::Point, rotation) = Ellipsoid(addunit.(radii, u"m"), center, rotation) diff --git a/src/primitives/frustum.jl b/src/primitives/frustum.jl index fee5523f4..505c840a4 100644 --- a/src/primitives/frustum.jl +++ b/src/primitives/frustum.jl @@ -10,7 +10,7 @@ See . See also [`FrustumSurface`](@ref). """ -struct Frustum{C<:CRS,D<:Disk{C}} <: Primitive{C} +struct Frustum{C<:CRS,D<:Disk{C}} <: Primitive{𝔼{3},C} bot::D top::D diff --git a/src/primitives/frustumsurface.jl b/src/primitives/frustumsurface.jl index 9bf6eb4b7..23ea9f134 100644 --- a/src/primitives/frustumsurface.jl +++ b/src/primitives/frustumsurface.jl @@ -10,7 +10,7 @@ See . See also [`Frustum`](@ref). """ -struct FrustumSurface{C<:CRS,D<:Disk{C}} <: Primitive{C} +struct FrustumSurface{C<:CRS,D<:Disk{C}} <: Primitive{𝔼{3},C} bot::D top::D diff --git a/src/primitives/line.jl b/src/primitives/line.jl index ae939f652..6af4348bd 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -9,9 +9,9 @@ A line passing through points `a` and `b`. See also [`Segment`](@ref). """ -struct Line{C<:CRS} <: Primitive{C} - a::Point{C} - b::Point{C} +struct Line{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} + a::Point{M,C} + b::Point{M,C} end Line(a::Tuple, b::Tuple) = Line(Point(a), Point(b)) diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index aee3fd528..0ee8206ae 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -32,12 +32,12 @@ Same as above, but here the apex is at `Apex(0, 0, 0)`. See also . """ -struct ParaboloidSurface{C<:CRS,ℒ<:Len} <: Primitive{C} - apex::Point{C} +struct ParaboloidSurface{C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} <: Primitive{𝔼{3},C} + apex::Point{Mₚ,C} radius::ℒ focallength::ℒ - ParaboloidSurface(apex::Point{C}, radius::ℒ, focallength::ℒ) where {C<:CRS,ℒ<:Len} = - new{C,float(ℒ)}(apex, radius, focallength) + ParaboloidSurface(apex::Point{Mₚ,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} = + new{C,Mₚ,float(ℒ)}(apex, radius, focallength) end ParaboloidSurface(apex::Point, radius::Len, focallength::Len) = ParaboloidSurface(apex, promote(radius, focallength)...) @@ -86,7 +86,10 @@ apex(p::ParaboloidSurface) = p.apex Return the focal axis, connecting the focus with the apex of the paraboloid. The axis is always aligned with the z direction. """ -axis(p::ParaboloidSurface{P,ℒ}) where {P,ℒ} = Line(p.apex, p.apex + Vec(ℒ(0), ℒ(0), p.focallength)) +function axis(p::ParaboloidSurface) + f = p.focallength + Line(p.apex, p.apex + Vec(zero(f), zero(f), f)) +end function centroid(p::ParaboloidSurface) c = p.apex diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index b724ae570..a2aa87624 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -13,8 +13,8 @@ defined by non-parallel vectors `u` and `v`. Alternatively specify point `p` and a given normal vector `n` to the plane. """ -struct Plane{C<:CRS,V<:Vec{3}} <: Primitive{C} - p::Point{C} +struct Plane{C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3}} <: Primitive{𝔼{3},C} + p::Point{Mₚ,C} u::V v::V end diff --git a/src/primitives/point.jl b/src/primitives/point.jl index a5c9b303b..bd057f30d 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -33,16 +33,20 @@ Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design algorithms assume a continuous space. The conversion to float avoids `InexactError` and other unexpected results. """ -struct Point{C<:CRS} <: Primitive{C} +struct Point{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} coords::C end +Point{M}(coords::C) where {M<:AbstractManifold,C<:CRS} = Point{M,C}(coords) + +Point(coords::CRS) = Point{_manifold(coords)}(coords) + # convenience constructor Point(coords...) = Point(Cartesian(coords...)) # conversions -Base.convert(::Type{Point{CRSₜ}}, p::Point{CRSₛ}) where {CRSₜ,CRSₛ} = Point(convert(CRSₜ, p.coords)) -Base.convert(::Type{Point{CRS}}, p::Point{CRS}) where {CRS} = p +Base.convert(::Type{Point{M,CRSₜ}}, p::Point{M,CRSₛ}) where {M,CRSₜ,CRSₛ} = Point{M}(convert(CRSₜ, p.coords)) +Base.convert(::Type{Point{M,CRS}}, p::Point{M,CRS}) where {M,CRS} = p paramdim(::Type{<:Point}) = 0 @@ -100,13 +104,18 @@ at a reference (or start) point `A`. <(A::Point, B::Point) >(A::Point, B::Point) -Generalized inequality for non-negative orthant Rⁿ₊. +Partial order for points on a given manifold. """ ≤(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) ≥(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) <(A::Point, B::Point) = all(x -> x > zero(x), B - A) >(A::Point, B::Point) = all(x -> x > zero(x), A - B) +≤(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≤ _lat(B) +≥(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≥ _lat(B) +<(A::Point{🌐}, B::Point{🌐}) = _lat(A) < _lat(B) +>(A::Point{🌐}, B::Point{🌐}) = _lat(A) > _lat(B) + """ ∠(A, B, C) @@ -145,3 +154,14 @@ function Base.show(io::IO, mime::MIME"text/plain", point::Point) print(io, "Point with ") show(io, mime, point.coords) end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_manifold(coords::CRS) = 𝔼{CoordRefSystems.ndims(coords)} +_manifold(::LatLon) = 🌐 +_manifold(::GeocentricLatLon) = 🌐 +_manifold(::AuthalicLatLon) = 🌐 + +_lat(P) = convert(LatLon, P.coords).lat diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 0b11322e5..3859135ba 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -9,8 +9,8 @@ A ray originating at point `p`, pointed in direction `v`. It can be called as `r(t)` with `t > 0` to cast it at `p + t * v`. """ -struct Ray{C<:CRS,V<:Vec} <: Primitive{C} - p::Point{C} +struct Ray{C<:CRS,Mₚ<:AbstractManifold,Dim,V<:Vec{Dim}} <: Primitive{𝔼{Dim},C} + p::Point{Mₚ,C} v::V end diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index f19b8f9ee..db66503a1 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -9,10 +9,10 @@ A sphere with `center` and `radius`. See also [`Ball`](@ref). """ -struct Sphere{C<:CRS,ℒ<:Len} <: Primitive{C} - center::Point{C} +struct Sphere{M<:AbstractManifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} + center::Point{M,C} radius::ℒ - Sphere(center::Point{C}, radius::ℒ) where {C<:CRS,ℒ<:Len} = new{C,float(ℒ)}(center, radius) + Sphere(center::Point{M,C}, radius::ℒ) where {M<:AbstractManifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) end Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index fe3ac8174..dfeca2126 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -9,13 +9,13 @@ A torus centered at `center` with axis of revolution directed by `normal` and with radii `major` and `minor`. """ -struct Torus{C<:CRS,V<:Vec{3},ℒ<:Len} <: Primitive{C} - center::Point{C} +struct Torus{C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3},ℒ<:Len} <: Primitive{𝔼{3},C} + center::Point{Mₚ,C} normal::V major::ℒ minor::ℒ - Torus(center::Point{C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,V<:Vec{3},ℒ<:Len} = - new{C,V,float(ℒ)}(center, normal, major, minor) + Torus(center::Point{Mₚ,C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3},ℒ<:Len} = + new{C,Mₚ,V,float(ℒ)}(center, normal, major, minor) end Torus(center::Point, normal::Vec, major::Len, minor::Len) = Torus(center, normal, promote(major, minor)...) diff --git a/src/sets.jl b/src/sets.jl index 409069398..adac64bf7 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -15,7 +15,7 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` """ -struct GeometrySet{C<:CRS,G<:Geometry{C}} <: Domain{C} +struct GeometrySet{M<:AbstractManifold,C<:CRS,G<:Geometry{M,C}} <: Domain{M,C} geoms::Vector{G} end @@ -37,7 +37,7 @@ Base.vcat(d1::Domain, d2::GeometrySet) = GeometrySet(vcat(collect(d1), d2.geoms) # SPECIAL CASE: POINT SET # ------------------------ -const PointSet{CRS} = GeometrySet{CRS,Point{CRS}} +const PointSet{M<:AbstractManifold,C<:CRS} = GeometrySet{M,C,Point{M,C}} """ PointSet(points) @@ -55,7 +55,7 @@ julia> PointSet([(1,2,3), (4,5,6)]) julia> PointSet((1,2,3), (4,5,6)) ``` """ -PointSet(points::AbstractVector{Point{C}}) where {C<:CRS} = PointSet{C}(points) +PointSet(points::AbstractVector{Point{M,C}}) where {M<:AbstractManifold,C<:CRS} = PointSet{M,C}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) diff --git a/src/subdomains.jl b/src/subdomains.jl index edb599f12..e074d199a 100644 --- a/src/subdomains.jl +++ b/src/subdomains.jl @@ -11,7 +11,7 @@ A partial view of a `domain` containing only the elements at `indices`. """ -struct SubDomain{C<:CRS,D<:Domain{C},I<:AbstractVector{Int}} <: Domain{C} +struct SubDomain{M<:AbstractManifold,C<:CRS,D<:Domain{M,C},I<:AbstractVector{Int}} <: Domain{M,C} domain::D inds::I end @@ -19,6 +19,14 @@ end # specialize constructor to avoid infinite loops SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[inds]) +""" + SubGrid{M,CRS,Dim} + +A subgrid of geometries in a given manifold `M` with point coordinates specified +in a coordinate reference system `CRS`, which is embedded in `Dim` dimensions. +""" +const SubGrid{M<:AbstractManifold,C<:CRS,Dim} = SubDomain{M,C,<:Grid{M,C,Dim}} + # ----------------- # DOMAIN INTERFACE # ----------------- diff --git a/src/trajecs.jl b/src/trajecs.jl index 19df53e44..0f657e8f3 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -7,11 +7,11 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. """ -struct CylindricalTrajectory{C<:CRS,ℒ<:Len} <: Domain{C} - centroids::Vector{Point{C}} +struct CylindricalTrajectory{C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} <: Domain{𝔼{3},C} + centroids::Vector{Point{Mₚ,C}} radius::ℒ - CylindricalTrajectory(centroids::Vector{Point{C}}, radius::ℒ) where {C<:CRS,ℒ<:Len} = - new{C,float(ℒ)}(centroids, radius) + CylindricalTrajectory(centroids::Vector{Point{Mₚ,C}}, radius::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} = + new{C,Mₚ,float(ℒ)}(centroids, radius) end CylindricalTrajectory(centroids, radius::Len) = CylindricalTrajectory(collect(centroids), radius) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 9002644d9..a2573e3dc 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -93,7 +93,7 @@ d = cartgrid(10, 10) v = view(d, 1:2) @test boundingbox(v) == Box(cart(0, 0), cart(2, 1)) - @test @allocated(boundingbox(v)) < 9000 + @test @allocated(boundingbox(v)) < 10000 g = cartgrid(10, 10) d = convert(RectilinearGrid, g) diff --git a/test/dummy.jl b/test/dummy.jl index 66ef9acb6..19ca4b980 100644 --- a/test/dummy.jl +++ b/test/dummy.jl @@ -1,6 +1,6 @@ # dummy type implementing the Domain trait -struct DummyDomain{C<:CRS} <: Domain{C} - origin::Point{C} +struct DummyDomain{M<:Meshes.AbstractManifold,C<:CRS} <: Domain{M,C} + origin::Point{M,C} end function Meshes.element(domain::DummyDomain, ind::Int) diff --git a/test/predicates.jl b/test/predicates.jl index 46ba1e25d..f649cd9be 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -139,12 +139,12 @@ @testset "isperiodic" begin # primitives - @test isperiodic(Box{Cartesian2D}) == (false, false) - @test isperiodic(Box{Cartesian3D}) == (false, false, false) - @test isperiodic(Ball{Cartesian2D}) == (false, true) - @test isperiodic(Ball{Cartesian3D}) == (false, false, true) - @test isperiodic(Sphere{Cartesian2D}) == (true,) - @test isperiodic(Sphere{Cartesian3D}) == (false, true) + @test isperiodic(Box{𝔼{2},Cartesian2D}) == (false, false) + @test isperiodic(Box{𝔼{3},Cartesian3D}) == (false, false, false) + @test isperiodic(Ball{𝔼{2},Cartesian2D}) == (false, true) + @test isperiodic(Ball{𝔼{3},Cartesian3D}) == (false, false, true) + @test isperiodic(Sphere{𝔼{2},Cartesian2D}) == (true,) + @test isperiodic(Sphere{𝔼{3},Cartesian3D}) == (false, true) @test isperiodic(Ellipsoid) == (false, true) @test isperiodic(Cylinder) == (false, true, false) @test isperiodic(CylinderSurface) == (true, false) From 0bbb1c584b2f7d5b9ac06549f0477418b7309d28 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:31:18 -0300 Subject: [PATCH 191/423] :robot: Format .jl files (#953) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/distances.jl | 3 +-- src/primitives/ellipsoid.jl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/distances.jl b/src/distances.jl index 64b799005..569f063c6 100644 --- a/src/distances.jl +++ b/src/distances.jl @@ -64,8 +64,7 @@ function _evaluate(d::Haversine, coords₁::LatLon, coords₂::LatLon) evaluate(d, v₁, v₂) * u end -_evaluate(d::Haversine, coords₁::CRS, coords₂::CRS) = - _evaluate(d, convert(LatLon, coords₁), convert(LatLon, coords₂)) +_evaluate(d::Haversine, coords₁::CRS, coords₂::CRS) = _evaluate(d, convert(LatLon, coords₁), convert(LatLon, coords₂)) evaluate(d::SphericalAngle, p₁::Point, p₂::Point) = _evaluate(d, coords(p₁), coords(p₂)) diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index b60a01f84..12866f264 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -11,7 +11,7 @@ struct Ellipsoid{C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} <: Primitive{𝔼{3}, radii::NTuple{3,ℒ} center::Point{Mₚ,C} rotation::R - Ellipsoid(radii::NTuple{3,ℒ}, center::Point{Mₚ,C}, rotation::R) where{C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} = + Ellipsoid(radii::NTuple{3,ℒ}, center::Point{Mₚ,C}, rotation::R) where {C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} = new{C,Mₚ,R,float(ℒ)}(radii, center, rotation) end From 6552d9c0dea5ba9220e6e6f41b6c2e166e37d66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 24 Jul 2024 17:29:14 -0300 Subject: [PATCH 192/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 16d5ea8a0..a28eb9a1b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.46.5" +version = "0.47.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 0982db59b91e1e8607b2d6fc0a4a1e8bbaae2796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 08:53:07 -0300 Subject: [PATCH 193/423] Remove warnings from viz recipes --- ext/geometryset.jl | 1 - ext/grid.jl | 1 - ext/mesh.jl | 1 - ext/subdomain.jl | 1 - 4 files changed, 4 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 0bd387394..113d695f5 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -32,7 +32,6 @@ end const ObservableVector{T} = Makie.Observable{<:AbstractVector{T}} function vizgset!(plot, ::Type{<:🌐}, pdim::Val, edim::Val, geoms, colorant) - @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." vizgset!(plot, 𝔼, pdim, edim, geoms, colorant) end diff --git a/ext/grid.jl b/ext/grid.jl index ba74a9ae0..509a851b6 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -11,7 +11,6 @@ function Makie.plot!(plot::Viz{<:Tuple{Grid}}) end function vizgrid!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) - @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." vizgrid!(plot, 𝔼, pdim, edim) end diff --git a/ext/mesh.jl b/ext/mesh.jl index 4714dbdfd..64779bfbd 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -12,7 +12,6 @@ function Makie.plot!(plot::Viz{<:Tuple{Mesh}}) end function vizmesh!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) - @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." vizmesh!(plot, 𝔼, pdim, edim) end diff --git a/ext/subdomain.jl b/ext/subdomain.jl index 452f6b59e..d97735d0c 100644 --- a/ext/subdomain.jl +++ b/ext/subdomain.jl @@ -11,7 +11,6 @@ function Makie.plot!(plot::Viz{<:Tuple{SubDomain}}) end function vizsubdom!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) - @warn "geodesic geometries can't be visualized yet. Visualizing as Euclidean..." vizsubdom!(plot, 𝔼, pdim, edim) end From 67fb0cf4401d69a1dc7bfaae13183e165a55016d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 11:19:47 -0300 Subject: [PATCH 194/423] Bridge hotfix (#955) * Use flat in bridge * Fix and test --- src/transforms/bridge.jl | 14 +++++++++++--- test/transforms.jl | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index 467940a6a..37697d381 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -52,13 +52,16 @@ function bridge(rings, rinds, δ) # retrieve coordinate type ℒ = lentype(first(rings)) + # retrieve original CRS + C = crs(first(rings)) + # initialize outer boundary - outer = verts[1] + outer = flat.(verts[1]) oinds = vinds[1] # merge holes into outer boundary for i in 2:length(verts) - inner = verts[i] + inner = flat.(verts[i]) iinds = vinds[i] # find closest pair of vertices (A, B) @@ -117,5 +120,10 @@ function bridge(rings, rinds, δ) end end - Ring(outer), dups + points = map(outer) do p + c = CoordRefSystems.rawvalues(coords(p)) + Point(CoordRefSystems.reconstruct(C, c)) + end + + Ring(points), dups end diff --git a/test/transforms.jl b/test/transforms.jl index 8f4c85d82..aeec0290a 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1873,6 +1873,14 @@ bpoly = poly |> Bridge() @test bpoly isa Quadrangle @test bpoly == poly + + # bridge with latlon coords + outer = latlon.([(0, 0), (0, 90), (90, 90), (90, 0)]) + hole1 = latlon.([(10, 10), (10, 20), (20, 20), (20, 10)]) + hole2 = latlon.([(10, 80), (10, 90), (20, 90), (20, 80)]) + poly = PolyArea([outer, hole1, hole2]) + bpoly = poly |> Bridge() + @test nvertices(bpoly) == 16 end @testset "Smoothing" begin From c7db0241dee3a8408a58a48bc7bc66cc1fad55b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 11:50:25 -0300 Subject: [PATCH 195/423] Refactor flat util --- src/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index cb24a3bdb..59136b9ab 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -70,7 +70,7 @@ Flatten coordinates of point `p` to Cartesian coordinates, ignoring the original units of the coordinate reference system. """ flat(p::Point) = Point(flat(coords(p))) -flat(c::LatLon) = Cartesian{datum(c)}(CoordRefSystems.rawvalues(c)) +flat(c::LatLon) = Cartesian{datum(c)}(ustrip(c.lat), ustrip(c.lon)) flat(c::CRS) = convert(Cartesian, c) """ From 824299f457d136b9a8422d7948927062dff1e23f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:16:59 -0300 Subject: [PATCH 196/423] 'applycoord' hotfix (#956) --- src/transforms.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/transforms.jl b/src/transforms.jl index d2ac1d04c..fecabb1ba 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -81,9 +81,8 @@ applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) # special treatment for lists of geometries applycoord(t::CoordinateTransform, g::NTuple{<:Any,<:Geometry}) = map(gᵢ -> applycoord(t, gᵢ), g) -applycoord(t::CoordinateTransform, g::AbstractVector{<:Geometry}) = tcollect(applycoord(t, gᵢ) for gᵢ in g) -applycoord(t::CoordinateTransform, g::CircularVector{<:Geometry}) = - CircularVector(tcollect(applycoord(t, gᵢ) for gᵢ in g)) +applycoord(t::CoordinateTransform, g::AbstractVector{<:Geometry}) = [applycoord(t, gᵢ) for gᵢ in g] +applycoord(t::CoordinateTransform, g::CircularVector{<:Geometry}) = CircularVector([applycoord(t, gᵢ) for gᵢ in g]) # ---------------- # IMPLEMENTATIONS From 61ac9e4ef6c0b0ac232a96c842008edc7359673a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 15:22:40 -0300 Subject: [PATCH 197/423] Remove tcollect from Shadow --- src/transforms/shadow.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index caa51c9d7..c0dbee205 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -106,5 +106,5 @@ _shadow(x, _) = x # special treatment for lists of geometries _shadow(g::NTuple{<:Any,<:Geometry}, dims) = map(gᵢ -> _shadow(gᵢ, dims), g) -_shadow(g::AbstractVector{<:Geometry}, dims) = tcollect(_shadow(gᵢ, dims) for gᵢ in g) -_shadow(g::CircularVector{<:Geometry}, dims) = CircularVector(tcollect(_shadow(gᵢ, dims) for gᵢ in g)) +_shadow(g::AbstractVector{<:Geometry}, dims) = [_shadow(gᵢ, dims) for gᵢ in g] +_shadow(g::CircularVector{<:Geometry}, dims) = CircularVector([_shadow(gᵢ, dims) for gᵢ in g]) From df543116a99e0d5346b9c1939b2e28d5c18ce11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 15:38:28 -0300 Subject: [PATCH 198/423] Updates for latest CRS.jl --- Project.toml | 2 +- src/tesselation/delaunay.jl | 2 +- src/tesselation/voronoi.jl | 2 +- src/transforms/bridge.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index a28eb9a1b..98c96734f 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.9.10" +CoordRefSystems = "0.10" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/src/tesselation/delaunay.jl b/src/tesselation/delaunay.jl index 52903d4cc..a517b3703 100644 --- a/src/tesselation/delaunay.jl +++ b/src/tesselation/delaunay.jl @@ -28,7 +28,7 @@ function tesselate(pset::PointSet, method::DelaunayTesselation) assertion(CoordRefSystems.ncoords(crs(pset)) == 2, "points must have 2 coordinates") # perform tesselation with raw coordinates - rawval = map(p -> CoordRefSystems.rawvalues(coords(p)), pset) + rawval = map(p -> CoordRefSystems.raw(coords(p)), pset) triang = triangulate(rawval, rng=method.rng) connec = connect.(each_solid_triangle(triang)) SimpleMesh(collect(pset), connec) diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index 6ad3e6f64..e53d17062 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -30,7 +30,7 @@ function tesselate(pset::PointSet, method::VoronoiTesselation) assertion(CoordRefSystems.ncoords(C) == 2, "points must have 2 coordinates") # perform tesselation with raw coordinates - rawval = map(p -> CoordRefSystems.rawvalues(coords(p)), pset) + rawval = map(p -> CoordRefSystems.raw(coords(p)), pset) triang = triangulate(rawval, rng=method.rng) vorono = voronoi(triang, clip=true) diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index 37697d381..10a7ffe2b 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -121,7 +121,7 @@ function bridge(rings, rinds, δ) end points = map(outer) do p - c = CoordRefSystems.rawvalues(coords(p)) + c = CoordRefSystems.raw(coords(p)) Point(CoordRefSystems.reconstruct(C, c)) end From ed6e3013f202e5e184bf8f80c4bc608a6df599bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 15:38:47 -0300 Subject: [PATCH 199/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 98c96734f..78a272654 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.0" +version = "0.47.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 68dd30b9029b4987f5ea9234d6978575c413438b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 17:00:44 -0300 Subject: [PATCH 200/423] Remove uses of tcollect --- src/sideof.jl | 2 +- src/winding.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sideof.jl b/src/sideof.jl index 34b989d6b..6fb576b1a 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -160,7 +160,7 @@ sideof(points, line::Line) = map(point -> sideof(point, line), points) function sideof(points, object::GeometryOrDomain) bbox = boundingbox(object) - isin = tcollect(point ∈ bbox for point in points) + isin = [point ∈ bbox for point in points] inds = findall(isin) side = fill(OUT, length(isin)) side[inds] .= sidewithinbox(collectat(points, inds), object) diff --git a/src/winding.jl b/src/winding.jl index c1f9e3ac8..6f65737ee 100644 --- a/src/winding.jl +++ b/src/winding.jl @@ -31,7 +31,7 @@ function winding(points, ring::Ring) Σ / oftype(Σ, 2π) end - tcollect(w(p) for p in points) + [w(p) for p in points] end winding(point::Point, ring::Ring) = winding((point,), ring) |> first @@ -61,7 +61,7 @@ function winding(points, mesh::Mesh) ∑ / oftype(∑, 4π) end - tcollect(w(p) for p in points) + [w(p) for p in points] end winding(point::Point, mesh::Mesh) = winding((point,), mesh) |> first From bcdaeefe0c867659f45889de6780937ae74df266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 19:29:25 -0300 Subject: [PATCH 201/423] Fix #952 - Drop Transducers.jl --- Project.toml | 2 -- src/Meshes.jl | 1 - src/utils.jl | 6 ++++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 78a272654..5635b1648 100644 --- a/Project.toml +++ b/Project.toml @@ -17,7 +17,6 @@ Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -Transducers = "28d57a85-8fef-5791-bfe6-a80928e7c999" TransformsBase = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" @@ -42,7 +41,6 @@ Rotations = "1.5.1" SparseArrays = "1.9" StaticArrays = "1.0" StatsBase = "0.33, 0.34" -Transducers = "0.4" TransformsBase = "1.4.1" Unitful = "1.17" julia = "1.9" diff --git a/src/Meshes.jl b/src/Meshes.jl index 3315fe9dc..c61c831bb 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -26,7 +26,6 @@ using DelaunayTriangulation: triangulate, voronoi using DelaunayTriangulation: each_solid_triangle using DelaunayTriangulation: each_polygon using DelaunayTriangulation: get_polygon_points -using Transducers: Filter, Map, TakeWhile, tcollect, ⨟ using Base.Cartesian: @nloops, @nref, @ntuple using Base: @propagate_inbounds diff --git a/src/utils.jl b/src/utils.jl index 59136b9ab..302f94eee 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -46,8 +46,10 @@ function collectat(iter, inds) if isempty(inds) eltype(iter)[] else - selectat(inds) = enumerate ⨟ TakeWhile(x -> first(x) ≤ last(inds)) ⨟ Filter(y -> first(y) ∈ inds) ⨟ Map(last) - iter |> selectat(inds) |> tcollect + e = enumerate(iter) + w = Iterators.takewhile(x -> (first(x) ≤ last(inds)), e) + f = Iterators.filter(x -> (first(x) ∈ inds), w) + map(last, f) end end From f43e113fe05bc6ee8d664948d1f9605ad87ba3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 25 Jul 2024 19:29:59 -0300 Subject: [PATCH 202/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5635b1648..31fd79126 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.1" +version = "0.47.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 3a6e3b17b6b7505155fab5d31f25483cf357ee83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 26 Jul 2024 06:56:09 -0300 Subject: [PATCH 203/423] Fix collectat util --- src/utils.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 302f94eee..a501c2596 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -46,8 +46,9 @@ function collectat(iter, inds) if isempty(inds) eltype(iter)[] else - e = enumerate(iter) - w = Iterators.takewhile(x -> (first(x) ≤ last(inds)), e) + m = maximum(inds) + e = Iterators.enumerate(iter) + w = Iterators.takewhile(x -> (first(x) ≤ m), e) f = Iterators.filter(x -> (first(x) ∈ inds), w) map(last, f) end From ce815e15d9caab59aa2ee5ff2675fe4e897871ab Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:03:00 -0300 Subject: [PATCH 204/423] Add `Repair{11}` and `Repair{12}` (#959) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'Repair{11}' and 'Repair{12}' * Add tests * Update tests * Update tests * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Apply suggestions --------- Co-authored-by: Júlio Hoffimann --- src/transforms/repair.jl | 42 +++++++++++++++++++++++++++++++++++++++- test/transforms.jl | 25 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index bd982bf4a..ad3ee5f85 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -19,7 +19,9 @@ Perform repairing operation with code `K`. - K = 7: faces are coherently oriented - K = 8: zero-area ears are removed - K = 9: rings of polygon are sorted -- K = 10: outer rings are expanded +- K = 10: outer rings of polygon are expanded +- K = 11: rings of polygon are coherently oriented +- K = 12: degenerate rings of polygon are removed ## Examples @@ -171,3 +173,41 @@ function _stretch10(g::Geometry) T = numtype(lentype(g)) Stretch(ntuple(i -> one(T) + 10atol(T), embeddim(g))) end + +# --------------- +# OPERATION (11) +# --------------- + +function apply(::Repair{11}, poly::PolyArea) + r = rings(poly) + + # fix orientation + ofix(r, o) = orientation(r) == o ? r : reverse(r) + outer = ofix(first(r), CCW) + inners = ofix.(r[2:end], CW) + + PolyArea([outer; inners]), nothing +end + +# --------------- +# OPERATION (12) +# --------------- + +function apply(::Repair{12}, poly::PolyArea) + r = rings(poly) + + # fix degeneracy + oring = first(r) + outer = if nvertices(oring) == 2 + A, B = vertices(oring) + P = center(Segment(A, B)) + Ring(A, P, B) + else + oring + end + + # remove degenerated rings + inners = filter(r -> nvertices(r) > 2, r[2:end]) + + PolyArea([outer; inners]), nothing +end diff --git a/test/transforms.jl b/test/transforms.jl index aeec0290a..2ed96d882 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1799,6 +1799,31 @@ @test opoly == poly end + @testset "Repair{11}" begin + outer = cart.([(0, 0), (0, 2), (2, 2), (2, 0)]) + inner = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + poly = PolyArea(outer, inner, fix=false) + repair = Repair{11}() + rpoly, cache = TB.apply(repair, poly) + router, rinner = rings(rpoly) + @test router == Ring(cart.([(0, 0), (2, 0), (2, 2), (0, 2)])) + @test rinner == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + end + + @testset "Repair{12}" begin + poly = PolyArea(cart.([(0, 0), (1, 0)]), fix=false) + repair = Repair{12}() + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea(cart.([(0, 0), (0.5, 0.0), (1, 0)])) + + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + inner = cart.([(1, 2), (2, 3)]) + poly = PolyArea(outer, inner, fix=false) + repair = Repair{12}() + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea(outer) + end + @testset "Bridge" begin @test !isaffine(Bridge) δ = T(0.01) * u"m" From c22f7836be6ebf3aeb8b8a3d922cd1c3464b7f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 26 Jul 2024 15:01:45 -0300 Subject: [PATCH 205/423] Refactor PolyArea constructor (#960) * Refactor PolyArea constructor * Fix tests * Fix tests * Fix tests * Clean inner constructor * More fixes * Remove inner constructor --------- Co-authored-by: Elias Carvalho --- docs/src/algorithms/orientation.md | 6 ++-- docs/src/geometries/polytopes.md | 6 ++-- docs/src/transforms.md | 4 +-- src/polytopes/polyarea.jl | 53 ++++-------------------------- src/polytopes/ring.jl | 4 +-- src/predicates/isconvex.jl | 1 - test/complement.jl | 8 ++--- test/discretization.jl | 8 ++--- test/polytopes.jl | 36 +++++--------------- test/predicates.jl | 22 ++++++------- test/transforms.jl | 14 ++++---- 11 files changed, 50 insertions(+), 112 deletions(-) diff --git a/docs/src/algorithms/orientation.md b/docs/src/algorithms/orientation.md index a12e83150..e854e6a0a 100644 --- a/docs/src/algorithms/orientation.md +++ b/docs/src/algorithms/orientation.md @@ -33,9 +33,9 @@ For polygons with holes, the function returns a vector with the orientation of all constituent rings: ```@example orientation -outer = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)] -hole1 = [(0.2,0.2),(0.4,0.2),(0.4,0.4),(0.2,0.4)] -hole2 = [(0.6,0.2),(0.8,0.2),(0.8,0.4),(0.6,0.4)] +outer = [(0, 0), (1, 0), (1, 1), (0, 1)] +hole1 = [(0.2, 0.2), (0.2, 0.4), (0.4, 0.4), (0.4, 0.2)] +hole2 = [(0.6, 0.2), (0.6, 0.4), (0.8, 0.4), (0.8, 0.2)] poly = PolyArea([outer, hole1, hole2]) orientation(poly) diff --git a/docs/src/geometries/polytopes.md b/docs/src/geometries/polytopes.md index c921e28a2..398a816d9 100644 --- a/docs/src/geometries/polytopes.md +++ b/docs/src/geometries/polytopes.md @@ -63,9 +63,9 @@ PolyArea ``` ```@example polytopes -outer = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)] -hole1 = [(0.2,0.2),(0.4,0.2),(0.4,0.4),(0.2,0.4)] -hole2 = [(0.6,0.2),(0.8,0.2),(0.8,0.4),(0.6,0.4)] +outer = [(0, 0), (1, 0), (1, 1), (0, 1)] +hole1 = [(0.2, 0.2), (0.2, 0.4), (0.4, 0.4), (0.4, 0.2)] +hole2 = [(0.6, 0.2), (0.6, 0.4), (0.8, 0.4), (0.8, 0.2)] poly = PolyArea([outer, hole1, hole2]) |> viz ``` diff --git a/docs/src/transforms.md b/docs/src/transforms.md index e2838bedc..6153b4093 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -216,8 +216,8 @@ Bridge ```@example transforms # polygon with two holes outer = [(0, 0), (1, 0), (1, 1), (0, 1)] -hole1 = [(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)] -hole2 = [(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)] +hole1 = [(0.2, 0.2), (0.2, 0.4), (0.4, 0.4), (0.4, 0.2)] +hole2 = [(0.6, 0.2), (0.6, 0.4), (0.8, 0.4), (0.8, 0.2)] poly = PolyArea([outer, hole1, hole2]) # polygon with single outer ring diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 4fac1a9cb..f84606124 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -3,8 +3,8 @@ # ------------------------------------------------------------------ """ - PolyArea(outer; fix=true) - PolyArea([outer, inner₁, inner₂, ..., innerₖ]; fix=true) + PolyArea(outer) + PolyArea([outer, inner₁, inner₂, ..., innerₖ]) A polygonal area with `outer` ring, and optional inner rings `inner₁`, `inner₂`, ..., `innerₖ`. @@ -13,59 +13,18 @@ Rings can be a vector of [`Point`](@ref) or a vector of tuples with coordinates for convenience, in which case the first point should *not* be repeated at the end of the vector. - -The option `fix` tries to correct issues with polygons -in the real world, including issues with: - -* `orientation` - Most algorithms assume that the - outer ring is oriented counter-clockwise (CCW) and - that all inner rings are oriented clockwise (CW). - -* `degeneracy` - Sometimes data is shared with - degenerate rings (e.g. only 2 vertices). """ struct PolyArea{M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} <: Polygon{M,C} rings::V - - function PolyArea{M,C,R,V}(rings; fix=true) where {M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} - if isempty(rings) - throw(ArgumentError("cannot create PolyArea without rings")) - end - - if fix - outer = rings[begin] - inners = length(rings) > 1 ? rings[(begin + 1):end] : R[] - - # fix orientation - ofix(r, o) = orientation(r) == o ? r : reverse(r) - outer = ofix(outer, CCW) - inners = ofix.(inners, CW) - - # fix degeneracy - if nvertices(outer) == 2 - A, B = vertices(outer) - P = center(Segment(A, B)) - outer = Ring(A, P, B) - end - inners = filter(r -> nvertices(r) > 2, inners) - - rings = [outer; inners] - end - - new(rings) - end end -PolyArea(rings::V; fix=true) where {M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} = - PolyArea{M,C,R,V}(rings; fix) - -PolyArea(vertices::AbstractVector{<:AbstractVector}; fix=true) = PolyArea([Ring(v) for v in vertices]; fix) +PolyArea(vertices::AbstractVector{<:AbstractVector}) = PolyArea([Ring(v) for v in vertices]) -PolyArea(outer::Ring; fix=true) = PolyArea([outer]; fix) +PolyArea(outer::Ring) = PolyArea([outer]) -PolyArea(outer::AbstractVector; fix=true) = PolyArea(Ring(outer); fix) +PolyArea(outer::AbstractVector) = PolyArea(Ring(outer)) -PolyArea(outer...; fix=true) = PolyArea(collect(outer); fix) +PolyArea(outer...) = PolyArea(collect(outer)) ==(p₁::PolyArea, p₂::PolyArea) = p₁.rings == p₂.rings diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 54ef6bcd8..60be8cc8d 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -52,10 +52,10 @@ Base.isapprox(r₁::Ring, r₂::Ring; atol=atol(lentype(r₁)), kwargs...) = Base.close(r::Ring) = r -# call `open` again to avoid issues in case of nested CircularVector +# call `open` again to avoid issues with nested CircularVector Base.open(r::Ring) = open(Rope(parent(r.vertices))) -# do not change which vertex comes first for closed chains +# do not change which vertex comes first in reverse order Base.reverse!(r::Ring) = (reverse!(@view r.vertices[(begin + 1):end]); r) """ diff --git a/src/predicates/isconvex.jl b/src/predicates/isconvex.jl index c551f65ff..9c4efac41 100644 --- a/src/predicates/isconvex.jl +++ b/src/predicates/isconvex.jl @@ -70,7 +70,6 @@ isconvex(m::Multi) = isapproxequal(measure(convexhull(m)), measure(m)) # OPTIMIZATIONS # -------------- -# TODO: check dim: 2 function isconvex(q::Quadrangle) v = vertices(q) d1 = Segment(v[1], v[3]) diff --git a/test/complement.jl b/test/complement.jl index d0da1dd9b..0d9bf0586 100644 --- a/test/complement.jl +++ b/test/complement.jl @@ -25,10 +25,10 @@ @test r[1] ≈ Ring(cart.([(0 - τ, 0 - τ), (1 + τ, 0 - τ), (1 + τ, 1 + τ), (0 - τ, 1 + τ)])) @test r[2] == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - o = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - i1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - i2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - p = PolyArea([o, i1, i2]) + o = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + i1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + i2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + p = PolyArea([o, reverse(i1), reverse(i2)]) m = !p r = rings(m) @test m isa MultiPolygon diff --git a/test/discretization.jl b/test/discretization.jl index 079790c33..d2062ee0e 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -163,10 +163,10 @@ @test eltype(elms) <: Triangle @test length(elms) == 3 - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - poly = PolyArea([outer, hole1, hole2]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) bpoly = poly |> Bridge(T(0.01)) mesh = discretizewithin(boundary(bpoly), method) @test nvertices(mesh) == 16 diff --git a/test/polytopes.jl b/test/polytopes.jl index c613dbd22..c840fd771 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -498,26 +498,6 @@ equaltest(p) isapproxtest(p) - # outer chain with 2 vertices is fixed by default - poly = PolyArea(cart.([(0, 0), (1, 0)])) - @test rings(poly) == [Ring(cart.([(0, 0), (0.5, 0.0), (1, 0)]))] - - # inner chain with 2 vertices is removed by default - poly = PolyArea([cart.([(0, 0), (1, 0), (1, 1), (0, 1)]), cart.([(1, 2), (2, 3)])]) - @test rings(poly) == [Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)]))] - - # orientation of chains is fixed by default - poly = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - @test vertices(poly) == CircularVector(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)]), fix=false) - @test vertices(poly) == CircularVector(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - - # test accessor methods - poly = PolyArea(cart.([(1, 2), (2, 3)]), fix=false) - @test vertices(poly) == CircularVector(cart.([(1, 2), (2, 3)])) - poly = PolyArea([cart.([(1, 2), (2, 3)]), cart.([(1.1, 2.54), (1.4, 1.5)])], fix=false) - @test vertices(poly) == CircularVector(cart.([(1, 2), (2, 3), (1.1, 2.54), (1.4, 1.5)])) - # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 fnames = ["poly$i.line" for i in 1:5] @@ -645,17 +625,17 @@ @test cart(0.75, 0.75) ∈ poly # area - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - poly = PolyArea([outer, hole1, hole2]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) @test area(poly) ≈ T(0.92) * u"m^2" - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) poly1 = PolyArea(outer) - poly2 = PolyArea([outer, hole1, hole2]) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) @test sprint(show, poly1) == "PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" @test sprint(show, poly2) == "PolyArea(4-Ring, 4-Ring, 4-Ring)" @test sprint(show, MIME("text/plain"), poly1) == """ diff --git a/test/predicates.jl b/test/predicates.jl index f649cd9be..e0475ce80 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -95,7 +95,7 @@ inner = cart.([(5, 7), (10, 12), (15, 7)]) pent = Pentagon(outer...) tri = Triangle(inner...) - poly = PolyArea([outer, inner]) + poly = PolyArea([outer, reverse(inner)]) multi = Multi([poly, tri]) @test isconvex(pent) @test isconvex(tri) @@ -258,7 +258,7 @@ pent = Pentagon(pts2...) tri = Triangle(pts1...) poly1 = PolyArea(pts2) - poly2 = PolyArea([pts2, pts1]) + poly2 = PolyArea([pts2, reverse(pts1)]) multi = Multi([poly2, tri]) @test tri ⊆ pent @test tri ⊆ poly1 @@ -268,11 +268,11 @@ @test pent ⊈ poly2 @test pent ⊆ multi - poly1 = PolyArea(cart.([(4, 12), (11, 11), (16, 8), (16, 1), (13, -2), (2, -2), (-3, 4), (-2, 8)])) - poly2 = PolyArea(cart.([(3, 0), (1, 2), (3, 4), (1, 6), (4, 7), (10, 7), (11, 4), (9, 0)])) - poly3 = PolyArea(cart.([(3, 2), (4, 4), (3, 8), (12, 8), (14, 4), (12, 1)])) - poly4 = PolyArea(cart.([(8, 2), (5, 4), (5, 6), (9, 6), (10, 4)])) - poly5 = PolyArea(cart.([(3, 9), (6, 11), (10, 10), (10, 9)])) + poly1 = PolyArea(cart.([(-2, 8), (-3, 4), (2, -2), (13, -2), (16, 1), (16, 8), (11, 11), (4, 12)])) + poly2 = PolyArea(cart.([(9, 0), (11, 4), (10, 7), (4, 7), (1, 6), (3, 4), (1, 2), (3, 0)])) + poly3 = PolyArea(cart.([(12, 1), (14, 4), (12, 8), (3, 8), (4, 4), (3, 2)])) + poly4 = PolyArea(cart.([(10, 4), (9, 6), (5, 6), (5, 4), (8, 2)])) + poly5 = PolyArea(cart.([(10, 9), (10, 10), (6, 11), (3, 9)])) @test poly2 ⊆ poly1 @test poly3 ⊆ poly1 @test poly4 ⊆ poly1 @@ -402,11 +402,11 @@ @test intersects(h1, h2) @test !intersects(h1, h3) - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) poly1 = PolyArea(outer) - poly2 = PolyArea([outer, hole1, hole2]) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) ball1 = Ball(cart(0.5, 0.5), T(0.05)) ball2 = Ball(cart(0.3, 0.3), T(0.05)) ball3 = Ball(cart(0.7, 0.3), T(0.05)) diff --git a/test/transforms.jl b/test/transforms.jl index 2ed96d882..78fdff739 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1802,7 +1802,7 @@ @testset "Repair{11}" begin outer = cart.([(0, 0), (0, 2), (2, 2), (2, 0)]) inner = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - poly = PolyArea(outer, inner, fix=false) + poly = PolyArea(outer, inner) repair = Repair{11}() rpoly, cache = TB.apply(repair, poly) router, rinner = rings(rpoly) @@ -1811,14 +1811,14 @@ end @testset "Repair{12}" begin - poly = PolyArea(cart.([(0, 0), (1, 0)]), fix=false) + poly = PolyArea(cart.([(0, 0), (1, 0)])) repair = Repair{12}() rpoly, cache = TB.apply(repair, poly) @test rpoly == PolyArea(cart.([(0, 0), (0.5, 0.0), (1, 0)])) outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) inner = cart.([(1, 2), (2, 3)]) - poly = PolyArea(outer, inner, fix=false) + poly = PolyArea(outer, inner) repair = Repair{12}() rpoly, cache = TB.apply(repair, poly) @test rpoly == PolyArea(outer) @@ -1853,10 +1853,10 @@ @test (q |> Bridge() |> boundary) == boundary(q) # bridges between holes - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - poly = PolyArea([outer, hole1, hole2]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) @test vertices(poly) == cart.([ (0, 0), From 2d784f8a61b4f85e44c24d83bdc539be08d32c5f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:26:30 -0300 Subject: [PATCH 206/423] Add `Repair` fallbacks (#961) * Add 'Repair' fallbacks * Apply suggestions * Apply suggestions * Update tests --- src/transforms/repair.jl | 14 +++++++++----- test/transforms.jl | 32 ++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index ad3ee5f85..7bae205a3 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -38,7 +38,7 @@ struct Repair{K} <: GeometricTransform end apply(::Repair{0}, geom::Polytope) = unique(geom), nothing -apply(::Repair{0}, mesh::Mesh) = @error "not implemented" +apply(::Repair{0}, mesh::Mesh) = error("not implemented") # -------------- # OPERATION (1) @@ -165,10 +165,6 @@ function revert(::Repair{10}, poly::PolyArea, c) PolyArea([o; r[2:end]]) end -apply(::Repair{10}, poly::Ngon) = poly, nothing - -revert(::Repair{10}, poly::Ngon, cache) = poly - function _stretch10(g::Geometry) T = numtype(lentype(g)) Stretch(ntuple(i -> one(T) + 10atol(T), embeddim(g))) @@ -211,3 +207,11 @@ function apply(::Repair{12}, poly::PolyArea) PolyArea([outer; inners]), nothing end + +# ---------- +# FALLBACKS +# ---------- + +apply(::Repair, geom::Geometry) = geom, nothing + +apply(t::Repair, dom::Domain) = GeometrySet([t(g) for g in dom]), nothing diff --git a/test/transforms.jl b/test/transforms.jl index 78fdff739..c67a97fbe 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1781,10 +1781,19 @@ end @testset "Repair{9}" begin - poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - bpoly = poly |> Repair{9}() - @test bpoly isa Quadrangle - @test bpoly == poly + quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + repair = Repair{9}() + rquad, cache = TB.apply(repair, quad) + @test rquad isa Quadrangle + @test rquad == quad + + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner1 = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner2 = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) + poly = PolyArea([outer, inner1, inner2]) + repair = Repair{9}() + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea([outer, inner2, inner1]) end @testset "Repair{10}" begin @@ -1824,6 +1833,21 @@ @test rpoly == PolyArea(outer) end + @testset "Repair fallbacks" begin + quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + repair = Repair{10}() + rquad, cache = TB.apply(repair, quad) + @test rquad isa Quadrangle + @test rquad == quad + + poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) + poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + gset = GeometrySet([poly1, poly2]) + repair = Repair{11}() + rgset, cache = TB.apply(repair, gset) + @test rgset == GeometrySet([repair(poly1), repair(poly2)]) + end + @testset "Bridge" begin @test !isaffine(Bridge) δ = T(0.01) * u"m" From 2c8ff36add9135a4fc97a05c7e851bd83fe16bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 26 Jul 2024 15:30:06 -0300 Subject: [PATCH 207/423] Update docs --- docs/src/index.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 1d494a8eb..846564b98 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -32,13 +32,9 @@ for finite element analysis (e.g. [JuAFEM.jl](https://kristofferc.github.io/JuAF experience with mesh representations that are adequate for finite finite element analysis, advanced geospatial modeling *and* visualization, not just one domain. -For advanced data science with geospatial data (i.e., tables over meshes), consider the -[GeoStats.jl](https://github.com/JuliaEarth/GeoStats.jl) framework. It provides sophisticated -methods for estimating (interpolating), simulating and learning geospatial functions over -[Meshes.jl](https://github.com/JuliaGeometry/Meshes.jl) meshes. Please check the -[Geospatial Data Science with Julia](https://juliaearth.github.io/geospatial-data-science-with-julia) -book for more information: - +The [Geospatial Data Science with Julia](https://juliaearth.github.io/geospatial-data-science-with-julia) +book is a great resource to learn more about [Meshes.jl](https://github.com/JuliaGeometry/Meshes.jl) +and geospatial data (i.e. tables over meshes): ```@raw html

@@ -48,9 +44,8 @@ book for more information:

``` -If you have questions or would like to brainstorm ideas in general, don't hesitate to start -a thread in our [zulip channel](https://julialang.zulipchat.com/#narrow/stream/275558-meshes.2Ejl). -We are happy to improve the ecosystem to meet user's needs. +If you have questions or would like to brainstorm ideas, don't hesitate to start a thread in our +[zulip channel](https://julialang.zulipchat.com/#narrow/stream/275558-meshes.2Ejl). ## Installation From 7cb69dbb0dde547bf95aaa766b31f56e49dc2f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 26 Jul 2024 15:30:25 -0300 Subject: [PATCH 208/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 31fd79126..53b6e6090 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.2" +version = "0.47.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 49feecd42765c222510188ca179a16e85d3f6918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 26 Jul 2024 18:08:06 -0300 Subject: [PATCH 209/423] Rename AbstractManifold to Manifold --- src/domains.jl | 2 +- src/geometries.jl | 2 +- src/manifolds.jl | 11 ++++++++--- src/mesh.jl | 4 ++-- src/mesh/cartesiangrid.jl | 4 ++-- src/mesh/simplemesh.jl | 2 +- src/mesh/transformedmesh.jl | 4 ++-- src/multigeoms.jl | 14 +++++++------- src/polytopes.jl | 6 +++--- src/polytopes/ngon.jl | 8 ++++---- src/polytopes/polyarea.jl | 2 +- src/polytopes/ring.jl | 6 +++--- src/polytopes/rope.jl | 2 +- src/primitives.jl | 2 +- src/primitives/ball.jl | 4 ++-- src/primitives/bezier.jl | 2 +- src/primitives/box.jl | 6 +++--- src/primitives/cone.jl | 2 +- src/primitives/conesurface.jl | 2 +- src/primitives/ellipsoid.jl | 4 ++-- src/primitives/line.jl | 2 +- src/primitives/paraboloidsurface.jl | 4 ++-- src/primitives/plane.jl | 2 +- src/primitives/point.jl | 4 ++-- src/primitives/ray.jl | 2 +- src/primitives/sphere.jl | 4 ++-- src/primitives/torus.jl | 4 ++-- src/sets.jl | 6 +++--- src/subdomains.jl | 4 ++-- src/trajecs.jl | 4 ++-- test/dummy.jl | 2 +- 31 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/domains.jl b/src/domains.jl index 4b852c289..20ce6fc3e 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -9,7 +9,7 @@ A domain is an indexable collection of geometries (e.g. mesh) in a given manifold `M` with point coordinates specified in a coordinate reference system `CRS`. """ -abstract type Domain{M<:AbstractManifold,C<:CRS} end +abstract type Domain{M<:Manifold,C<:CRS} end """ element(domain, ind) diff --git a/src/geometries.jl b/src/geometries.jl index cdf10717e..30c710fb2 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -8,7 +8,7 @@ A geometry in a given manifold `M` with point coordinates specified in a coordinate reference system `CRS`. """ -abstract type Geometry{M<:AbstractManifold,C<:CRS} end +abstract type Geometry{M<:Manifold,C<:CRS} end Broadcast.broadcastable(g::Geometry) = Ref(g) diff --git a/src/manifolds.jl b/src/manifolds.jl index f7e515a44..403d13cf7 100644 --- a/src/manifolds.jl +++ b/src/manifolds.jl @@ -2,18 +2,23 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -abstract type AbstractManifold end +""" + Manifold + +A manifold where geometries and domains are defined. +""" +abstract type Manifold end """ 𝔼{Dim} Euclidean manifold with dimension `Dim`. """ -abstract type 𝔼{Dim} <: AbstractManifold end +abstract type 𝔼{Dim} <: Manifold end """ 🌐 Ellipsoid manifold for geodesic geometry. """ -abstract type 🌐 <: AbstractManifold end +abstract type 🌐 <: Manifold end diff --git a/src/mesh.jl b/src/mesh.jl index ea107cca7..92a94df9c 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -9,7 +9,7 @@ A mesh of geometries in a given manifold `M` with point coordinates specified in a coordinate reference system `CRS`. Unlike a general domain, a mesh has a well-defined topology `TP`. """ -abstract type Mesh{M<:AbstractManifold,C<:CRS,TP<:Topology} <: Domain{M,C} end +abstract type Mesh{M<:Manifold,C<:CRS,TP<:Topology} <: Domain{M,C} end """ vertex(mesh, ind) @@ -145,7 +145,7 @@ end A grid of geometries in a given manifold `M` with points coordinates specified in a coordinate reference system `CRS`, which is embedded in `Dim` dimensions. """ -const Grid{M<:AbstractManifold,C<:CRS,Dim} = Mesh{M,C,GridTopology{Dim}} +const Grid{M<:Manifold,C<:CRS,Dim} = Mesh{M,C,GridTopology{Dim}} """ vertex(grid, ijk) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index 89ff2190f..f38456276 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -50,7 +50,7 @@ Create a 1D grid from -1 to 1 with 100 segments: julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` """ -struct CartesianGrid{C<:CRS,Mₚ<:AbstractManifold,Dim,ℒ<:Len} <: Grid{𝔼{Dim},C,Dim} +struct CartesianGrid{C<:CRS,Mₚ<:Manifold,Dim,ℒ<:Len} <: Grid{𝔼{Dim},C,Dim} origin::Point{Mₚ,C} spacing::NTuple{Dim,ℒ} offset::Dims{Dim} @@ -61,7 +61,7 @@ struct CartesianGrid{C<:CRS,Mₚ<:AbstractManifold,Dim,ℒ<:Len} <: Grid{𝔼{Di spacing::NTuple{Dim,ℒ}, offset::Dims{Dim}, topology::GridTopology{Dim} - ) where {C<:CRS,Mₚ<:AbstractManifold,Dim,ℒ<:Len} + ) where {C<:CRS,Mₚ<:Manifold,Dim,ℒ<:Len} if !all(>(zero(ℒ)), spacing) throw(ArgumentError("spacing must be positive")) end diff --git a/src/mesh/simplemesh.jl b/src/mesh/simplemesh.jl index ca966424a..fe40785f6 100644 --- a/src/mesh/simplemesh.jl +++ b/src/mesh/simplemesh.jl @@ -31,7 +31,7 @@ See also [`Topology`](@ref), [`GridTopology`](@ref), of the mesh to a [`HalfEdgeTopology`](@ref) instead of a [`SimpleTopology`](@ref). """ -struct SimpleMesh{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}},TP<:Topology} <: Mesh{M,C,TP} +struct SimpleMesh{M<:Manifold,C<:CRS,V<:AbstractVector{Point{M,C}},TP<:Topology} <: Mesh{M,C,TP} vertices::V topology::TP end diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 753716ac0..9a8c7feb5 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,7 +7,7 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{M<:AbstractManifold,C<:CRS,TP<:Topology,MS<:Mesh{M,C,TP},TR<:Transform} <: Mesh{M,C,TP} +struct TransformedMesh{M<:Manifold,C<:CRS,TP<:Topology,MS<:Mesh{M,C,TP},TR<:Transform} <: Mesh{M,C,TP} mesh::MS transform::TR end @@ -24,7 +24,7 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{M<:AbstractManifold,C<:CRS,Dim,G<:Grid{M,C,Dim},TR} = TransformedMesh{M,C,GridTopology{Dim},G,TR} +const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid{M,C,Dim},TR} = TransformedMesh{M,C,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) diff --git a/src/multigeoms.jl b/src/multigeoms.jl index 169a38efb..1f4ee0d0b 100644 --- a/src/multigeoms.jl +++ b/src/multigeoms.jl @@ -15,7 +15,7 @@ multiple polygons as a single entity (e.g. country with islands). - Type aliases are [`MultiPoint`](@ref), [`MultiSegment`](@ref), [`MultiRope`](@ref), [`MultiRing`](@ref), [`MultiPolygon`](@ref). """ -struct Multi{M<:AbstractManifold,C<:CRS,G<:Geometry{M,C}} <: Geometry{M,C} +struct Multi{M<:Manifold,C<:CRS,G<:Geometry{M,C}} <: Geometry{M,C} geoms::Vector{G} end @@ -23,12 +23,12 @@ end Multi(geoms) = Multi(collect(geoms)) # type aliases for convenience -const MultiPoint{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Point{M,C}} -const MultiSegment{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Segment{M,C}} -const MultiRope{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Rope{M,C}} -const MultiRing{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Ring{M,C}} -const MultiPolygon{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Polygon{M,C}} -const MultiPolyhedron{M<:AbstractManifold,C<:CRS} = Multi{M,C,<:Polyhedron{M,C}} +const MultiPoint{M<:Manifold,C<:CRS} = Multi{M,C,<:Point{M,C}} +const MultiSegment{M<:Manifold,C<:CRS} = Multi{M,C,<:Segment{M,C}} +const MultiRope{M<:Manifold,C<:CRS} = Multi{M,C,<:Rope{M,C}} +const MultiRing{M<:Manifold,C<:CRS} = Multi{M,C,<:Ring{M,C}} +const MultiPolygon{M<:Manifold,C<:CRS} = Multi{M,C,<:Polygon{M,C}} +const MultiPolyhedron{M<:Manifold,C<:CRS} = Multi{M,C,<:Polyhedron{M,C}} paramdim(m::Multi) = maximum(paramdim, m.geoms) diff --git a/src/polytopes.jl b/src/polytopes.jl index 4bc45111d..5bfd6059a 100644 --- a/src/polytopes.jl +++ b/src/polytopes.jl @@ -25,19 +25,19 @@ have (K-1)-polytopes in common. See . - Type aliases are `Chain`, `Polygon`, `Polyhedron`. """ -abstract type Polytope{K,M<:AbstractManifold,C<:CRS} <: Geometry{M,C} end +abstract type Polytope{K,M<:Manifold,C<:CRS} <: Geometry{M,C} end # heper macro to define polytopes macro polytope(type, K, N) structexpr = if K == 3 quote - struct $type{C<:CRS,Mₚ<:AbstractManifold} <: Polytope{$K,𝔼{3},C} + struct $type{C<:CRS,Mₚ<:Manifold} <: Polytope{$K,𝔼{3},C} vertices::NTuple{$N,Point{Mₚ,C}} end end else quote - struct $type{M<:AbstractManifold,C<:CRS} <: Polytope{$K,M,C} + struct $type{M<:Manifold,C<:CRS} <: Polytope{$K,M,C} vertices::NTuple{$N,Point{M,C}} end end diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 27f2a41c3..6c2835f29 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -20,9 +20,9 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. - Type aliases are `Triangle`, `Quadrangle`, `Pentagon`, `Hexagon`, `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ -struct Ngon{N,M<:AbstractManifold,C<:CRS} <: Polygon{M,C} +struct Ngon{N,M<:Manifold,C<:CRS} <: Polygon{M,C} vertices::NTuple{N,Point{M,C}} - function Ngon{N,M,C}(vertices) where {N,M<:AbstractManifold,C<:CRS} + function Ngon{N,M,C}(vertices) where {N,M<:Manifold,C<:CRS} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) end @@ -30,11 +30,11 @@ struct Ngon{N,M<:AbstractManifold,C<:CRS} <: Polygon{M,C} end end -Ngon{N}(vertices::NTuple{N,Point{M,C}}) where {N,M<:AbstractManifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon{N}(vertices::NTuple{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) Ngon{N}(vertices::Vararg{P,N}) where {N,P<:Point} = Ngon{N}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,Point{M,C}}) where {N,M<:AbstractManifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon(vertices::NTuple{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index f84606124..4d0e6b162 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -14,7 +14,7 @@ vector of tuples with coordinates for convenience, in which case the first point should *not* be repeated at the end of the vector. """ -struct PolyArea{M<:AbstractManifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} <: Polygon{M,C} +struct PolyArea{M<:Manifold,C<:CRS,R<:Ring{M,C},V<:AbstractVector{R}} <: Polygon{M,C} rings::V end diff --git a/src/polytopes/ring.jl b/src/polytopes/ring.jl index 60be8cc8d..af552517a 100644 --- a/src/polytopes/ring.jl +++ b/src/polytopes/ring.jl @@ -9,10 +9,10 @@ A closed polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Rope`](@ref). """ -struct Ring{M<:AbstractManifold,C<:CRS,V<:CircularVector{Point{M,C}}} <: Chain{M,C} +struct Ring{M<:Manifold,C<:CRS,V<:CircularVector{Point{M,C}}} <: Chain{M,C} vertices::V - function Ring{M,C,V}(vertices) where {M<:AbstractManifold,C<:CRS,V<:CircularVector{Point{M,C}}} + function Ring{M,C,V}(vertices) where {M<:Manifold,C<:CRS,V<:CircularVector{Point{M,C}}} if first(vertices) == last(vertices) && length(vertices) ≥ 2 throw(ArgumentError(""" First and last vertices of `Ring` constructor must be different @@ -24,7 +24,7 @@ struct Ring{M<:AbstractManifold,C<:CRS,V<:CircularVector{Point{M,C}}} <: Chain{M end end -Ring(vertices::CircularVector{Point{M,C}}) where {M<:AbstractManifold,C<:CRS} = Ring{M,C,typeof(vertices)}(vertices) +Ring(vertices::CircularVector{Point{M,C}}) where {M<:Manifold,C<:CRS} = Ring{M,C,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) diff --git a/src/polytopes/rope.jl b/src/polytopes/rope.jl index a346d057d..0cae2a71d 100644 --- a/src/polytopes/rope.jl +++ b/src/polytopes/rope.jl @@ -9,7 +9,7 @@ An open polygonal chain from a sequence of points `p1`, `p2`, ..., `pn`. See also [`Chain`](@ref) and [`Ring`](@ref). """ -struct Rope{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Chain{M,C} +struct Rope{M<:Manifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Chain{M,C} vertices::V end diff --git a/src/primitives.jl b/src/primitives.jl index a1a3638f6..7af61b8d2 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -10,7 +10,7 @@ entity with no parts (a.k.a. atomic). For example, a sphere is a primitive described in terms of a mathematical expression involving a metric and a radius. See . """ -abstract type Primitive{M<:AbstractManifold,C<:CRS} <: Geometry{M,C} end +abstract type Primitive{M<:Manifold,C<:CRS} <: Geometry{M,C} end function Base.show(io::IO, geom::Primitive) name = prettyname(geom) diff --git a/src/primitives/ball.jl b/src/primitives/ball.jl index 144420c20..f6b79075b 100644 --- a/src/primitives/ball.jl +++ b/src/primitives/ball.jl @@ -9,10 +9,10 @@ A ball with `center` and `radius`. See also [`Sphere`](@ref). """ -struct Ball{M<:AbstractManifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} +struct Ball{M<:Manifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} center::Point{M,C} radius::ℒ - Ball(center::Point{M,C}, radius::ℒ) where {M<:AbstractManifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) + Ball(center::Point{M,C}, radius::ℒ) where {M<:Manifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) end Ball(center::Point, radius) = Ball(center, addunit(radius, u"m")) diff --git a/src/primitives/bezier.jl b/src/primitives/bezier.jl index 722200f2d..3e35edcb0 100644 --- a/src/primitives/bezier.jl +++ b/src/primitives/bezier.jl @@ -20,7 +20,7 @@ large number of points but less precise, can be used via BezierCurve([(0.,0.),(1.,-1.)]) ``` """ -struct BezierCurve{M<:AbstractManifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Primitive{M,C} +struct BezierCurve{M<:Manifold,C<:CRS,V<:AbstractVector{Point{M,C}}} <: Primitive{M,C} controls::V end diff --git a/src/primitives/box.jl b/src/primitives/box.jl index ce5559ffd..22dba8d53 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -27,17 +27,17 @@ Construct a geodesic box on the ellipsoid: Box(Point(LatLon(0, 0)), Point(LatLon(1, 1))) ``` """ -struct Box{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} +struct Box{M<:Manifold,C<:CRS} <: Primitive{M,C} min::Point{M,C} max::Point{M,C} - function Box{M,C}(min, max) where {M<:AbstractManifold,C<:CRS} + function Box{M,C}(min, max) where {M<:Manifold,C<:CRS} assertion(min ≤ max, "`min` must be less than or equal to `max`") new(min, max) end end -Box(min::Point{M,C}, max::Point{M,C}) where {M<:AbstractManifold,C<:CRS} = Box{M,C}(min, max) +Box(min::Point{M,C}, max::Point{M,C}) where {M<:Manifold,C<:CRS} = Box{M,C}(min, max) Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) diff --git a/src/primitives/cone.jl b/src/primitives/cone.jl index 777a25e81..716239ffa 100644 --- a/src/primitives/cone.jl +++ b/src/primitives/cone.jl @@ -10,7 +10,7 @@ See . See also [`ConeSurface`](@ref). """ -struct Cone{C<:CRS,D<:Disk{C},Mₚ<:AbstractManifold} <: Primitive{𝔼{3},C} +struct Cone{C<:CRS,D<:Disk{C},Mₚ<:Manifold} <: Primitive{𝔼{3},C} base::D apex::Point{Mₚ,C} end diff --git a/src/primitives/conesurface.jl b/src/primitives/conesurface.jl index 872a1a838..9b95e7a67 100644 --- a/src/primitives/conesurface.jl +++ b/src/primitives/conesurface.jl @@ -10,7 +10,7 @@ See . See also [`Cone`](@ref). """ -struct ConeSurface{C<:CRS,D<:Disk{C},Mₚ<:AbstractManifold} <: Primitive{𝔼{3},C} +struct ConeSurface{C<:CRS,D<:Disk{C},Mₚ<:Manifold} <: Primitive{𝔼{3},C} base::D apex::Point{Mₚ,C} end diff --git a/src/primitives/ellipsoid.jl b/src/primitives/ellipsoid.jl index 12866f264..da5c26c0a 100644 --- a/src/primitives/ellipsoid.jl +++ b/src/primitives/ellipsoid.jl @@ -7,11 +7,11 @@ A 3D ellipsoid with given `radii`, `center` and `rotation`. """ -struct Ellipsoid{C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} <: Primitive{𝔼{3},C} +struct Ellipsoid{C<:CRS,Mₚ<:Manifold,R,ℒ<:Len} <: Primitive{𝔼{3},C} radii::NTuple{3,ℒ} center::Point{Mₚ,C} rotation::R - Ellipsoid(radii::NTuple{3,ℒ}, center::Point{Mₚ,C}, rotation::R) where {C<:CRS,Mₚ<:AbstractManifold,R,ℒ<:Len} = + Ellipsoid(radii::NTuple{3,ℒ}, center::Point{Mₚ,C}, rotation::R) where {C<:CRS,Mₚ<:Manifold,R,ℒ<:Len} = new{C,Mₚ,R,float(ℒ)}(radii, center, rotation) end diff --git a/src/primitives/line.jl b/src/primitives/line.jl index 6af4348bd..f5ec752a3 100644 --- a/src/primitives/line.jl +++ b/src/primitives/line.jl @@ -9,7 +9,7 @@ A line passing through points `a` and `b`. See also [`Segment`](@ref). """ -struct Line{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} +struct Line{M<:Manifold,C<:CRS} <: Primitive{M,C} a::Point{M,C} b::Point{M,C} end diff --git a/src/primitives/paraboloidsurface.jl b/src/primitives/paraboloidsurface.jl index 0ee8206ae..0acee3309 100644 --- a/src/primitives/paraboloidsurface.jl +++ b/src/primitives/paraboloidsurface.jl @@ -32,11 +32,11 @@ Same as above, but here the apex is at `Apex(0, 0, 0)`. See also . """ -struct ParaboloidSurface{C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} <: Primitive{𝔼{3},C} +struct ParaboloidSurface{C<:CRS,Mₚ<:Manifold,ℒ<:Len} <: Primitive{𝔼{3},C} apex::Point{Mₚ,C} radius::ℒ focallength::ℒ - ParaboloidSurface(apex::Point{Mₚ,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} = + ParaboloidSurface(apex::Point{Mₚ,C}, radius::ℒ, focallength::ℒ) where {C<:CRS,Mₚ<:Manifold,ℒ<:Len} = new{C,Mₚ,float(ℒ)}(apex, radius, focallength) end diff --git a/src/primitives/plane.jl b/src/primitives/plane.jl index a2aa87624..1c539a4f2 100644 --- a/src/primitives/plane.jl +++ b/src/primitives/plane.jl @@ -13,7 +13,7 @@ defined by non-parallel vectors `u` and `v`. Alternatively specify point `p` and a given normal vector `n` to the plane. """ -struct Plane{C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3}} <: Primitive{𝔼{3},C} +struct Plane{C<:CRS,Mₚ<:Manifold,V<:Vec{3}} <: Primitive{𝔼{3},C} p::Point{Mₚ,C} u::V v::V diff --git a/src/primitives/point.jl b/src/primitives/point.jl index bd057f30d..61b856f47 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -33,11 +33,11 @@ Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design algorithms assume a continuous space. The conversion to float avoids `InexactError` and other unexpected results. """ -struct Point{M<:AbstractManifold,C<:CRS} <: Primitive{M,C} +struct Point{M<:Manifold,C<:CRS} <: Primitive{M,C} coords::C end -Point{M}(coords::C) where {M<:AbstractManifold,C<:CRS} = Point{M,C}(coords) +Point{M}(coords::C) where {M<:Manifold,C<:CRS} = Point{M,C}(coords) Point(coords::CRS) = Point{_manifold(coords)}(coords) diff --git a/src/primitives/ray.jl b/src/primitives/ray.jl index 3859135ba..14183f227 100644 --- a/src/primitives/ray.jl +++ b/src/primitives/ray.jl @@ -9,7 +9,7 @@ A ray originating at point `p`, pointed in direction `v`. It can be called as `r(t)` with `t > 0` to cast it at `p + t * v`. """ -struct Ray{C<:CRS,Mₚ<:AbstractManifold,Dim,V<:Vec{Dim}} <: Primitive{𝔼{Dim},C} +struct Ray{C<:CRS,Mₚ<:Manifold,Dim,V<:Vec{Dim}} <: Primitive{𝔼{Dim},C} p::Point{Mₚ,C} v::V end diff --git a/src/primitives/sphere.jl b/src/primitives/sphere.jl index db66503a1..d4f2b9062 100644 --- a/src/primitives/sphere.jl +++ b/src/primitives/sphere.jl @@ -9,10 +9,10 @@ A sphere with `center` and `radius`. See also [`Ball`](@ref). """ -struct Sphere{M<:AbstractManifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} +struct Sphere{M<:Manifold,C<:CRS,ℒ<:Len} <: Primitive{M,C} center::Point{M,C} radius::ℒ - Sphere(center::Point{M,C}, radius::ℒ) where {M<:AbstractManifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) + Sphere(center::Point{M,C}, radius::ℒ) where {M<:Manifold,C<:CRS,ℒ<:Len} = new{M,C,float(ℒ)}(center, radius) end Sphere(center::Point, radius) = Sphere(center, addunit(radius, u"m")) diff --git a/src/primitives/torus.jl b/src/primitives/torus.jl index dfeca2126..d228f4fa5 100644 --- a/src/primitives/torus.jl +++ b/src/primitives/torus.jl @@ -9,12 +9,12 @@ A torus centered at `center` with axis of revolution directed by `normal` and with radii `major` and `minor`. """ -struct Torus{C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3},ℒ<:Len} <: Primitive{𝔼{3},C} +struct Torus{C<:CRS,Mₚ<:Manifold,V<:Vec{3},ℒ<:Len} <: Primitive{𝔼{3},C} center::Point{Mₚ,C} normal::V major::ℒ minor::ℒ - Torus(center::Point{Mₚ,C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,V<:Vec{3},ℒ<:Len} = + Torus(center::Point{Mₚ,C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,Mₚ<:Manifold,V<:Vec{3},ℒ<:Len} = new{C,Mₚ,V,float(ℒ)}(center, normal, major, minor) end diff --git a/src/sets.jl b/src/sets.jl index adac64bf7..20f82cc3f 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -15,7 +15,7 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` """ -struct GeometrySet{M<:AbstractManifold,C<:CRS,G<:Geometry{M,C}} <: Domain{M,C} +struct GeometrySet{M<:Manifold,C<:CRS,G<:Geometry{M,C}} <: Domain{M,C} geoms::Vector{G} end @@ -37,7 +37,7 @@ Base.vcat(d1::Domain, d2::GeometrySet) = GeometrySet(vcat(collect(d1), d2.geoms) # SPECIAL CASE: POINT SET # ------------------------ -const PointSet{M<:AbstractManifold,C<:CRS} = GeometrySet{M,C,Point{M,C}} +const PointSet{M<:Manifold,C<:CRS} = GeometrySet{M,C,Point{M,C}} """ PointSet(points) @@ -55,7 +55,7 @@ julia> PointSet([(1,2,3), (4,5,6)]) julia> PointSet((1,2,3), (4,5,6)) ``` """ -PointSet(points::AbstractVector{Point{M,C}}) where {M<:AbstractManifold,C<:CRS} = PointSet{M,C}(points) +PointSet(points::AbstractVector{Point{M,C}}) where {M<:Manifold,C<:CRS} = PointSet{M,C}(points) PointSet(points::Vararg{P}) where {P<:Point} = PointSet(collect(points)) PointSet(coords::AbstractVector{TP}) where {TP<:Tuple} = PointSet(Point.(coords)) PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) diff --git a/src/subdomains.jl b/src/subdomains.jl index e074d199a..ac6ab701d 100644 --- a/src/subdomains.jl +++ b/src/subdomains.jl @@ -11,7 +11,7 @@ A partial view of a `domain` containing only the elements at `indices`. """ -struct SubDomain{M<:AbstractManifold,C<:CRS,D<:Domain{M,C},I<:AbstractVector{Int}} <: Domain{M,C} +struct SubDomain{M<:Manifold,C<:CRS,D<:Domain{M,C},I<:AbstractVector{Int}} <: Domain{M,C} domain::D inds::I end @@ -25,7 +25,7 @@ SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[ A subgrid of geometries in a given manifold `M` with point coordinates specified in a coordinate reference system `CRS`, which is embedded in `Dim` dimensions. """ -const SubGrid{M<:AbstractManifold,C<:CRS,Dim} = SubDomain{M,C,<:Grid{M,C,Dim}} +const SubGrid{M<:Manifold,C<:CRS,Dim} = SubDomain{M,C,<:Grid{M,C,Dim}} # ----------------- # DOMAIN INTERFACE diff --git a/src/trajecs.jl b/src/trajecs.jl index 0f657e8f3..22999248a 100644 --- a/src/trajecs.jl +++ b/src/trajecs.jl @@ -7,10 +7,10 @@ Trajectory of cylinders of given `radius` positioned at the `centroids`. """ -struct CylindricalTrajectory{C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} <: Domain{𝔼{3},C} +struct CylindricalTrajectory{C<:CRS,Mₚ<:Manifold,ℒ<:Len} <: Domain{𝔼{3},C} centroids::Vector{Point{Mₚ,C}} radius::ℒ - CylindricalTrajectory(centroids::Vector{Point{Mₚ,C}}, radius::ℒ) where {C<:CRS,Mₚ<:AbstractManifold,ℒ<:Len} = + CylindricalTrajectory(centroids::Vector{Point{Mₚ,C}}, radius::ℒ) where {C<:CRS,Mₚ<:Manifold,ℒ<:Len} = new{C,Mₚ,float(ℒ)}(centroids, radius) end diff --git a/test/dummy.jl b/test/dummy.jl index 19ca4b980..e6400a806 100644 --- a/test/dummy.jl +++ b/test/dummy.jl @@ -1,5 +1,5 @@ # dummy type implementing the Domain trait -struct DummyDomain{M<:Meshes.AbstractManifold,C<:CRS} <: Domain{M,C} +struct DummyDomain{M<:Meshes.Manifold,C<:CRS} <: Domain{M,C} origin::Point{M,C} end From e416adf91ad22affeaaf3a8116880bf4e68303fe Mon Sep 17 00:00:00 2001 From: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com> Date: Sun, 28 Jul 2024 17:59:20 +0100 Subject: [PATCH 210/423] Update mention of ExactPredicates.jl to also mention AdaptivePredicates.jl (#963) * Update predicates.md * Update predicates.md --- docs/src/predicates.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/predicates.md b/docs/src/predicates.md index 57551e1e8..86daf080f 100644 --- a/docs/src/predicates.md +++ b/docs/src/predicates.md @@ -14,9 +14,9 @@ For example, rather than checking if a point `p` is exactly in a sphere of radiu on the point type, so `p` might be slightly outside the sphere but still be considered as being inside. -Exact arithmetic is expensive to apply and approximations are typically sufficient; -exact predicates are available in [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl) -if you need them. +Robust predicates are often expensive to apply and approximations typically suffice. +If needed, consider [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl) or +[AdaptivePredicates.jl](https://github.com/JuliaGeometry/AdaptivePredicates.jl). ## isparametrized From d5079aab4dc0d30ca9cf5220a2d9969b196721b9 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Sun, 28 Jul 2024 14:51:55 -0400 Subject: [PATCH 211/423] Allow custom atols using scoped values (#962) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow custom atols using Preferences * add docs * use ScopedValues instead of Preferences * update docs * Apply suggestions from code review * Adjust documentation * Update src/tolerances.jl Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com> * Update src/tolerances.jl Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com> * revert inlining * adjust docs slightly * add inferred tests --------- Co-authored-by: Júlio Hoffimann Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com> --- Project.toml | 2 ++ docs/make.jl | 3 ++- docs/src/tolerances.md | 15 +++++++++++++++ src/Meshes.jl | 1 + src/tolerances.jl | 7 +++++-- test/tolerances.jl | 4 ++++ 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 docs/src/tolerances.md diff --git a/Project.toml b/Project.toml index 53b6e6090..41069bd72 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" +ScopedValues = "7e506255-f358-4e82-b7e4-beb19740aa63" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" @@ -38,6 +39,7 @@ Makie = "0.21" NearestNeighbors = "0.4" Random = "1.9" Rotations = "1.5.1" +ScopedValues = "1.2" SparseArrays = "1.9" StaticArrays = "1.0" StatsBase = "0.33, 0.34" diff --git a/docs/make.jl b/docs/make.jl index f0caeee6c..d5aa54458 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -34,7 +34,8 @@ makedocs( "Transforms" => "transforms.md", "Random" => "rand.md", "Visualization" => "visualization.md", - "Input/Output" => "io.md" + "Input/Output" => "io.md", + "Tolerances" => "tolerances.md" ], "Contributing" => ["contributing/guidelines.md"], "About" => ["License" => "about/license.md"], diff --git a/docs/src/tolerances.md b/docs/src/tolerances.md new file mode 100644 index 000000000..c11b8b701 --- /dev/null +++ b/docs/src/tolerances.md @@ -0,0 +1,15 @@ +# Tolerances + +The absolute tolerance used for floating point comparisons is hard-coded in +the project to `1e-10` for `Float64` and to `1f-5` for `Float32`. You can use +[ScopedValues.jl](https://github.com/vchuravy/ScopedValues.jl) to customize +these tolerance values in specific computations: + +```julia +using Meshes +using ScopedValues + +with(Meshes.ATOL64 => 1e-9, Meshes.ATOL32 => 1f-4) do + # do your computations with custom tolerances +end +``` diff --git a/src/Meshes.jl b/src/Meshes.jl index c61c831bb..eaec4c31c 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -26,6 +26,7 @@ using DelaunayTriangulation: triangulate, voronoi using DelaunayTriangulation: each_solid_triangle using DelaunayTriangulation: each_polygon using DelaunayTriangulation: get_polygon_points +using ScopedValues: ScopedValue using Base.Cartesian: @nloops, @nref, @ntuple using Base: @propagate_inbounds diff --git a/src/tolerances.jl b/src/tolerances.jl index c6698859d..b5aa60b24 100644 --- a/src/tolerances.jl +++ b/src/tolerances.jl @@ -2,6 +2,9 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ +const ATOL64 = ScopedValue(1e-10) +const ATOL32 = ScopedValue(1f-5) + """ atol(T) atol(x::T) @@ -15,8 +18,8 @@ isapprox(a::T, b::T, atol=atol(T)) ``` """ atol(x) = atol(typeof(x)) -atol(::Type{Float64}) = 1.0e-10 -atol(::Type{Float32}) = 1.0f-5 +atol(::Type{Float64}) = ATOL64[] +atol(::Type{Float32}) = ATOL32[] atol(ℒ::Type{<:Len}) = atol(numtype(ℒ)) * unit(ℒ) atol(𝒜::Type{<:Area}) = atol(numtype(𝒜))^2 * unit(𝒜) atol(𝒱::Type{<:Vol}) = atol(numtype(𝒱))^3 * unit(𝒱) diff --git a/test/tolerances.jl b/test/tolerances.jl index 2e4da9908..34e67ea99 100644 --- a/test/tolerances.jl +++ b/test/tolerances.jl @@ -17,4 +17,8 @@ @test atol(zero(ℒ)) == atol(ℒ) @test atol(zero(𝒜)) == atol(𝒜) @test atol(zero(𝒱)) == atol(𝒱) + @inferred atol(T) + @inferred atol(ℒ) + @inferred atol(𝒜) + @inferred atol(𝒱) end From bf37a142cd4872d73ff2a0531df42b51257426ca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 17:53:33 -0300 Subject: [PATCH 212/423] [AUTO] JuliaFormatter.jl run (#964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :robot: Format .jl files * Update src/tolerances.jl Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --------- Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> Co-authored-by: Júlio Hoffimann Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --- src/tolerances.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tolerances.jl b/src/tolerances.jl index b5aa60b24..3ba29034c 100644 --- a/src/tolerances.jl +++ b/src/tolerances.jl @@ -2,8 +2,8 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -const ATOL64 = ScopedValue(1e-10) -const ATOL32 = ScopedValue(1f-5) +const ATOL64 = ScopedValue(1.0e-10) +const ATOL32 = ScopedValue(1.0f-5) """ atol(T) From 33c659fc9e1dbf7619f78539b0b955b6c24572da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 29 Jul 2024 09:20:13 -0300 Subject: [PATCH 213/423] Refactor flat util --- src/utils.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index a501c2596..6babdca8c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -73,8 +73,7 @@ Flatten coordinates of point `p` to Cartesian coordinates, ignoring the original units of the coordinate reference system. """ flat(p::Point) = Point(flat(coords(p))) -flat(c::LatLon) = Cartesian{datum(c)}(ustrip(c.lat), ustrip(c.lon)) -flat(c::CRS) = convert(Cartesian, c) +flat(c::CRS) = Cartesian{datum(c)}(CoordRefSystems.raw(c)) """ signarea(A, B, C) From 47df422853bb6c0225c142d617526142205f432a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 29 Jul 2024 11:20:30 -0300 Subject: [PATCH 214/423] Perturbation technique in clip --- src/clipping/sutherlandhodgman.jl | 21 +++++++++++++++++++-- test/intersections.jl | 11 +++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/clipping/sutherlandhodgman.jl b/src/clipping/sutherlandhodgman.jl index 819359681..d650981a6 100644 --- a/src/clipping/sutherlandhodgman.jl +++ b/src/clipping/sutherlandhodgman.jl @@ -49,9 +49,9 @@ function clip(ring::Ring, other::Ring, ::SutherlandHodgman) push!(u, r₁) elseif isinside₁ && !isinside₂ push!(u, r₁) - push!(u, lᵣ ∩ lₒ) + push!(u, intersectpoint(lᵣ, lₒ)) elseif !isinside₁ && isinside₂ - push!(u, lᵣ ∩ lₒ) + push!(u, intersectpoint(lᵣ, lₒ)) end end @@ -60,3 +60,20 @@ function clip(ring::Ring, other::Ring, ::SutherlandHodgman) isempty(r) ? nothing : Ring(unique(r)) end + +# helper function to find any intersection point +# between crossing or overlapping lines +function intersectpoint(l₁::Line, l₂::Line) + intersection(l₁, l₂) do I + if type(I) == Crossing # get intersection point + get(I) + elseif type(I) == Overlapping # perturb line and retry + ℒ = lentype(l₁) + T = numtype(ℒ) + δx = rand((1, -1)) * rand(T) * atol(ℒ) + δy = rand((1, -1)) * rand(T) * atol(ℒ) + l₁′ = Line(l₁(0), l₁(1) + Vec(δx, δy)) + intersectpoint(l₁′, l₂) + end + end +end diff --git a/test/intersections.jl b/test/intersections.jl index 3e110f510..e268e9e62 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1143,6 +1143,17 @@ r = Ray(cart(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) @test intersection(r, o) |> type == NotIntersecting @test isnothing(r ∩ o) + + t = Triangle(cart(0.9356498598903396, 6.5), cart(1.3571428571428377, 6.5), cart(1.0, 7.0)) + q = Quadrangle(cart(0.0, 0.0), cart(6.0, 0.0), cart(1.0, 7.0), cart(1.0, 6.0)) + @test intersection(t, q) |> type == Intersecting + @test t ∩ q isa PolyArea + @test q ∩ t isa PolyArea + + t1 = Triangle(cart(0.0, 0.0), cart(0.0, 1.000000000000001), cart(1.0, 1.0)) + t2 = Triangle(cart(0.0, 1.0), cart(0.0, 2.0), cart(1.0, 1.000000000001)) + @test intersection(t1, t2) |> type == Intersecting + @test t1 ∩ t2 isa PolyArea end @testset "Polygons" begin From 455c8b74c373346b0504ae9a8c60146771f6b4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 29 Jul 2024 11:21:05 -0300 Subject: [PATCH 215/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 41069bd72..bd9ef698b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.3" +version = "0.47.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 24858f72799c5d5ac0b1533091e52e65ea46d9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 29 Jul 2024 14:28:26 -0300 Subject: [PATCH 216/423] Group utils in separate files --- src/utils.jl | 244 ++------------------------------------------ src/utils/assert.jl | 19 ++++ src/utils/basic.jl | 55 ++++++++++ src/utils/cmp.jl | 26 +++++ src/utils/crs.jl | 24 +++++ src/utils/misc.jl | 106 +++++++++++++++++++ src/utils/units.jl | 28 +++++ 7 files changed, 264 insertions(+), 238 deletions(-) create mode 100644 src/utils/assert.jl create mode 100644 src/utils/basic.jl create mode 100644 src/utils/cmp.jl create mode 100644 src/utils/crs.jl create mode 100644 src/utils/misc.jl create mode 100644 src/utils/units.jl diff --git a/src/utils.jl b/src/utils.jl index 6babdca8c..adc3a611f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -5,241 +5,9 @@ # auxiliary type for dispatch purposes const GeometryOrDomain = Union{Geometry,Domain} -""" - assertion(cond, msg) - -Throws an `AssertionError(msg)` if `cond` is `false`. -""" -assertion(cond, msg) = cond || throw(AssertionError(msg)) - -""" - checkdim(geom, dim) - -Throws an `ArgumentError` if the `embeddim` of the geometry `geom` -is different than the specified dimension `dim`. -""" -checkdim(geom, dim) = - embeddim(geom) ≠ dim && throw(ArgumentError("geometry must be embedded in $dim-dimensional space")) - -""" - constructor(G) - -Given a (parametric) type `G{T₁,T₂,...}`, return the type `G`. -""" -constructor(G::Type{<:GeometryOrDomain}) = getfield(Meshes, nameof(G)) - -""" - fitdims(dims, D) - -Fit tuple `dims` to a given length `D` by repeating the last dimension. -""" -function fitdims(dims::Dims{N}, D) where {N} - ntuple(i -> i ≤ N ? dims[i] : last(dims), D) -end - -""" - collectat(iter, inds) - -Collect iterator `iter` at indices `inds` without materialization. -""" -function collectat(iter, inds) - if isempty(inds) - eltype(iter)[] - else - m = maximum(inds) - e = Iterators.enumerate(iter) - w = Iterators.takewhile(x -> (first(x) ≤ m), e) - f = Iterators.filter(x -> (first(x) ∈ inds), w) - map(last, f) - end -end - -""" - withcrs(g, v) - -Point at the end of the vector `v` with the same CRS of `g`. -""" -function withcrs(g::GeometryOrDomain, v::StaticVector) - C = crs(g) - cart = Cartesian{datum(C)}(Tuple(v)) - ctor = CoordRefSystems.constructor(C) - Point(convert(ctor, cart)) -end - -""" - flat(p) - -Flatten coordinates of point `p` to Cartesian coordinates, -ignoring the original units of the coordinate reference system. -""" -flat(p::Point) = Point(flat(coords(p))) -flat(c::CRS) = Cartesian{datum(c)}(CoordRefSystems.raw(c)) - -""" - signarea(A, B, C) - -Compute signed area of triangle formed by points `A`, `B` and `C`. -""" -function signarea(A::Point, B::Point, C::Point) - checkdim(A, 2) - ((B - A) × (C - A)) / 2 -end - -""" - householderbasis(n) - -Returns a pair of orthonormal tangent vectors `u` and `v` from a normal `n`, -such that `u`, `v`, and `n` form a right-hand orthogonal system. - -## References - -* D.S. Lopes et al. 2013. ["Tangent vectors to a 3-D surface normal: A geometric tool - to find orthogonal vectors based on the Householder transformation"] - (https://doi.org/10.1016/j.cad.2012.11.003) -""" -function householderbasis(n::Vec{3,ℒ}) where {ℒ} - n̂ = norm(n) - i = argmax(n .+ n̂) - n̂ᵢ = Vec(ntuple(j -> j == i ? n̂ : zero(ℒ), 3)) - h = n + n̂ᵢ - H = (I - 2h * transpose(h) / (transpose(h) * h)) * unit(ℒ) - u, v = [H[:, j] for j in 1:3 if j != i] - i == 2 && ((u, v) = (v, u)) - Vec(u), Vec(v) -end - -""" - svdbasis(points) - -Returns the 2D basis that retains most of the variance in the list of 3D `points` -using the singular value decomposition (SVD). - -See . -""" -function svdbasis(p::AbstractVector{<:Point}) - checkdim(first(p), 3) - ℒ = lentype(eltype(p)) - X = reduce(hcat, to.(p)) - μ = sum(X, dims=2) / size(X, 2) - Z = X .- μ - U = usvd(Z).U - u = Vec(U[:, 1]...) - v = Vec(U[:, 2]...) - n = Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)) - isnegative((u × v) ⋅ n) ? (v, u) : (u, v) -end - -""" - mayberound(λ, x, tol) - -Round `λ` to `x` if it is within the tolerance `tol`. -""" -function mayberound(λ::T, x::T, atol=atol(T)) where {T} - isapprox(λ, x, atol=atol) ? x : λ -end - -""" - intersectparameters(a, b, c, d) - -Compute the parameters `λ₁` and `λ₂` of the lines -`a + λ₁ ⋅ v⃗₁`, with `v⃗₁ = b - a` and -`c + λ₂ ⋅ v⃗₂`, with `v⃗₂ = d - c` spanned by the input -points `a`, `b` resp. `c`, `d` such that to yield line -points with minimal distance or the intersection point -(if lines intersect). - -Furthermore, the ranks `r` of the matrix of the linear -system `A ⋅ λ⃗ = y⃗`, with `A = [v⃗₁ -v⃗₂], y⃗ = c - a` -and the rank `rₐ` of the augmented matrix `[A y⃗]` are -calculated in order to identify the intersection type: - -- Intersection: r == rₐ == 2 -- Colinear: r == rₐ == 1 -- No intersection: r != rₐ - - No intersection and parallel: r == 1, rₐ == 2 - - No intersection, skew lines: r == 2, rₐ == 3 -""" -function intersectparameters(a::Point, b::Point, c::Point, d::Point) - A = ustrip.([(b - a) (c - d)]) - y = ustrip.(c - a) - T = eltype(A) - - # calculate the rank of the augmented matrix by checking - # the zero entries of the diagonal of R - _, R = qr([A y]) - - # for Dim == 2 one has to check the L1 norm of rows as - # there are more columns than rows - τ = atol(T) - rₐ = sum(>(τ), sum(abs, R, dims=2)) - - # calculate the rank of the rectangular matrix - r = sum(>(τ), sum(abs, view(R, :, 1:2), dims=2)) - - # calculate parameters of intersection or closest point - if r ≥ 2 - λ = A \ y - λ₁, λ₂ = λ[1], λ[2] - else # parallel or collinear - λ₁, λ₂ = zero(T), zero(T) - end - - λ₁, λ₂, r, rₐ -end - -""" - XYZ(xyz) - -Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. -""" -@generated function XYZ(xyz::NTuple{Dim,<:AbstractVector{T}}) where {Dim,T} - exprs = ntuple(Dim) do d - quote - a = xyz[$d] - A = Array{T,Dim}(undef, length.(xyz)) - @nloops $Dim i A begin - @nref($Dim, A, i) = a[$(Symbol(:i_, d))] - end - A - end - end - Expr(:tuple, exprs...) -end - -isequalzero(x) = x == zero(x) -isequalone(x) = x == oneunit(x) - -isapproxequal(x, y; atol=atol(x), kwargs...) = isapprox(x, y; atol, kwargs...) -isapproxzero(x; atol=atol(x), kwargs...) = isapprox(x, zero(x); atol, kwargs...) -isapproxone(x; atol=atol(x), kwargs...) = isapprox(x, oneunit(x); atol, kwargs...) - -ispositive(x) = x > zero(x) -isnegative(x) = x < zero(x) -isnonpositive(x) = x ≤ zero(x) -isnonnegative(x) = x ≥ zero(x) - -# Function wrappers that handle units -# The result units of some operations, such as dot and cross, -# are treated in a special way to handle Meshes.jl use cases - -function usvd(A) - u = unit(eltype(A)) - F = svd(ustrip.(A)) - SVD(F.U * u, F.S * u, F.Vt * u) -end - -uinv(A) = inv(ustrip.(A)) * unit(eltype(A))^-1 - -unormalize(a::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(normalize(a) * unit(ℒ)) - -udot(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = ustrip(a ⋅ b) * unit(ℒ) -udot(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = udot(promote(a, b)...) - -ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b) * unit(ℒ)) -ucross(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = ucross(promote(a, b)...) - -ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}, c::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b × c) * unit(ℒ)) - -urotbetween(u::Vec, v::Vec) = rotation_between(ustrip.(u), ustrip.(v)) - -urotapply(R::Rotation, v::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(R * ustrip.(v) * unit(ℒ)) +include("utils/basic.jl") +include("utils/assert.jl") +include("utils/cmp.jl") +include("utils/units.jl") +include("utils/crs.jl") +include("utils/misc.jl") diff --git a/src/utils/assert.jl b/src/utils/assert.jl new file mode 100644 index 000000000..6d187cf1f --- /dev/null +++ b/src/utils/assert.jl @@ -0,0 +1,19 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + assertion(cond, msg) + +Throws an `AssertionError(msg)` if `cond` is `false`. +""" +assertion(cond, msg) = cond || throw(AssertionError(msg)) + +""" + checkdim(geom, dim) + +Throws an `ArgumentError` if the `embeddim` of the geometry `geom` +is different than the specified dimension `dim`. +""" +checkdim(geom, dim) = + embeddim(geom) ≠ dim && throw(ArgumentError("geometry must be embedded in $dim-dimensional space")) diff --git a/src/utils/basic.jl b/src/utils/basic.jl new file mode 100644 index 000000000..18b3909ed --- /dev/null +++ b/src/utils/basic.jl @@ -0,0 +1,55 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + constructor(G) + +Given a (parametric) type `G{T₁,T₂,...}`, return the type `G`. +""" +constructor(G::Type) = getfield(Meshes, nameof(G)) + +""" + fitdims(dims, D) + +Fit tuple `dims` to a given length `D` by repeating the last dimension. +""" +function fitdims(dims::Dims{N}, D) where {N} + ntuple(i -> i ≤ N ? dims[i] : last(dims), D) +end + +""" + collectat(iter, inds) + +Collect iterator `iter` at indices `inds` without materialization. +""" +function collectat(iter, inds) + if isempty(inds) + eltype(iter)[] + else + m = maximum(inds) + e = Iterators.enumerate(iter) + w = Iterators.takewhile(x -> (first(x) ≤ m), e) + f = Iterators.filter(x -> (first(x) ∈ inds), w) + map(last, f) + end +end + +""" + XYZ(xyz) + +Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. +""" +@generated function XYZ(xyz::NTuple{Dim,<:AbstractVector{T}}) where {Dim,T} + exprs = ntuple(Dim) do d + quote + a = xyz[$d] + A = Array{T,Dim}(undef, length.(xyz)) + @nloops $Dim i A begin + @nref($Dim, A, i) = a[$(Symbol(:i_, d))] + end + A + end + end + Expr(:tuple, exprs...) +end diff --git a/src/utils/cmp.jl b/src/utils/cmp.jl new file mode 100644 index 000000000..9f7f0f2e4 --- /dev/null +++ b/src/utils/cmp.jl @@ -0,0 +1,26 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +# Comparisons between unitful and non-unitful quantities + +isequalzero(x) = x == zero(x) +isequalone(x) = x == oneunit(x) + +isapproxequal(x, y; atol=atol(x), kwargs...) = isapprox(x, y; atol, kwargs...) +isapproxzero(x; atol=atol(x), kwargs...) = isapprox(x, zero(x); atol, kwargs...) +isapproxone(x; atol=atol(x), kwargs...) = isapprox(x, oneunit(x); atol, kwargs...) + +ispositive(x) = x > zero(x) +isnegative(x) = x < zero(x) +isnonpositive(x) = x ≤ zero(x) +isnonnegative(x) = x ≥ zero(x) + +""" + mayberound(λ, x, tol) + +Round `λ` to `x` if it is within the tolerance `tol`. +""" +function mayberound(λ::T, x::T, atol=atol(T)) where {T} + isapprox(λ, x, atol=atol) ? x : λ +end diff --git a/src/utils/crs.jl b/src/utils/crs.jl new file mode 100644 index 000000000..bda5e466b --- /dev/null +++ b/src/utils/crs.jl @@ -0,0 +1,24 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + withcrs(g, v) + +Point at the end of the vector `v` with the same CRS of `g`. +""" +function withcrs(g::GeometryOrDomain, v::StaticVector) + C = crs(g) + cart = Cartesian{datum(C)}(Tuple(v)) + ctor = CoordRefSystems.constructor(C) + Point(convert(ctor, cart)) +end + +""" + flat(p) + +Flatten coordinates of point `p` to Cartesian coordinates, +ignoring the original units of the coordinate reference system. +""" +flat(p::Point) = Point(flat(coords(p))) +flat(c::CRS) = Cartesian{datum(c)}(CoordRefSystems.raw(c)) diff --git a/src/utils/misc.jl b/src/utils/misc.jl new file mode 100644 index 000000000..f10a3d786 --- /dev/null +++ b/src/utils/misc.jl @@ -0,0 +1,106 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + signarea(A, B, C) + +Compute signed area of triangle formed by points `A`, `B` and `C`. +""" +function signarea(A::Point, B::Point, C::Point) + checkdim(A, 2) + ((B - A) × (C - A)) / 2 +end + +""" + householderbasis(n) + +Returns a pair of orthonormal tangent vectors `u` and `v` from a normal `n`, +such that `u`, `v`, and `n` form a right-hand orthogonal system. + +## References + +* D.S. Lopes et al. 2013. ["Tangent vectors to a 3-D surface normal: A geometric tool + to find orthogonal vectors based on the Householder transformation"] + (https://doi.org/10.1016/j.cad.2012.11.003) +""" +function householderbasis(n::Vec{3,ℒ}) where {ℒ} + n̂ = norm(n) + i = argmax(n .+ n̂) + n̂ᵢ = Vec(ntuple(j -> j == i ? n̂ : zero(ℒ), 3)) + h = n + n̂ᵢ + H = (I - 2h * transpose(h) / (transpose(h) * h)) * unit(ℒ) + u, v = [H[:, j] for j in 1:3 if j != i] + i == 2 && ((u, v) = (v, u)) + Vec(u), Vec(v) +end + +""" + svdbasis(points) + +Returns the 2D basis that retains most of the variance in the list of 3D `points` +using the singular value decomposition (SVD). + +See . +""" +function svdbasis(p::AbstractVector{<:Point}) + checkdim(first(p), 3) + ℒ = lentype(eltype(p)) + X = reduce(hcat, to.(p)) + μ = sum(X, dims=2) / size(X, 2) + Z = X .- μ + U = usvd(Z).U + u = Vec(U[:, 1]...) + v = Vec(U[:, 2]...) + n = Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)) + isnegative((u × v) ⋅ n) ? (v, u) : (u, v) +end + +""" + intersectparameters(a, b, c, d) + +Compute the parameters `λ₁` and `λ₂` of the lines +`a + λ₁ ⋅ v⃗₁`, with `v⃗₁ = b - a` and +`c + λ₂ ⋅ v⃗₂`, with `v⃗₂ = d - c` spanned by the input +points `a`, `b` resp. `c`, `d` such that to yield line +points with minimal distance or the intersection point +(if lines intersect). + +Furthermore, the ranks `r` of the matrix of the linear +system `A ⋅ λ⃗ = y⃗`, with `A = [v⃗₁ -v⃗₂], y⃗ = c - a` +and the rank `rₐ` of the augmented matrix `[A y⃗]` are +calculated in order to identify the intersection type: + +- Intersection: r == rₐ == 2 +- Colinear: r == rₐ == 1 +- No intersection: r != rₐ + - No intersection and parallel: r == 1, rₐ == 2 + - No intersection, skew lines: r == 2, rₐ == 3 +""" +function intersectparameters(a::Point, b::Point, c::Point, d::Point) + A = ustrip.([(b - a) (c - d)]) + y = ustrip.(c - a) + T = eltype(A) + + # calculate the rank of the augmented matrix by checking + # the zero entries of the diagonal of R + _, R = qr([A y]) + + # for Dim == 2 one has to check the L1 norm of rows as + # there are more columns than rows + τ = atol(T) + rₐ = sum(>(τ), sum(abs, R, dims=2)) + + # calculate the rank of the rectangular matrix + r = sum(>(τ), sum(abs, view(R, :, 1:2), dims=2)) + + # calculate parameters of intersection or closest point + if r ≥ 2 + λ = A \ y + λ₁, λ₂ = λ[1], λ[2] + else # parallel or collinear + λ₁, λ₂ = zero(T), zero(T) + end + + λ₁, λ₂, r, rₐ +end diff --git a/src/utils/units.jl b/src/utils/units.jl new file mode 100644 index 000000000..089eb5c94 --- /dev/null +++ b/src/utils/units.jl @@ -0,0 +1,28 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +# The result units of some operations, such as dot and cross, +# are treated in a special way to handle Meshes.jl use cases + +function usvd(A) + u = unit(eltype(A)) + F = svd(ustrip.(A)) + SVD(F.U * u, F.S * u, F.Vt * u) +end + +uinv(A) = inv(ustrip.(A)) * unit(eltype(A))^-1 + +unormalize(a::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(normalize(a) * unit(ℒ)) + +udot(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = ustrip(a ⋅ b) * unit(ℒ) +udot(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = udot(promote(a, b)...) + +ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b) * unit(ℒ)) +ucross(a::Vec{Dim,ℒ₁}, b::Vec{Dim,ℒ₂}) where {Dim,ℒ₁,ℒ₂} = ucross(promote(a, b)...) + +ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}, c::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(ustrip.(a × b × c) * unit(ℒ)) + +urotbetween(u::Vec, v::Vec) = rotation_between(ustrip.(u), ustrip.(v)) + +urotapply(R::Rotation, v::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(R * ustrip.(v) * unit(ℒ)) \ No newline at end of file From 904a6d95ff0b1924f726944bf03a4cbc296bc399 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:31:11 -0300 Subject: [PATCH 217/423] :robot: Format .jl files (#966) Co-authored-by: juliohm <3345261+juliohm@users.noreply.github.com> --- src/utils/units.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/units.jl b/src/utils/units.jl index 089eb5c94..d31b6a128 100644 --- a/src/utils/units.jl +++ b/src/utils/units.jl @@ -25,4 +25,4 @@ ucross(a::Vec{Dim,ℒ}, b::Vec{Dim,ℒ}, c::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec( urotbetween(u::Vec, v::Vec) = rotation_between(ustrip.(u), ustrip.(v)) -urotapply(R::Rotation, v::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(R * ustrip.(v) * unit(ℒ)) \ No newline at end of file +urotapply(R::Rotation, v::Vec{Dim,ℒ}) where {Dim,ℒ} = Vec(R * ustrip.(v) * unit(ℒ)) From 7bee2bef270f64dd556cddb0a7bd4c940af6a42a Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:07:45 -0400 Subject: [PATCH 218/423] Use julia-format action (#965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use julia-format action * test reviewdog * rename file back to FormatPR.yml * Update src/Meshes.jl --------- Co-authored-by: Júlio Hoffimann --- .github/workflows/FormatPR.yml | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/.github/workflows/FormatPR.yml b/.github/workflows/FormatPR.yml index 3717c0414..8ce8a2d44 100644 --- a/.github/workflows/FormatPR.yml +++ b/.github/workflows/FormatPR.yml @@ -1,28 +1,11 @@ -name: FormatPR +name: Format suggestions on: - push: - branches: - - master + pull_request + jobs: - build: + code-style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Install JuliaFormatter and format - run: | - julia -e 'import Pkg; Pkg.add("JuliaFormatter")' - julia -e 'using JuliaFormatter; format(".")' - - name: Create Pull Request - id: cpr - uses: peter-evans/create-pull-request@v6 + - uses: julia-actions/julia-format@v3 with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ":robot: Format .jl files" - title: '[AUTO] JuliaFormatter.jl run' - branch: auto-juliaformatter-pr - delete-branch: true - labels: formatting, automated pr, no changelog - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" + version: '1' # Set `version` to '1.0.54' if you need to use JuliaFormatter.jl v1.0.54 (default: '1') From ad4ea2921cb971eb7216c984320a91272af63c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 29 Jul 2024 16:13:58 -0300 Subject: [PATCH 219/423] Refactor intersectpoint --- src/clipping/sutherlandhodgman.jl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/clipping/sutherlandhodgman.jl b/src/clipping/sutherlandhodgman.jl index d650981a6..cbd3250bc 100644 --- a/src/clipping/sutherlandhodgman.jl +++ b/src/clipping/sutherlandhodgman.jl @@ -64,16 +64,6 @@ end # helper function to find any intersection point # between crossing or overlapping lines function intersectpoint(l₁::Line, l₂::Line) - intersection(l₁, l₂) do I - if type(I) == Crossing # get intersection point - get(I) - elseif type(I) == Overlapping # perturb line and retry - ℒ = lentype(l₁) - T = numtype(ℒ) - δx = rand((1, -1)) * rand(T) * atol(ℒ) - δy = rand((1, -1)) * rand(T) * atol(ℒ) - l₁′ = Line(l₁(0), l₁(1) + Vec(δx, δy)) - intersectpoint(l₁′, l₂) - end - end + λ(I) = type(I) == Overlapping ? l₁(0) : get(I) + intersection(λ, l₁, l₂) end From 2f3bc3aa91835b8d51bde396c40ea9878c5eb2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 07:59:45 -0300 Subject: [PATCH 220/423] Add more ordering relations --- src/Meshes.jl | 4 ++ src/intersections/boxes.jl | 2 +- src/predicates.jl | 1 + src/predicates/in.jl | 2 +- src/predicates/ordering.jl | 103 +++++++++++++++++++++++++++++++++++++ src/primitives/box.jl | 2 +- src/primitives/point.jl | 18 ------- test/predicates.jl | 38 ++++++++++++++ test/primitives.jl | 10 ---- test/sampling.jl | 4 +- 10 files changed, 151 insertions(+), 33 deletions(-) create mode 100644 src/predicates/ordering.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index eaec4c31c..aeeefd821 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -191,6 +191,10 @@ export sides, diagonal, focallength, + ≺, + ≻, + ⪯, + ⪰, # polytopes Polytope, diff --git a/src/intersections/boxes.jl b/src/intersections/boxes.jl index a415a7e04..f7419499d 100644 --- a/src/intersections/boxes.jl +++ b/src/intersections/boxes.jl @@ -28,7 +28,7 @@ function intersection(f, box₁::Box, box₂::Box) elseif all(<(τ), δ̄) return @IT CornerTouching u f elseif any(<(τ), δ̄) && (δ == δ̄ || δ == -δ̄) - return @IT Touching (u ≤ v ? Box(u, v) : Box(v, u)) f + return @IT Touching (u ⪯ v ? Box(u, v) : Box(v, u)) f else return @IT NotIntersecting nothing f end diff --git a/src/predicates.jl b/src/predicates.jl index ddce13b0b..87ff25075 100644 --- a/src/predicates.jl +++ b/src/predicates.jl @@ -15,6 +15,7 @@ include("predicates/hasholes.jl") include("predicates/in.jl") include("predicates/issubset.jl") include("predicates/intersects.jl") +include("predicates/ordering.jl") # other predicates include("predicates/iscollinear.jl") diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 0b384f0e7..71bb4efa2 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -32,7 +32,7 @@ Base.in(p::Point, c::Chain) = any(s -> p ∈ s, segments(c)) Base.in(p::Point, pl::Plane) = isapproxzero(udot(normal(pl), p - pl(0, 0))) -Base.in(p::Point, b::Box) = minimum(b) ≤ p ≤ maximum(b) +Base.in(p::Point, b::Box) = minimum(b) ⪯ p ⪯ maximum(b) function Base.in(p::Point, b::Ball) c = center(b) diff --git a/src/predicates/ordering.jl b/src/predicates/ordering.jl new file mode 100644 index 000000000..51cbbc724 --- /dev/null +++ b/src/predicates/ordering.jl @@ -0,0 +1,103 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +# ---------------------- +# LEXICOGRAPHICAL ORDER +# ---------------------- + +""" + <(A::Point, B::Point) + +The lexicographical order of points `A` and `B` (`<`). + +`A < B` if the tuples of coordinates satisfy `(a₁, a₂, ...) < (b₁, b₂, ...)`. + +See +""" +<(A::Point, B::Point) = CoordRefSystems.values(coords(A)) < CoordRefSystems.values(coords(B)) + +""" + >(A::Point, B::Point) + +The lexicographical order of points `A` and `B` (`>`). + +`A > B` if the tuples of coordinates satisfy `(a₁, a₂, ...) > (b₁, b₂, ...)`. + +See +""" +>(A::Point, B::Point) = CoordRefSystems.values(coords(A)) > CoordRefSystems.values(coords(B)) + +""" + ≤(A::Point, B::Point) + +The lexicographical order of points `A` and `B` (`\\le`). + +`A ≤ B` if the tuples of coordinates satisfy `(a₁, a₂, ...) ≤ (b₁, b₂, ...)`. + +See +""" +≤(A::Point, B::Point) = CoordRefSystems.values(coords(A)) ≤ CoordRefSystems.values(coords(B)) + +""" + ≥(A::Point, B::Point) + +The lexicographical order of points `A` and `B` (`\\ge`). + +`A ≥ B` if the tuples of coordinates satisfy `(a₁, a₂, ...) ≥ (b₁, b₂, ...)`. + +See +""" +≥(A::Point, B::Point) = CoordRefSystems.values(coords(A)) ≥ CoordRefSystems.values(coords(B)) + +# -------------- +# PRODUCT ORDER +# -------------- + +""" + ≺(A::Point, B::Point) + +The product order of points `A` and `B` (`\\prec`). + +`A ≺ B` if `aᵢ < bᵢ` for all coordinates `aᵢ` and `bᵢ`. + +See +""" +≺(A::Point, B::Point) = all(x -> x > zero(x), B - A) +≺(A::Point{🌐}, B::Point{🌐}) = _lat(A) < _lat(B) + +""" + ≻(A::Point, B::Point) + +The product order of points `A` and `B` (`\\succ`). + +`A ≻ B` if `aᵢ > bᵢ` for all coordinates `aᵢ` and `bᵢ`. + +See +""" +≻(A::Point, B::Point) = all(x -> x > zero(x), A - B) +≻(A::Point{🌐}, B::Point{🌐}) = _lat(A) > _lat(B) + +""" + ⪯(A::Point, B::Point) + +The product order of points `A` and `B` (`\\preceq`). + +`A ⪯ B` if `aᵢ ≤ bᵢ` for all coordinates `aᵢ` and `bᵢ`. + +See +""" +⪯(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) +⪯(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≤ _lat(B) + +""" + ⪰(A::Point, B::Point) + +The product order of points `A` and `B` (`\\succeq`). + +`A ⪰ B` if `aᵢ ≥ bᵢ` for all coordinates `aᵢ` and `bᵢ`. + +See +""" +⪰(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) +⪰(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≥ _lat(B) diff --git a/src/primitives/box.jl b/src/primitives/box.jl index 22dba8d53..8cc04ade7 100644 --- a/src/primitives/box.jl +++ b/src/primitives/box.jl @@ -32,7 +32,7 @@ struct Box{M<:Manifold,C<:CRS} <: Primitive{M,C} max::Point{M,C} function Box{M,C}(min, max) where {M<:Manifold,C<:CRS} - assertion(min ≤ max, "`min` must be less than or equal to `max`") + assertion(min ⪯ max, "can only construct box with min ⪯ max") new(min, max) end end diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 61b856f47..0679c6d4a 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -98,24 +98,6 @@ at a reference (or start) point `A`. -(A::Point, v::Vec) = withcrs(A, to(A) - v) -(v::Vec, A::Point) = A - v -""" - ≤(A::Point, B::Point) - ≥(A::Point, B::Point) - <(A::Point, B::Point) - >(A::Point, B::Point) - -Partial order for points on a given manifold. -""" -≤(A::Point, B::Point) = all(x -> x ≥ zero(x), B - A) -≥(A::Point, B::Point) = all(x -> x ≥ zero(x), A - B) -<(A::Point, B::Point) = all(x -> x > zero(x), B - A) ->(A::Point, B::Point) = all(x -> x > zero(x), A - B) - -≤(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≤ _lat(B) -≥(A::Point{🌐}, B::Point{🌐}) = _lat(A) ≥ _lat(B) -<(A::Point{🌐}, B::Point{🌐}) = _lat(A) < _lat(B) ->(A::Point{🌐}, B::Point{🌐}) = _lat(A) > _lat(B) - """ ∠(A, B, C) diff --git a/test/predicates.jl b/test/predicates.jl index e0475ce80..8c13b5e5a 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -551,6 +551,44 @@ @test intersects(seg, multi) end + @testset "ordering" begin + # lexicographical order + @test cart(0, 0) < cart(1, 1) + @test cart(0, 0) < cart(0, 1) + @test cart(1, 0) < cart(1, 1) + @test !(cart(1, 0) < cart(1, 0)) + @test !(cart(1, 0) < cart(0, 0)) + @test cart(1, 1) > cart(0, 0) + @test cart(0, 1) > cart(0, 0) + @test cart(1, 1) > cart(1, 0) + @test cart(1, 0) ≥ cart(1, 0) + @test cart(1, 0) ≥ cart(0, 0) + @test cart(0, 0) ≤ cart(0, 0) + + # product order + @test cart(0, 0) ≺ cart(1, 1) + @test !(cart(0, 0) ≺ cart(0, 1)) + @test !(cart(1, 0) ≺ cart(1, 1)) + @test !(cart(1, 0) ≺ cart(1, 0)) + @test !(cart(1, 0) ≺ cart(0, 0)) + @test cart(1, 1) ≻ cart(0, 0) + @test !(cart(0, 1) ≻ cart(0, 0)) + @test !(cart(1, 1) ≻ cart(1, 0)) + @test cart(1, 0) ⪰ cart(1, 0) + @test cart(1, 0) ⪰ cart(0, 0) + @test cart(0, 0) ⪯ cart(0, 0) + + # product order + @test cart(1, 1) ⪯ cart(1, 1) + @test !(cart(1, 1) ≺ cart(1, 1)) + @test cart(1, 2) ⪯ cart(3, 4) + @test cart(1, 2) ≺ cart(3, 4) + @test cart(1, 1) ⪰ cart(1, 1) + @test !(cart(1, 1) ≻ cart(1, 1)) + @test cart(3, 4) ⪰ cart(1, 2) + @test cart(3, 4) ≻ cart(1, 2) + end + @testset "iscollinear" begin @test iscollinear(cart(0, 0), cart(1, 1), cart(2, 2)) end diff --git a/test/primitives.jl b/test/primitives.jl index 2bb7db6e5..2a1e32a76 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -99,16 +99,6 @@ p2 = convert(P, p1) @test p2 isa P - # generalized inequality - @test cart(1, 1) ≤ cart(1, 1) - @test !(cart(1, 1) < cart(1, 1)) - @test cart(1, 2) ≤ cart(3, 4) - @test cart(1, 2) < cart(3, 4) - @test cart(1, 1) ≥ cart(1, 1) - @test !(cart(1, 1) > cart(1, 1)) - @test cart(3, 4) ≥ cart(1, 2) - @test cart(3, 4) > cart(1, 2) - # center and centroid @test Meshes.center(cart(1, 1)) == cart(1, 1) @test centroid(cart(1, 1)) == cart(1, 1) diff --git a/test/sampling.jl b/test/sampling.jl index 89fc59875..d46bb3c25 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -357,7 +357,7 @@ poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, HomogeneousSampling(100)) - @test all(p -> (cart(0, 0) ≤ p ≤ cart(1, 1)) || (cart(1, 1) ≤ p ≤ cart(2, 2)), ps) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) @@ -375,7 +375,7 @@ poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) ps = sample(multi, MinDistanceSampling(0.1)) - @test all(p -> (cart(0, 0) ≤ p ≤ cart(1, 1)) || (cart(1, 1) ≤ p ≤ cart(2, 2)), ps) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) From f8cd72c9c8c154e60d9e4e02d014293fa9e78ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 08:44:05 -0300 Subject: [PATCH 221/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index bd9ef698b..390b42376 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.4" +version = "0.47.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 9dfac9f703012ab8793fd19e3a207acedafdb45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 08:48:47 -0300 Subject: [PATCH 222/423] Update docs --- docs/src/predicates.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/predicates.md b/docs/src/predicates.md index 86daf080f..acb889a6d 100644 --- a/docs/src/predicates.md +++ b/docs/src/predicates.md @@ -12,7 +12,8 @@ One important note to make is that these predicates are not necessarily exact. For example, rather than checking if a point `p` is exactly in a sphere of radius `r` centered at `c`, we check if `norm(p-c) ≈ r` with an absolute tolerance depending on the point type, so `p` might be slightly outside the sphere but still be considered -as being inside. +as being inside. This absolute tolerance can be adjusted in specific scopes as discussed +in the [Tolerances](tolerances.md) section. Robust predicates are often expensive to apply and approximations typically suffice. If needed, consider [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl) or From 9c011fd00fc104060ce535204c2c66a6520dcca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 08:58:09 -0300 Subject: [PATCH 223/423] Update docs --- docs/src/predicates.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/src/predicates.md b/docs/src/predicates.md index acb889a6d..7761fd03d 100644 --- a/docs/src/predicates.md +++ b/docs/src/predicates.md @@ -62,6 +62,24 @@ issimple hasholes ``` +## point₁ ≤ point₂ + +```@docs +Base.:<(::Point, ::Point) +Base.:>(::Point, ::Point) +Base.:≤(::Point, ::Point) +Base.:≥(::Point, ::Point) +``` + +## point₁ ⪯ point₂ + +```@docs +≺(::Point, ::Point) +≻(::Point, ::Point) +⪯(::Point, ::Point) +⪰(::Point, ::Point) +``` + ## point ∈ geometry ```@docs From 8ead50164e45f049142136920f606fa0918dac5a Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:31:56 -0300 Subject: [PATCH 224/423] Add 'Repair' method for 'Multi' (#967) --- src/transforms/repair.jl | 2 ++ test/transforms.jl | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 7bae205a3..7ae137eaa 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -214,4 +214,6 @@ end apply(::Repair, geom::Geometry) = geom, nothing +apply(t::Repair, multi::Multi) = Multi([t(g) for g in parent(multi)]), nothing + apply(t::Repair, dom::Domain) = GeometrySet([t(g) for g in dom]), nothing diff --git a/test/transforms.jl b/test/transforms.jl index c67a97fbe..76186aec6 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1840,6 +1840,13 @@ @test rquad isa Quadrangle @test rquad == quad + poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) + poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + multi = Multi([poly1, poly2]) + repair = Repair{11}() + rmulti, cache = TB.apply(repair, multi) + @test rmulti == Multi([repair(poly1), repair(poly2)]) + poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) gset = GeometrySet([poly1, poly2]) From 6d747f27914a32bb06e6496bd051ad03501ea906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 18:06:41 -0300 Subject: [PATCH 225/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 390b42376..61dbf6d73 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.5" +version = "0.47.6" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ba63dafd9a07e86ac82e88f5123ed13037f5a40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 21:24:39 -0300 Subject: [PATCH 226/423] signarea is only a helper function --- src/Meshes.jl | 8 ++++---- src/measures.jl | 6 +----- src/polytopes/ngon.jl | 8 -------- test/polytopes.jl | 5 ----- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index aeeefd821..6711a32ed 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -191,10 +191,6 @@ export sides, diagonal, focallength, - ≺, - ≻, - ⪯, - ⪰, # polytopes Polytope, @@ -397,6 +393,10 @@ export intersects, iscollinear, iscoplanar, + ≺, + ≻, + ⪯, + ⪰, # winding number winding, diff --git a/src/measures.jl b/src/measures.jl index 143afd171..8949f79c1 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -77,11 +77,7 @@ end measure(s::Segment) = norm(maximum(s) - minimum(s)) -measure(t::Triangle) = _measure(t, Val(embeddim(t))) - -_measure(t::Triangle, ::Val{2}) = abs(signarea(t)) - -function _measure(t::Triangle, ::Val{3}) +function measure(t::Triangle) A, B, C = vertices(t) norm((B - A) × (C - A)) / 2 end diff --git a/src/polytopes/ngon.jl b/src/polytopes/ngon.jl index 6c2835f29..9216af334 100644 --- a/src/polytopes/ngon.jl +++ b/src/polytopes/ngon.jl @@ -63,18 +63,10 @@ angles(ngon::Ngon) = angles(boundary(ngon)) innerangles(ngon::Ngon) = innerangles(boundary(ngon)) -signarea(ngon::Ngon) = sum(signarea, simplexify(ngon)) - # ---------- # TRIANGLES # ---------- -function signarea(t::Triangle) - checkdim(t, 2) - v = t.vertices - signarea(v[1], v[2], v[3]) -end - function normal(t::Triangle) checkdim(t, 3) A, B, C = t.vertices diff --git a/test/polytopes.jl b/test/polytopes.jl index c840fd771..8ac3c53b0 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -306,10 +306,8 @@ @test vertex(t, 1) == cart(0, 0) @test vertex(t, 2) == cart(1, 0) @test vertex(t, 3) == cart(0, 1) - @test signarea(t) == T(0.5) * u"m^2" @test area(t) == T(0.5) * u"m^2" t = Triangle(cart(0, 0), cart(0, 1), cart(1, 0)) - @test signarea(t) == T(-0.5) * u"m^2" @test area(t) == T(0.5) * u"m^2" t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) for p in cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) @@ -383,9 +381,6 @@ @test isapprox(normal(t), vector(0, -0.7071067811865475, 0.7071067811865475)) @test isapprox(norm(normal(t)), oneunit(ℳ)) - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - @test_throws ArgumentError signarea(t) - # CRS propagation t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) @test crs(t(T(0), T(0))) === crs(t) From 0ff5d65a3a3c6eada9b369b29ddb32c38e7f7460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 30 Jul 2024 21:31:29 -0300 Subject: [PATCH 227/423] Improve docstring of measure --- src/measures.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/measures.jl b/src/measures.jl index 8949f79c1..f94cdeb38 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -9,11 +9,11 @@ """ measure(object) -Return the measure or "volume" of the `object`. +Return the measure of the geometric `object`. -### Notes - -- Type aliases are [`length`](@ref), [`area`](@ref), [`volume`](@ref) +This function is also known as [`length`](@ref), +[`area`](@ref) or [`volume`](@ref) depending on +the parametric dimension of the object. """ function measure end From 58ecde2e0beeebcb1859351fdce63e00f3806b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 31 Jul 2024 13:49:46 -0300 Subject: [PATCH 228/423] Fast measure for polygons embedded in E{2} (#969) * Fast measure for polygons embedded in E{2} * Apply suggestions --- src/measures.jl | 9 +++++++++ test/predicates.jl | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/measures.jl b/src/measures.jl index f94cdeb38..0b2e01c8c 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -82,6 +82,15 @@ function measure(t::Triangle) norm((B - A) × (C - A)) / 2 end +function measure(p::Polygon{𝔼{2}}) + sum(rings(p)) do r + v = vertices(r) + n = nvertices(r) + c = centroid(r) + sum(signarea(c, v[i], v[i + 1]) for i in 1:n) + end +end + function measure(t::Tetrahedron) A, B, C, D = vertices(t) abs((A - D) ⋅ ((B - D) × (C - D))) / 6 diff --git a/test/predicates.jl b/test/predicates.jl index 8c13b5e5a..33235ecae 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -91,8 +91,8 @@ @test !isconvex(q5) t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) @test isconvex(t) - outer = cart.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) - inner = cart.([(5, 7), (10, 12), (15, 7)]) + outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) + inner = cart.([(15, 7), (10, 12), (5, 7)]) pent = Pentagon(outer...) tri = Triangle(inner...) poly = PolyArea([outer, reverse(inner)]) @@ -101,11 +101,11 @@ @test isconvex(tri) @test !isconvex(poly) @test isconvex(multi) - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) poly1 = PolyArea(outer) - poly2 = PolyArea([outer, hole1, hole2]) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) @test isconvex(poly1) @test !isconvex(poly2) poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) @@ -253,12 +253,12 @@ @test l1 ⊆ l1 @test l2 ⊆ l2 - pts1 = cart.([(5, 7), (10, 12), (15, 7)]) - pts2 = cart.([(6, 1), (2, 10), (10, 16), (18, 10), (14, 1)]) - pent = Pentagon(pts2...) - tri = Triangle(pts1...) - poly1 = PolyArea(pts2) - poly2 = PolyArea([pts2, reverse(pts1)]) + outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) + inner = cart.([(15, 7), (10, 12), (5, 7)]) + pent = Pentagon(outer...) + tri = Triangle(inner...) + poly1 = PolyArea(outer) + poly2 = PolyArea([outer, reverse(inner)]) multi = Multi([poly2, tri]) @test tri ⊆ pent @test tri ⊆ poly1 From db36f549c3ca6678f45a5ebe4c91c11faab95bcc Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:23:57 -0300 Subject: [PATCH 229/423] Dispatch with Manifold in `discretize` methods (#968) * Dispatch with Manifold in 'discretize' methods * Update src/discretization.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Add tests * Apply suggestions --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/discretization.jl | 36 ++++++++++++++++++---------------- src/discretization/dehn.jl | 2 +- src/discretization/delaunay.jl | 2 +- src/discretization/fan.jl | 6 +----- src/discretization/held.jl | 2 +- src/discretization/regular.jl | 8 +++----- test/discretization.jl | 7 +++++++ 7 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index ab6c3f0b0..c5784175b 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -91,34 +91,38 @@ end discretize(multi::Multi, method::BoundaryDiscretizationMethod) = mapreduce(geom -> discretize(geom, method), merge, parent(multi)) -discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) = - _discretizewithin(ring, Val(embeddim(ring)), method) - -function _discretizewithin(ring::Ring, ::Val{3}, method::BoundaryDiscretizationMethod) +function discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) # collect vertices to get rid of static containers points = collect(vertices(ring)) # discretize within 2D ring with given method - ring2D = Ring(proj2D(points)) + ring2D = Ring(_proj2D(manifold(ring), points)) mesh = discretizewithin(ring2D, method) # return mesh with original points SimpleMesh(points, topology(mesh)) end +_proj2D(::Type{𝔼{3}}, points) = proj2D(points) + +function _proj2D(::Type{🌐}, points) + map(points) do p + latlon = convert(LatLon, coords(p)) + flat(Point(latlon)) + end +end + # ---------------- # DEFAULT METHODS # ---------------- -discretize(geometry) = _discretize(geometry, Val(embeddim(geometry))) - -_discretize(geometry, ::Val) = simplexify(geometry) +discretize(geometry) = simplexify(geometry) -_discretize(ball::Ball, ::Val{2}) = discretize(ball, RegularDiscretization(50)) +discretize(ball::Ball{𝔼{2}}) = discretize(ball, RegularDiscretization(50)) discretize(disk::Disk) = discretize(disk, RegularDiscretization(50)) -_discretize(sphere::Sphere, ::Val{3}) = discretize(sphere, RegularDiscretization(50)) +discretize(sphere::Sphere{𝔼{3}}) = discretize(sphere, RegularDiscretization(50)) discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(50)) @@ -151,15 +155,13 @@ when the `object` has parametric dimension 2. """ function simplexify end -simplexify(geometry) = _simplexify(geometry, Val(embeddim(geometry))) - -_simplexify(geometry, ::Val) = simplexify(discretize(geometry)) +simplexify(geometry) = simplexify(discretize(geometry)) -_simplexify(box::Box, ::Val{1}) = SimpleMesh(collect(extrema(box)), GridTopology(1)) +simplexify(box::Box{𝔼{1}}) = SimpleMesh(collect(extrema(box)), GridTopology(1)) -_simplexify(box::Box, ::Val{2}) = discretize(box, FanTriangulation()) +simplexify(box::Box{𝔼{2}}) = discretize(box, FanTriangulation()) -_simplexify(box::Box, ::Val{3}) = discretize(box, Tetrahedralization()) +simplexify(box::Box{𝔼{3}}) = discretize(box, Tetrahedralization()) simplexify(seg::Segment) = SimpleMesh(pointify(seg), GridTopology(1)) @@ -175,7 +177,7 @@ end simplexify(bezier::BezierCurve) = discretize(bezier, RegularDiscretization(50)) -_simplexify(sphere::Sphere, ::Val{2}) = discretize(sphere, RegularDiscretization(50)) +simplexify(sphere::Sphere{𝔼{2}}) = discretize(sphere, RegularDiscretization(50)) simplexify(circle::Circle) = discretize(circle, RegularDiscretization(50)) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index a39b0eb70..d023544e0 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -21,7 +21,7 @@ with small number of vertices. """ struct DehnTriangulation <: BoundaryDiscretizationMethod end -function _discretizewithin(ring::Ring, ::Val{2}, ::DehnTriangulation) +function discretizewithin(ring::Ring{𝔼{2}}, ::DehnTriangulation) # points on resulting mesh points = collect(vertices(ring)) diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl index 70feab3e6..38d52507c 100644 --- a/src/discretization/delaunay.jl +++ b/src/discretization/delaunay.jl @@ -24,7 +24,7 @@ end DelaunayTriangulation(rng=Random.default_rng()) = DelaunayTriangulation(rng) -function _discretizewithin(ring::Ring, ::Val{2}, method::DelaunayTriangulation) +function discretizewithin(ring::Ring{𝔼{2}}, method::DelaunayTriangulation) points = vertices(ring) coords = map(p -> ustrip.(to(p)), points) bnodes = [1:nvertices(ring); 1] diff --git a/src/discretization/fan.jl b/src/discretization/fan.jl index 7ee608969..38ad0c643 100644 --- a/src/discretization/fan.jl +++ b/src/discretization/fan.jl @@ -11,11 +11,7 @@ See [https://en.wikipedia.org/wiki/Fan_triangulation] """ struct FanTriangulation <: BoundaryDiscretizationMethod end -_discretizewithin(ring::Ring, ::Val{2}, ::FanTriangulation) = fan(ring) - -_discretizewithin(ring::Ring, ::Val{3}, ::FanTriangulation) = fan(ring) - -function fan(ring::Ring) +function discretizewithin(ring::Ring, ::FanTriangulation) points = collect(vertices(ring)) connec = [connect((1, i, i + 1)) for i in 2:(nvertices(ring) - 1)] SimpleMesh(points, connec) diff --git a/src/discretization/held.jl b/src/discretization/held.jl index 8f1e1420f..1312a444b 100644 --- a/src/discretization/held.jl +++ b/src/discretization/held.jl @@ -33,7 +33,7 @@ end HeldTriangulation(rng=Random.default_rng(); shuffle=true) = HeldTriangulation(rng, shuffle) -function _discretizewithin(ring::Ring, ::Val{2}, method::HeldTriangulation) +function discretizewithin(ring::Ring{𝔼{2}}, method::HeldTriangulation) # helper function to shuffle ears earshuffle!(𝒬) = method.shuffle && shuffle!(method.rng, 𝒬) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index bf68710a1..d61eae91f 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -46,15 +46,13 @@ end # append to grid topology # ------------------------ -appendtopo(g, tg) = _appendtopo(g, Val(embeddim(g)), tg) +appendtopo(g, tg) = tg -_appendtopo(g, ::Val, tp) = tp - -_appendtopo(::Ball, ::Val{2}, tg) = _appendcenter(tg) +appendtopo(::Ball{𝔼{2}}, tg) = _appendcenter(tg) appendtopo(::Disk, tg) = _appendcenter(tg) -_appendtopo(::Sphere, ::Val{3}, tg) = _appendpoles(tg, 2, true) +appendtopo(::Sphere{𝔼{3}}, tg) = _appendpoles(tg, 2, true) appendtopo(::Ellipsoid, tg) = _appendpoles(tg, 2, true) diff --git a/test/discretization.jl b/test/discretization.jl index d2062ee0e..2ebd66c01 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -180,6 +180,13 @@ @test eltype(mesh) <: Triangle @test nelements(mesh) == 2 + # latlon coordinates + poly = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) + mesh = discretize(poly, method) + @test vertices(mesh) == vertices(poly) + @test eltype(mesh) <: Triangle + @test nelements(mesh) == 2 + # preserves order of vertices poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) mesh = simplexify(poly) From 8cb14ceecba065d440c1dfb06dac0b30a52ed8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 31 Jul 2024 15:44:48 -0300 Subject: [PATCH 230/423] Fix orientation with LatLon coords (#970) --- src/orientation.jl | 8 +++----- test/orientation.jl | 6 ++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/orientation.jl b/src/orientation.jl index 28c9b462b..7894cacab 100644 --- a/src/orientation.jl +++ b/src/orientation.jl @@ -26,15 +26,13 @@ function orientation(p::Polygon) hasholes(p) ? o : first(o) end -orientation(r::Ring) = _orientation(r, Val(embeddim(r))) +orientation(r::Ring{𝔼{3}}) = orientation(proj2D(r)) -_orientation(r::Ring, ::Val{3}) = _orientation(proj2D(r), Val(2)) - -function _orientation(r::Ring, ::Val{2}) +function orientation(r::Ring) ℒ = lentype(r) v = vertices(r) n = nvertices(r) - A(i) = signarea(v[1], v[i], v[i + 1]) + A(i) = signarea(flat(v[1]), flat(v[i]), flat(v[i + 1])) Σ = sum(A, 2:(n - 1), init=zero(ℒ)^2) Σ ≥ zero(Σ) ? CCW : CW end diff --git a/test/orientation.jl b/test/orientation.jl index fe359c6bd..c0d9d4623 100644 --- a/test/orientation.jl +++ b/test/orientation.jl @@ -9,4 +9,10 @@ r1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) r2 = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)])) @test orientation(r1) == orientation(r2) + + # orientation of rings with LatLon coordinates + r = Ring(latlon.([(0, 0), (0, 90), (90, 0)])) + @test orientation(r) == CCW + r = Ring(latlon.([(0, 0), (90, 0), (0, 90)])) + @test orientation(r) == CW end From ec6ad2c85b7d274eca3472cd0595154a527d9379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 31 Jul 2024 15:45:25 -0300 Subject: [PATCH 231/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 61dbf6d73..9c48ec57a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.6" +version = "0.47.7" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f29f9f0b52a4da3cef62859b29b18e8df73f3000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 1 Aug 2024 08:40:50 -0300 Subject: [PATCH 232/423] Improve type stability in Bridge --- src/transforms/bridge.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index 10a7ffe2b..7fff75cd8 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -36,13 +36,13 @@ function apply(transform::Bridge, poly::PolyArea) ring, dups = if hasholes(rpoly) bridge(rings(rpoly), rinds, δ) else - first(rings(rpoly)), [] + first(rings(rpoly)), Tuple{Int,Int}[] end PolyArea(ring), dups end -apply(::Bridge, poly::Ngon) = poly, [] +apply(::Bridge, poly::Ngon) = poly, Tuple{Int,Int}[] function bridge(rings, rinds, δ) # extract vertices and indices From 235fad796ab2cd9096aeaf47c338ab9617ba2652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 1 Aug 2024 09:08:05 -0300 Subject: [PATCH 233/423] Make Bridge type stable --- src/transforms/bridge.jl | 4 +--- test/transforms.jl | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index 7fff75cd8..89edf6f3d 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -52,9 +52,6 @@ function bridge(rings, rinds, δ) # retrieve coordinate type ℒ = lentype(first(rings)) - # retrieve original CRS - C = crs(first(rings)) - # initialize outer boundary outer = flat.(verts[1]) oinds = vinds[1] @@ -121,6 +118,7 @@ function bridge(rings, rinds, δ) end points = map(outer) do p + C = crs(first(rings)) c = CoordRefSystems.raw(coords(p)) Point(CoordRefSystems.reconstruct(C, c)) end diff --git a/test/transforms.jl b/test/transforms.jl index 76186aec6..f1c787051 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1872,6 +1872,9 @@ @test !hasholes(bpoly) @test nvertices(bpoly) == 15 + # make sure that result is inferred + @inferred poly |> Bridge(T(0.1)) + # unique and bridges poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) cpoly = poly |> Repair{0}() |> Bridge() From 7e3fc03d20789c910494e4088fd4f0085ae78341 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Thu, 1 Aug 2024 18:31:54 +0200 Subject: [PATCH 234/423] escape underscore in docstring link (#972) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * escape underscore in docstring * Update src/transforms.jl --------- Co-authored-by: Júlio Hoffimann --- src/transforms.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/transforms.jl b/src/transforms.jl index fecabb1ba..3d9019ec6 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -36,8 +36,7 @@ end CoordinateTransform A method to transform the coordinates of objects. -See [https://en.wikipedia.org/wiki/List_of_common_coordinate_transformations] -(https://en.wikipedia.org/wiki/List_of_common_coordinate_transformations). +See . """ abstract type CoordinateTransform <: GeometricTransform end From f1272609b9cc6570e1df0ec58863bdbc6a9e793b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 1 Aug 2024 17:56:30 -0300 Subject: [PATCH 235/423] Improve docs --- docs/src/index.md | 6 +++--- src/primitives/point.jl | 12 ++++++------ src/vectors.jl | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 846564b98..fb8a3e27f 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -262,19 +262,19 @@ We can also compute angles of any given chain, no matter if it is open or closed: ```@example overview -angles(r) * 180 / pi +angles(r) ``` The sign of these angles is a function of the orientation: ```@example overview -angles(reverse(r)) * 180 / pi +angles(reverse(r)) ``` In case of rings (i.e. closed chains), we can compute inner angles as well: ```@example overview -innerangles(r) * 180 / pi +innerangles(r) ``` And there is a lot more functionality available like for instance diff --git a/src/primitives/point.jl b/src/primitives/point.jl index 0679c6d4a..f5caa837d 100644 --- a/src/primitives/point.jl +++ b/src/primitives/point.jl @@ -16,15 +16,15 @@ Euclidean basis, and integer coordinates are converted to float. ```julia # 2D points Point(1.0, 2.0) # add default units -Point(1.0u"m", 2.0u"m") # double precision as expected -Point(1f0u"km", 2f0u"km") # single precision as expected -Point(1u"m", 2u"m") # integer is converted to float by design +Point(1.0m, 2.0m) # double precision as expected +Point(1f0km, 2f0km) # single precision as expected +Point(1m, 2m) # integer is converted to float by design # 3D points Point(1.0, 2.0, 3.0) # add default units -Point(1.0u"m", 2.0u"m", 3.0u"m") # double precision as expected -Point(1f0u"km", 2f0u"km", 3f0u"km") # single precision as expected -Point(1u"m", 2u"m", 3u"m") # integer is converted to float by design +Point(1.0m, 2.0m, 3.0m) # double precision as expected +Point(1f0km, 2f0km, 3f0km) # single precision as expected +Point(1m, 2m, 3m) # integer is converted to float by design ``` ### Notes diff --git a/src/vectors.jl b/src/vectors.jl index c94e1eb89..831b03383 100644 --- a/src/vectors.jl +++ b/src/vectors.jl @@ -22,15 +22,15 @@ v = B - A # 2D vectors Vec(1.0, 2.0) # add default units -Vec(1.0u"m", 2.0u"m") # double precision as expected -Vec(1f0u"km", 2f0u"km") # single precision as expected -Vec(1u"m", 2u"m") # integer is converted to float by design +Vec(1.0m, 2.0m) # double precision as expected +Vec(1f0km, 2f0km) # single precision as expected +Vec(1m, 2m) # integer is converted to float by design # 3D vectors Vec(1.0, 2.0, 3.0) # add default units -Vec(1.0u"m", 2.0u"m", 3.0u"m") # double precision as expected -Vec(1f0u"km", 2f0u"km", 3f0u"km") # single precision as expected -Vec(1u"m", 2u"m", 3u"m") # integer is converted to float by design +Vec(1.0m, 2.0m, 3.0m) # double precision as expected +Vec(1f0km, 2f0km, 3f0km) # single precision as expected +Vec(1m, 2m, 3m) # integer is converted to float by design ``` ### Notes From 9a8df53fead5c2074d015af4c1a03ad86494fcac Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:25:42 -0300 Subject: [PATCH 236/423] TransformedMesh: Handle transforms that change the Manifold and/or CRS (#973) * TransformedMesh: Handle transforms that change the Manifold and/or CRS * Fix code --- ext/grid/transformed.jl | 4 ++-- src/Meshes.jl | 2 -- src/mesh/transformedmesh.jl | 12 +++++++++++- test/boundingboxes.jl | 4 ++-- test/mesh.jl | 36 ++++++++++++++++++++++++------------ test/transforms.jl | 12 ++++++------ 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 43743bd1d..c35d76a48 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -14,10 +14,10 @@ function isoptimized(t::Affine{2}) isdiag(A) || isrotation(A) end -vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) = +vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) = transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) -vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{3}, edim::Val{3}) = +vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{3}, edim::Val{3}) = transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) function transformedgrid!(plot, fallback) diff --git a/src/Meshes.jl b/src/Meshes.jl index 6711a32ed..7d8107e9a 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -301,8 +301,6 @@ export RectilinearGrid, StructuredGrid, SimpleMesh, - TransformedMesh, - TransformedGrid, vertex, vertices, nvertices, diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 9a8c7feb5..302e99327 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -7,9 +7,19 @@ Lazy representation of a geometric `transform` applied to a `mesh`. """ -struct TransformedMesh{M<:Manifold,C<:CRS,TP<:Topology,MS<:Mesh{M,C,TP},TR<:Transform} <: Mesh{M,C,TP} +struct TransformedMesh{M<:Manifold,C<:CRS,TP<:Topology,MS<:Mesh,TR<:Transform} <: Mesh{M,C,TP} mesh::MS transform::TR + + function TransformedMesh{M,C}(mesh::MS, transform::TR) where {M<:Manifold,C<:CRS,MS<:Mesh,TR<:Transform} + TP = typeof(topology(mesh)) + new{M,C,TP,MS,TR}(mesh, transform) + end +end + +function TransformedMesh(m::Mesh, t::Transform) + p = t(first(vertices(m))) + TransformedMesh{manifold(p),crs(p)}(m, t) end # specialize constructor to avoid deep structures diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index a2573e3dc..2608354d3 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -101,13 +101,13 @@ @test @allocated(boundingbox(d)) < 50 g = cartgrid(10, 10) - d = TransformedGrid(g, Rotate(T(π / 2))) + d = Meshes.TransformedGrid(g, Rotate(T(π / 2))) @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 g = cartgrid(10, 10) rg = convert(RectilinearGrid, g) - d = TransformedGrid(rg, Rotate(T(π / 2))) + d = Meshes.TransformedGrid(rg, Rotate(T(π / 2))) @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 diff --git a/test/mesh.jl b/test/mesh.jl index 49a3694c7..c044ae8f8 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -701,27 +701,38 @@ sgrid = convert(StructuredGrid, grid) mesh = convert(SimpleMesh, grid) trans = Identity() - tmesh = TransformedMesh(mesh, trans) + tmesh = Meshes.TransformedMesh(mesh, trans) @test crs(tmesh) <: Cartesian{NoDatum} @test Meshes.lentype(tmesh) == ℳ @test parent(tmesh) === mesh @test Meshes.transform(tmesh) === trans - @test TransformedMesh(grid, trans) == grid - @test TransformedMesh(rgrid, trans) == rgrid - @test TransformedMesh(sgrid, trans) == sgrid - @test TransformedMesh(mesh, trans) == mesh + @test Meshes.TransformedMesh(grid, trans) == grid + @test Meshes.TransformedMesh(rgrid, trans) == rgrid + @test Meshes.TransformedMesh(sgrid, trans) == sgrid + @test Meshes.TransformedMesh(mesh, trans) == mesh trans = Translate(T(10), T(10)) → Translate(T(-10), T(-10)) - @test TransformedMesh(grid, trans) == grid - @test TransformedMesh(rgrid, trans) == rgrid - @test TransformedMesh(sgrid, trans) == sgrid - @test TransformedMesh(mesh, trans) == mesh + @test Meshes.TransformedMesh(grid, trans) == grid + @test Meshes.TransformedMesh(rgrid, trans) == rgrid + @test Meshes.TransformedMesh(sgrid, trans) == sgrid + @test Meshes.TransformedMesh(mesh, trans) == mesh trans1 = Translate(T(10), T(10)) trans2 = Translate(T(-10), T(-10)) - @test TransformedMesh(TransformedMesh(grid, trans1), trans2) == TransformedMesh(grid, trans1 → trans2) + @test Meshes.TransformedMesh(Meshes.TransformedMesh(grid, trans1), trans2) == + Meshes.TransformedMesh(grid, trans1 → trans2) + + # transforms that change the Manifold and/or CRS + points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + trans = Proj(Cartesian) + tmesh = Meshes.TransformedMesh(mesh, trans) + @test manifold(tmesh) === 𝔼{3} + @test crs(tmesh) <: Cartesian + # grid interface trans = Identity() - tgrid = TransformedMesh(grid, trans) - @test tgrid isa TransformedGrid + tgrid = Meshes.TransformedMesh(grid, trans) + @test tgrid isa Meshes.TransformedGrid @test size(tgrid) == (10, 10) @test minimum(tgrid) == cart(0, 0) @test maximum(tgrid) == cart(10, 10) @@ -742,6 +753,7 @@ @test size(sub) == (10, 5) @test minimum(sub) == cart(0, 2) @test maximum(sub) == cart(10, 7) + @test sprint(show, tgrid) == "10×10 TransformedGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), tgrid) == """ diff --git a/test/transforms.jl b/test/transforms.jl index f1c787051..ddf742936 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -174,7 +174,7 @@ f = Rotate(Angle2d(T(π / 2))) d = cartgrid(10, 10) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -185,7 +185,7 @@ f = Rotate(Angle2d(T(π / 2))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -196,7 +196,7 @@ f = Rotate(Angle2d(T(π / 2))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -547,7 +547,7 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = cartgrid(10, 10) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -558,7 +558,7 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -569,7 +569,7 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa TransformedGrid + @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d From a28e47c87f78f8a0a1c33ac694a9b16ebf717efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 2 Aug 2024 10:28:36 -0300 Subject: [PATCH 237/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9c48ec57a..84f550c27 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.7" +version = "0.47.8" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 2e1e458d630263dcad328480ad5cf6412baa1036 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:12:45 -0300 Subject: [PATCH 238/423] Fix 'TransformedGrid' alias (#975) --- src/mesh/transformedmesh.jl | 2 +- test/mesh.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 302e99327..b8a619f7c 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -34,7 +34,7 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) # alias to improve readability in IO methods -const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid{M,C,Dim},TR} = TransformedMesh{M,C,GridTopology{Dim},G,TR} +const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid,TR<:Transform} = TransformedMesh{M,C,GridTopology{Dim},G,TR} TransformedGrid(g::Grid, t::Transform) = TransformedMesh(g, t) diff --git a/test/mesh.jl b/test/mesh.jl index c044ae8f8..c41b7c60f 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -728,6 +728,11 @@ tmesh = Meshes.TransformedMesh(mesh, trans) @test manifold(tmesh) === 𝔼{3} @test crs(tmesh) <: Cartesian + trans = Proj(Polar) + tgrid = Meshes.TransformedMesh(grid, trans) + @test tgrid isa Meshes.TransformedGrid + @test manifold(tgrid) === 𝔼{2} + @test crs(tgrid) <: Polar # grid interface trans = Identity() From 4f2369900e9f08f2e0e743f3364a6964ca831427 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:53:17 -0300 Subject: [PATCH 239/423] Make 'DehnTriangulation' type-stable (#976) --- src/discretization.jl | 2 +- src/discretization/dehn.jl | 2 +- test/discretization.jl | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index c5784175b..1cffa5e76 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -81,7 +81,7 @@ function discretize(polygon::Polygon, method::BoundaryDiscretizationMethod) end end end - connec = connect.(Tuple.(einds)) + connec = [connect(ntuple(i -> inds[i], 3)) for inds in einds] # return mesh without duplicates SimpleMesh(points, connec) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index d023544e0..31d3b8a53 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -70,6 +70,6 @@ function dehn1899(v::AbstractVector{<:Point}, inds) [left; right] else # return the triangle - [connect(Tuple(inds), Triangle)] + [connect(ntuple(i -> inds[i], 3))] end end diff --git a/test/discretization.jl b/test/discretization.jl index 2ebd66c01..8f9b8044d 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -27,6 +27,10 @@ @test nelements(mesh) == 6 @test eltype(mesh) <: Triangle + # type stability tests + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @inferred discretize(poly, DehnTriangulation()) + octa = Octagon( cart(0.2, 0.2, 0.0), cart(0.5, -0.5, 0.0), From 57be2c9d0c080d7c9489b73c96c251f663e5b3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 2 Aug 2024 16:08:44 -0300 Subject: [PATCH 240/423] Improve docs --- docs/src/algorithms/merging.md | 17 ++++++++++++++++- docs/src/algorithms/sampling.md | 2 +- docs/src/algorithms/sideof.md | 22 ++++++++++++++++++++-- docs/src/algorithms/winding.md | 14 +++++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/src/algorithms/merging.md b/docs/src/algorithms/merging.md index 524c6597b..e7d3d259c 100644 --- a/docs/src/algorithms/merging.md +++ b/docs/src/algorithms/merging.md @@ -1,5 +1,20 @@ # Merging +Geometries and meshes can be [`merge`](@ref)d into a single +geometric object as illustrated in the following example. +The resulting type depends on the combination of input types, +and can be a [`Mesh`](@ref) or [`Multi`](@ref) geometry. + ```@docs merge(::Mesh, ::Mesh) -``` \ No newline at end of file +``` + +```@example merge +using Meshes # hide +import CairoMakie as Mke # hide + +g = CartesianGrid(2, 2) +t = Triangle((3, 0), (4, 0), (3, 1)) + +m = merge(g, t) +``` diff --git a/docs/src/algorithms/sampling.md b/docs/src/algorithms/sampling.md index 4a36ba0e7..a0db927f8 100644 --- a/docs/src/algorithms/sampling.md +++ b/docs/src/algorithms/sampling.md @@ -6,7 +6,7 @@ import CairoMakie as Mke # hide ``` ```@docs -sample +sample(::Any, ::SamplingMethod) SamplingMethod DiscreteSamplingMethod ContinuousSamplingMethod diff --git a/docs/src/algorithms/sideof.md b/docs/src/algorithms/sideof.md index 7979dd62e..5f844b657 100644 --- a/docs/src/algorithms/sideof.md +++ b/docs/src/algorithms/sideof.md @@ -1,6 +1,24 @@ # Sideof +The [`sideof`](@ref) function can be used to efficiently query the side +of multiple points with respect to a given geometry or mesh. + ```@docs SideType -sideof -``` \ No newline at end of file +sideof(::Point, ::Line) +sideof(::Point, ::Ring) +sideof(::Point, ::Mesh) +``` + +```@example sideof +using Meshes # hide + +sideof(Point(0, 0), Line((1, 0), (1, 1))) +``` + +```@example sideof +points = [Point(0, 0), Point(0.2, 0.2), Point(2, 1)] +polygon = Triangle((0, 0), (1, 0), (0, 1)) + +sideof(points, boundary(polygon)) +``` diff --git a/docs/src/algorithms/winding.md b/docs/src/algorithms/winding.md index 6c27587d6..a8d9c95ee 100644 --- a/docs/src/algorithms/winding.md +++ b/docs/src/algorithms/winding.md @@ -1,5 +1,17 @@ # Winding +The [`winding`](@ref) number is intimately connected to the +[`sideof`](@ref) function, which is used more often in applications. + ```@docs winding -``` \ No newline at end of file +``` + +```@example winding +using Meshes # hide + +points = [Point(0, 0), Point(0.2, 0.2), Point(2, 1)] +polygon = Triangle((0, 0), (1, 0), (0, 1)) + +winding(points, boundary(polygon)) +``` From 344d0f7e60b69697b1bb922c0255d5a548852b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 2 Aug 2024 16:10:01 -0300 Subject: [PATCH 241/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 84f550c27..172a8be83 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.8" +version = "0.47.9" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 50b4f1b60ea38b5add1226115e899a2952962ef1 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:22:46 -0300 Subject: [PATCH 242/423] Rename discretization methods (#977) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename discretization methods * Fix docs * Update docs/src/algorithms/discretization.md --------- Co-authored-by: Júlio Hoffimann --- docs/src/algorithms/discretization.md | 7 +- src/Meshes.jl | 4 +- src/discretization.jl | 93 +++++++++++++--------- src/discretization/dehn.jl | 2 +- src/discretization/delaunay.jl | 2 +- src/discretization/fan.jl | 2 +- src/discretization/held.jl | 2 +- src/discretization/{tetra.jl => manual.jl} | 14 ++-- test/discretization.jl | 6 +- 9 files changed, 73 insertions(+), 59 deletions(-) rename src/discretization/{tetra.jl => manual.jl} (66%) diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index 71940e6c8..db2ddebb3 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -10,7 +10,6 @@ discretize discretizewithin simplexify DiscretizationMethod -BoundaryDiscretizationMethod ``` ## FanTriangulation @@ -158,16 +157,16 @@ mesh = discretize(sphere, RegularDiscretization(10,10)) viz(mesh, showsegments = true) ``` -## Tetrahedralization +## ManualDiscretization ```@docs -Tetrahedralization +ManualDiscretization ``` ```@example discretization box = Box((0., 0., 0.), (1., 1., 1.)) -mesh = discretize(box, Tetrahedralization()) +mesh = discretize(box, ManualDiscretization()) viz(mesh, colors = 1:nelements(mesh)) ``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 7d8107e9a..f9e8bd6f7 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -489,12 +489,12 @@ export # discretization DiscretizationMethod, - BoundaryDiscretizationMethod, + BoundaryTriangulationMethod, FanTriangulation, DehnTriangulation, HeldTriangulation, DelaunayTriangulation, - Tetrahedralization, + ManualDiscretization, RegularDiscretization, discretize, discretizewithin, diff --git a/src/discretization.jl b/src/discretization.jl index 1cffa5e76..f0823df20 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -9,6 +9,13 @@ A method for discretizing geometries into meshes. """ abstract type DiscretizationMethod end +""" + TriangulationMethod + +A method for discretizing geometries into triangular meshes. +""" +abstract type TriangulationMethod <: DiscretizationMethod end + """ discretize(geometry, [method]) @@ -20,11 +27,11 @@ used with a specific number of elements. function discretize end """ - BoundaryDiscretizationMethod + BoundaryTriangulationMethod -A method for discretizing geometries based on their boundary. +A method for discretizing geometries into triangular meshes based on their boundary. """ -abstract type BoundaryDiscretizationMethod <: DiscretizationMethod end +abstract type BoundaryTriangulationMethod <: TriangulationMethod end """ discretizewithin(boundary, method) @@ -33,9 +40,43 @@ Discretize geometry within `boundary` with boundary discretization `method`. """ function discretizewithin end -discretize(geometry::Geometry, method::BoundaryDiscretizationMethod) = discretizewithin(boundary(geometry), method) +# ----------- +# DISCRETIZE +# ----------- + +discretize(geometry) = simplexify(geometry) + +discretize(ball::Ball{𝔼{2}}) = discretize(ball, RegularDiscretization(50)) + +discretize(disk::Disk) = discretize(disk, RegularDiscretization(50)) + +discretize(sphere::Sphere{𝔼{3}}) = discretize(sphere, RegularDiscretization(50)) + +discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(50)) + +discretize(torus::Torus) = discretize(torus, RegularDiscretization(50)) + +discretize(cyl::Cylinder) = discretize(cyl, RegularDiscretization(2, 50, 2)) + +discretize(cylsurf::CylinderSurface) = discretize(cylsurf, RegularDiscretization(50, 2)) + +discretize(consurf::ConeSurface) = discretize(consurf, RegularDiscretization(50, 2)) + +discretize(frustsurf::FrustumSurface) = discretize(frustsurf, RegularDiscretization(50, 2)) + +discretize(parsurf::ParaboloidSurface) = discretize(parsurf, RegularDiscretization(50)) + +discretize(multi::Multi) = mapreduce(discretize, merge, parent(multi)) + +discretize(mesh::Mesh) = mesh + +# ----------------- +# BOUNDARY METHODS +# ----------------- + +discretize(geometry::Geometry, method::BoundaryTriangulationMethod) = discretizewithin(boundary(geometry), method) -function discretize(polygon::Polygon, method::BoundaryDiscretizationMethod) +function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) # clean up polygon if necessary cpoly = polygon |> Repair{0}() |> Repair{8}() @@ -88,10 +129,10 @@ function discretize(polygon::Polygon, method::BoundaryDiscretizationMethod) end end -discretize(multi::Multi, method::BoundaryDiscretizationMethod) = +discretize(multi::Multi, method::BoundaryTriangulationMethod) = mapreduce(geom -> discretize(geom, method), merge, parent(multi)) -function discretizewithin(ring::Ring, method::BoundaryDiscretizationMethod) +function discretizewithin(ring::Ring, method::BoundaryTriangulationMethod) # collect vertices to get rid of static containers points = collect(vertices(ring)) @@ -112,35 +153,9 @@ function _proj2D(::Type{🌐}, points) end end -# ---------------- -# DEFAULT METHODS -# ---------------- - -discretize(geometry) = simplexify(geometry) - -discretize(ball::Ball{𝔼{2}}) = discretize(ball, RegularDiscretization(50)) - -discretize(disk::Disk) = discretize(disk, RegularDiscretization(50)) - -discretize(sphere::Sphere{𝔼{3}}) = discretize(sphere, RegularDiscretization(50)) - -discretize(ellipsoid::Ellipsoid) = discretize(ellipsoid, RegularDiscretization(50)) - -discretize(torus::Torus) = discretize(torus, RegularDiscretization(50)) - -discretize(cyl::Cylinder) = discretize(cyl, RegularDiscretization(2, 50, 2)) - -discretize(cylsurf::CylinderSurface) = discretize(cylsurf, RegularDiscretization(50, 2)) - -discretize(consurf::ConeSurface) = discretize(consurf, RegularDiscretization(50, 2)) - -discretize(frustsurf::FrustumSurface) = discretize(frustsurf, RegularDiscretization(50, 2)) - -discretize(parsurf::ParaboloidSurface) = discretize(parsurf, RegularDiscretization(50)) - -discretize(multi::Multi) = mapreduce(discretize, merge, parent(multi)) - -discretize(mesh::Mesh) = mesh +# ----------- +# SIMPLEXIFY +# ----------- """ simplexify(object) @@ -161,7 +176,7 @@ simplexify(box::Box{𝔼{1}}) = SimpleMesh(collect(extrema(box)), GridTopology(1 simplexify(box::Box{𝔼{2}}) = discretize(box, FanTriangulation()) -simplexify(box::Box{𝔼{3}}) = discretize(box, Tetrahedralization()) +simplexify(box::Box{𝔼{3}}) = discretize(box, ManualDiscretization()) simplexify(seg::Segment) = SimpleMesh(pointify(seg), GridTopology(1)) @@ -183,7 +198,7 @@ simplexify(circle::Circle) = discretize(circle, RegularDiscretization(50)) simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? DelaunayTriangulation() : DehnTriangulation()) -simplexify(poly::Polyhedron) = discretize(poly, Tetrahedralization()) +simplexify(poly::Polyhedron) = discretize(poly, ManualDiscretization()) simplexify(multi::Multi) = mapreduce(simplexify, merge, parent(multi)) @@ -230,5 +245,5 @@ include("discretization/fan.jl") include("discretization/dehn.jl") include("discretization/held.jl") include("discretization/delaunay.jl") -include("discretization/tetra.jl") +include("discretization/manual.jl") include("discretization/regular.jl") diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index 31d3b8a53..40867877c 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -19,7 +19,7 @@ with small number of vertices. * Devadoss, S & Rourke, J. 2011. [Discrete and computational geometry] (https://press.princeton.edu/books/hardcover/9780691145532/discrete-and-computational-geometry) """ -struct DehnTriangulation <: BoundaryDiscretizationMethod end +struct DehnTriangulation <: BoundaryTriangulationMethod end function discretizewithin(ring::Ring{𝔼{2}}, ::DehnTriangulation) # points on resulting mesh diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl index 38d52507c..12e7ebd05 100644 --- a/src/discretization/delaunay.jl +++ b/src/discretization/delaunay.jl @@ -18,7 +18,7 @@ Optionally, specify the random number generator `rng`. Wraps DelaunayTriangulation.jl. For any internal errors, file an issue at [DelaunayTriangulation.jl](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/issues/new) """ -struct DelaunayTriangulation{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod +struct DelaunayTriangulation{RNG<:AbstractRNG} <: BoundaryTriangulationMethod rng::RNG end diff --git a/src/discretization/fan.jl b/src/discretization/fan.jl index 38ad0c643..196f2e66e 100644 --- a/src/discretization/fan.jl +++ b/src/discretization/fan.jl @@ -9,7 +9,7 @@ The fan triangulation algorithm for convex polygons. See [https://en.wikipedia.org/wiki/Fan_triangulation] (https://en.wikipedia.org/wiki/Fan_triangulation). """ -struct FanTriangulation <: BoundaryDiscretizationMethod end +struct FanTriangulation <: BoundaryTriangulationMethod end function discretizewithin(ring::Ring, ::FanTriangulation) points = collect(vertices(ring)) diff --git a/src/discretization/held.jl b/src/discretization/held.jl index 1312a444b..7e80c31c5 100644 --- a/src/discretization/held.jl +++ b/src/discretization/held.jl @@ -26,7 +26,7 @@ generator `rng`. constrained Delaunay triangulation of polygons] (https://www.sciencedirect.com/science/article/pii/S092577211830004X) """ -struct HeldTriangulation{RNG<:AbstractRNG} <: BoundaryDiscretizationMethod +struct HeldTriangulation{RNG<:AbstractRNG} <: BoundaryTriangulationMethod rng::RNG shuffle::Bool end diff --git a/src/discretization/tetra.jl b/src/discretization/manual.jl similarity index 66% rename from src/discretization/tetra.jl rename to src/discretization/manual.jl index 236170ad2..2cd86eeab 100644 --- a/src/discretization/tetra.jl +++ b/src/discretization/manual.jl @@ -3,28 +3,28 @@ # ------------------------------------------------------------------ """ - Tetrahedralization() + ManualDiscretization() -A method to discretize geometries into tetrahedra. +Discretize geometries manually using indices of vertices. """ -struct Tetrahedralization <: DiscretizationMethod end +struct ManualDiscretization <: DiscretizationMethod end -function discretize(box::Box, ::Tetrahedralization) +function discretize(box::Box, ::ManualDiscretization) indices = [(1, 5, 6, 8), (1, 3, 4, 8), (1, 3, 6, 8), (1, 2, 3, 6), (3, 6, 7, 8)] SimpleMesh(pointify(box), connect.(indices, Tetrahedron)) end -function discretize(hexa::Hexahedron, ::Tetrahedralization) +function discretize(hexa::Hexahedron, ::ManualDiscretization) indices = [(1, 5, 6, 8), (1, 3, 4, 8), (1, 3, 6, 8), (1, 2, 3, 6), (3, 6, 7, 8)] SimpleMesh(pointify(hexa), connect.(indices, Tetrahedron)) end -function discretize(pyramid::Pyramid, ::Tetrahedralization) +function discretize(pyramid::Pyramid, ::ManualDiscretization) indices = [(1, 2, 4, 5), (3, 4, 2, 5)] SimpleMesh(pointify(pyramid), connect.(indices, Tetrahedron)) end -function discretize(wedge::Wedge, ::Tetrahedralization) +function discretize(wedge::Wedge, ::ManualDiscretization) indices = [(1, 2, 3, 4), (4, 5, 6, 2), (4, 5, 6, 3)] SimpleMesh(pointify(wedge), connect.(indices, Tetrahedron)) end diff --git a/test/discretization.jl b/test/discretization.jl index 8f9b8044d..41b310ccb 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -310,11 +310,11 @@ @test mesh[1] == Triangle(cart(0, 0), cart(0, 0), cart(0, 0)) end - @testset "Tetrahedralization" begin + @testset "ManualDiscretization" begin box = Box(cart(0, 0, 0), cart(1, 1, 1)) hexa = Hexahedron(pointify(box)...) - bmesh = discretize(box, Tetrahedralization()) - hmesh = discretize(hexa, Tetrahedralization()) + bmesh = discretize(box, ManualDiscretization()) + hmesh = discretize(hexa, ManualDiscretization()) @test bmesh == hmesh @test nvertices(bmesh) == 8 @test nelements(bmesh) == 5 From 432c3beb10f92223d923bbe7b20877355bb0bcdc Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Tue, 6 Aug 2024 15:13:04 +0200 Subject: [PATCH 243/423] Make VoronoiTessellation respect input order and improve performance (#978) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update voronoi * add test * maybe fix format * rename inner variables * Update src/tesselation/voronoi.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/tesselation.jl * Update test/tesselation.jl * fix typo * apply suggestion to tests * typo again * lazy version * refactor test * Update test/tesselation.jl * Update test/tesselation.jl * Remove tesselation test with LatLon * Apply suggestions * Apply suggestions --------- Co-authored-by: Júlio Hoffimann Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> --- src/Meshes.jl | 2 +- src/tesselation/voronoi.jl | 9 ++++++--- test/tesselation.jl | 10 +++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index f9e8bd6f7..b7debc8df 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -24,7 +24,7 @@ using NearestNeighbors: KDTree, BallTree using NearestNeighbors: knn, inrange using DelaunayTriangulation: triangulate, voronoi using DelaunayTriangulation: each_solid_triangle -using DelaunayTriangulation: each_polygon +using DelaunayTriangulation: get_polygons using DelaunayTriangulation: get_polygon_points using ScopedValues: ScopedValue using Base.Cartesian: @nloops, @nref, @ntuple diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index e53d17062..7db911376 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -39,9 +39,12 @@ function tesselate(pset::PointSet, method::VoronoiTesselation) coords = CoordRefSystems.reconstruct(C, T.(xy)) Point(coords) end - polygs = each_polygon(vorono) - tuples = [Tuple(inds[begin:(end - 1)]) for inds in polygs] - connec = connect.(tuples) + polygs = get_polygons(vorono) + connec = Vector{Connectivity}(undef, length(polygs)) + for (i, inds) in polygs + tup = ntuple(j -> inds[j], length(inds) - 1) + connec[i] = connect(tup, Ngon) + end mesh = SimpleMesh(points, connec) # remove unused points diff --git a/test/tesselation.jl b/test/tesselation.jl index 3b07e7d44..53446e799 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -36,14 +36,14 @@ mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) @test crs(mesh) === crs(pset) - coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] - pset = PointSet(Point.(coords)) - mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test crs(mesh) === crs(pset) - # error: the number of coordinates of the points must be 2 pts = randpoint3(10) pset = PointSet(pts) @test_throws AssertionError tesselate(pset, VoronoiTesselation(StableRNG(2024))) + + # Test polygon order is the same as input points order + pts = randpoint2(10) + mesh = tesselate(pts, VoronoiTesselation(StableRNG(2024))) + @test all(sideof(p, boundary(poly)) ∈ (IN, ON) for (p, poly) in zip(pts, mesh)) end end From 072680f97036eb3e21d9028606f7a196539bbd6b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:15:44 -0300 Subject: [PATCH 244/423] Optimize viz of `TransformedGrid` with `Proj` transform (#981) * Optimize viz of 'TransformedGrid' with 'Proj' transform * Apply suggestions --- ext/MeshesMakieExt.jl | 3 +++ ext/grid/transformed.jl | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index ac98d2511..dd6ea4630 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -9,8 +9,11 @@ using Unitful using Rotations using StaticArrays using LinearAlgebra +using CoordRefSystems using Colorfy +using CoordRefSystems: Projected + import TransformsBase as TB import Meshes: viz, viz! diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index c35d76a48..c5bbc9fea 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -2,14 +2,18 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -isoptimized(::TB.Identity) = true -isoptimized(t::TB.SequentialTransform) = all(isoptimized, t) +isoptimized(::Type, ::TB.Identity) = true +isoptimized(CRS::Type, t::TB.SequentialTransform) = all(tᵢ -> isoptimized(CRS, tᵢ), t) -isoptimized(::GeometricTransform) = false -isoptimized(::Rotate{<:Angle2d}) = true -isoptimized(::Translate) = true -isoptimized(::Scale) = true -function isoptimized(t::Affine{2}) +isoptimized(::Type, ::GeometricTransform) = false + +isoptimized(::Type{<:Cartesian2D}, ::Proj{<:Projected}) = true +isoptimized(::Type{<:Projected}, ::Proj{<:Cartesian2D}) = true + +isoptimized(::Type, ::Rotate{<:Angle2d}) = true +isoptimized(::Type, ::Translate) = true +isoptimized(::Type, ::Scale) = true +function isoptimized(::Type, t::Affine{2}) A, _ = TB.parameters(t) isdiag(A) || isrotation(A) end @@ -24,7 +28,7 @@ function transformedgrid!(plot, fallback) tgrid = plot[:object] grid = Makie.@lift parent($tgrid) trans = Makie.@lift Meshes.transform($tgrid) - if isoptimized(trans[]) + if isoptimized(crs(grid[]), trans[]) color = plot[:color] alpha = plot[:alpha] colormap = plot[:colormap] @@ -40,6 +44,8 @@ end makietransform!(plot, trans::Makie.Observable{<:TB.Identity}) = nothing +makietransform!(plot, trans::Makie.Observable{<:Proj}) = nothing + makietransform!(plot, trans::Makie.Observable{<:TB.SequentialTransform}) = foreach(t -> makietransform!(plot, Makie.Observable(t)), trans[]) From 1fa0329044e9336bfdb7daa8da652c22925f690d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 6 Aug 2024 13:40:01 -0300 Subject: [PATCH 245/423] Use efficient point in poly for 2D Ngon (#983) --- src/predicates/in.jl | 28 ++++++++++++++-------------- test/tesselation.jl | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 71bb4efa2..85b789b74 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -110,7 +110,20 @@ function Base.in(p::Point, t::Torus) (R - √(x^2 + y^2))^2 + z^2 ≤ r^2 end -function Base.in(p::Point, t::Triangle) +function Base.in(point::Point, poly::Polygon{𝔼{2}}) + r = rings(poly) + inside = sideof(point, first(r)) != OUT + if hasholes(poly) + outside = all(sideof(point, r[i]) == OUT for i in 2:length(r)) + inside && outside + else + inside + end +end + +Base.in(p::Point, poly::Polygon{𝔼{3}}) = any(Δ -> p ∈ Δ, simplexify(poly)) + +function Base.in(p::Point, t::Triangle{𝔼{3}}) # given coordinates a, b, c = vertices(t) @@ -137,19 +150,6 @@ function Base.in(p::Point, t::Triangle) λ₂ ≥ 0 && λ₃ ≥ 0 && (λ₂ + λ₃) ≤ 1 end -Base.in(p::Point, ngon::Ngon) = any(Δ -> p ∈ Δ, simplexify(ngon)) - -function Base.in(point::Point, poly::Polygon) - r = rings(poly) - inside = sideof(point, first(r)) != OUT - if hasholes(poly) - outside = all(sideof(point, r[i]) == OUT for i in 2:length(r)) - inside && outside - else - inside - end -end - Base.in(p::Point, m::Multi) = any(g -> p ∈ g, parent(m)) """ diff --git a/test/tesselation.jl b/test/tesselation.jl index 53446e799..64c8f3c95 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -44,6 +44,6 @@ # Test polygon order is the same as input points order pts = randpoint2(10) mesh = tesselate(pts, VoronoiTesselation(StableRNG(2024))) - @test all(sideof(p, boundary(poly)) ∈ (IN, ON) for (p, poly) in zip(pts, mesh)) + @test all(p ∈ poly for (p, poly) in zip(pts, mesh)) end end From 96b458c4d7fa3ced7297da5fff078dcde60ee4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 6 Aug 2024 14:11:30 -0300 Subject: [PATCH 246/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 172a8be83..bf9aef418 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.47.9" +version = "0.48.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 5d62b07d2c48537b4e1440f4ab88b9556609de70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 7 Aug 2024 14:34:48 -0300 Subject: [PATCH 247/423] Implement reverse for Segment --- src/polytopes/segment.jl | 2 ++ test/polytopes.jl | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/polytopes/segment.jl b/src/polytopes/segment.jl index aed6a3ba3..588302cc4 100644 --- a/src/polytopes/segment.jl +++ b/src/polytopes/segment.jl @@ -38,3 +38,5 @@ function (s::Segment)(t) a, b = s.vertices a + t * (b - a) end + +Base.reverse(s::Segment) = Segment(reverse(extrema(s))) diff --git a/test/polytopes.jl b/test/polytopes.jl index 8ac3c53b0..cf887a850 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -13,6 +13,7 @@ @test s ≈ s @test !(s ≈ Segment(cart(2.0), cart(1.0))) @test !(s ≈ Segment(cart(-1.0), cart(2.0))) + @test reverse(s) == Segment(cart(2.0), cart(1.0)) s = Segment(cart(0, 0), cart(1, 1)) @test minimum(s) == cart(0, 0) @@ -28,6 +29,7 @@ @test s ≈ s @test !(s ≈ Segment(cart(1, 1), cart(0, 0))) @test !(s ≈ Segment(cart(1, 2), cart(0, 0))) + @test reverse(s) == Segment(cart(1, 1), cart(0, 0)) s = Segment(cart(0, 0, 0), cart(1, 1, 1)) @test all(cart(x, x, x) ∈ s for x in 0:0.01:1) @@ -36,6 +38,7 @@ @test s ≈ s @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 0, 0))) @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 1, 0))) + @test reverse(s) == Segment(cart(1, 1, 1), cart(0, 0, 0)) s = Segment(cart(0, 0), cart(1, 1)) equaltest(s) From 43af54c07ff937562e40c9059552e58c7c1c5312 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:27:51 -0300 Subject: [PATCH 248/423] Update `Proj` show (#985) * Update 'Proj' show * Apply suggestions --- src/transforms/proj.jl | 12 ++++++++++++ test/transforms.jl | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 9cccc6a25..e21e3091b 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -40,3 +40,15 @@ applycoord(::Proj{CRS}, p::Point) where {CRS} = Point(convert(CRS, coords(p))) applycoord(t::Proj, g::RectilinearGrid) = applycoord(t, convert(SimpleMesh, g)) applycoord(t::Proj, g::StructuredGrid) = applycoord(t, convert(SimpleMesh, g)) + +# ----------- +# IO METHODS +# ----------- + +Base.show(io::IO, ::Proj{CRS}) where {CRS} = print(io, "Proj(CRS: $CRS)") + +function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS}) where {CRS} + summary(io, t) + println(io) + print(io, "└─ CRS: $CRS") +end diff --git a/test/transforms.jl b/test/transforms.jl index ddf742936..1230ec304 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1109,6 +1109,11 @@ @test TB.parameters(Proj(Polar)) == (; CRS=Polar) @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) + f = Proj(Mercator) + @test sprint(show, f) == "Proj(CRS: Mercator)" + @test sprint(show, MIME"text/plain"(), f) == """ + Proj transform + └─ CRS: Mercator""" # ---- # VEC From d4c1891e221eda482e851e59585b21b1b20b3d62 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:44:48 -0300 Subject: [PATCH 249/423] Update 'Repair' show (#986) --- src/transforms/repair.jl | 12 ++++++++++++ test/transforms.jl | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 7ae137eaa..74193db6a 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -217,3 +217,15 @@ apply(::Repair, geom::Geometry) = geom, nothing apply(t::Repair, multi::Multi) = Multi([t(g) for g in parent(multi)]), nothing apply(t::Repair, dom::Domain) = GeometrySet([t(g) for g in dom]), nothing + +# ----------- +# IO METHODS +# ----------- + +Base.show(io::IO, ::Repair{K}) where {K} = print(io, "Repair(K: $K)") + +function Base.show(io::IO, ::MIME"text/plain", t::Repair{K}) where {K} + summary(io, t) + println(io) + print(io, "└─ K: $K") +end diff --git a/test/transforms.jl b/test/transforms.jl index 1230ec304..690a7a495 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1730,6 +1730,12 @@ rpoly = poly |> Repair{0}() @test nvertices(rpoly) == 4 @test vertices(rpoly) == cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + + repair = Repair{0}() + @test sprint(show, repair) == "Repair(K: 0)" + @test sprint(show, MIME"text/plain"(), repair) == """ + Repair transform + └─ K: 0""" end @testset "Repair{1}" begin From 6aa7dbd57c4f344f3f8c5f1c114ceee30aff057c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 7 Aug 2024 18:26:35 -0300 Subject: [PATCH 250/423] Handle orientation in measure of 2D Polygon --- src/measures.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/measures.jl b/src/measures.jl index 0b2e01c8c..20097bfa2 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -82,18 +82,19 @@ function measure(t::Triangle) norm((B - A) × (C - A)) / 2 end +function measure(t::Tetrahedron) + A, B, C, D = vertices(t) + abs((A - D) ⋅ ((B - D) × (C - D))) / 6 +end + function measure(p::Polygon{𝔼{2}}) - sum(rings(p)) do r + Σ = sum(rings(p)) do r v = vertices(r) n = nvertices(r) c = centroid(r) sum(signarea(c, v[i], v[i + 1]) for i in 1:n) end -end - -function measure(t::Tetrahedron) - A, B, C, D = vertices(t) - abs((A - D) ⋅ ((B - D) × (C - D))) / 6 + abs(Σ) end measure(c::Chain) = sum(measure, segments(c)) From 020ca785bf9d2635a5b11ec3b113895a303f3362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 7 Aug 2024 18:27:15 -0300 Subject: [PATCH 251/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index bf9aef418..9b7bee78e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.0" +version = "0.48.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f8787fa0caa8e94ad7dcb74fecd18584997c614a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 8 Aug 2024 08:13:37 -0300 Subject: [PATCH 252/423] Add outer ctor for Repair (#987) --- docs/src/transforms.md | 2 +- src/discretization.jl | 2 +- src/tesselation/voronoi.jl | 2 +- src/transforms/bridge.jl | 2 +- src/transforms/repair.jl | 6 ++-- test/transforms.jl | 58 +++++++++++++++++++------------------- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 6153b4093..5d68062b6 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -204,7 +204,7 @@ points = [(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)] connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) mesh = SimpleMesh(points, connec) -rmesh = mesh |> Repair{1}() +rmesh = mesh |> Repair(1) ``` ## Bridge diff --git a/src/discretization.jl b/src/discretization.jl index f0823df20..ebf0fbe7d 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -78,7 +78,7 @@ discretize(geometry::Geometry, method::BoundaryTriangulationMethod) = discretize function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) # clean up polygon if necessary - cpoly = polygon |> Repair{0}() |> Repair{8}() + cpoly = polygon |> Repair(0) |> Repair(8) # handle degenerate polygons if nvertices(cpoly) == 1 diff --git a/src/tesselation/voronoi.jl b/src/tesselation/voronoi.jl index 7db911376..1cfa92a0e 100644 --- a/src/tesselation/voronoi.jl +++ b/src/tesselation/voronoi.jl @@ -48,5 +48,5 @@ function tesselate(pset::PointSet, method::VoronoiTesselation) mesh = SimpleMesh(points, connec) # remove unused points - mesh |> Repair{1}() + mesh |> Repair(1) end diff --git a/src/transforms/bridge.jl b/src/transforms/bridge.jl index 89edf6f3d..1ec9e1a30 100644 --- a/src/transforms/bridge.jl +++ b/src/transforms/bridge.jl @@ -28,7 +28,7 @@ function apply(transform::Bridge, poly::PolyArea) ℒ = lentype(poly) # sort rings lexicographically - rpoly, rinds = apply(Repair{9}(), poly) + rpoly, rinds = apply(Repair(9), poly) # retrieve bridge width δ = convert(ℒ, transform.δ) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 74193db6a..3b6402f55 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - Repair{K} + Repair(K) Perform repairing operation with code `K`. @@ -27,11 +27,13 @@ Perform repairing operation with code `K`. ``` # remove duplicates and degenerates -mesh |> Repair{0}() |> Repair{3}() +mesh |> Repair(0) |> Repair(3) ``` """ struct Repair{K} <: GeometricTransform end +Repair(K) = Repair{K}() + # -------------- # OPERATION (0) # -------------- diff --git a/test/transforms.jl b/test/transforms.jl index 690a7a495..5559d663a 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1724,47 +1724,47 @@ @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) end - @testset "Repair{0}" begin + @testset "Repair(0)" begin @test !isaffine(Repair) poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) - rpoly = poly |> Repair{0}() + rpoly = poly |> Repair(0) @test nvertices(rpoly) == 4 @test vertices(rpoly) == cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - repair = Repair{0}() + repair = Repair(0) @test sprint(show, repair) == "Repair(K: 0)" @test sprint(show, MIME"text/plain"(), repair) == """ Repair transform └─ K: 0""" end - @testset "Repair{1}" begin + @testset "Repair(1)" begin # a tetrahedron with an unused vertex points = cart.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) mesh = SimpleMesh(points, connec) - rmesh = mesh |> Repair{1}() + rmesh = mesh |> Repair(1) @test nvertices(rmesh) == nvertices(mesh) - 1 @test nelements(rmesh) == nelements(mesh) @test cart(5, 5, 5) ∉ vertices(rmesh) end - @testset "Repair{2}" begin end + @testset "Repair(2)" begin end - @testset "Repair{3}" begin end + @testset "Repair(3)" begin end - @testset "Repair{4}" begin end + @testset "Repair(4)" begin end - @testset "Repair{5}" begin end + @testset "Repair(5)" begin end - @testset "Repair{6}" begin end + @testset "Repair(6)" begin end - @testset "Repair{7}" begin + @testset "Repair(7)" begin # mesh with inconsistent orientation points = randpoint3(6) connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) mesh = SimpleMesh(points, connec) - rmesh = mesh |> Repair{7}() + rmesh = mesh |> Repair(7) topo = topology(mesh) rtopo = topology(rmesh) e = collect(elements(topo)) @@ -1775,25 +1775,25 @@ @test n[4] != e[4] end - @testset "Repair{8}" begin + @testset "Repair(8)" begin poly = PolyArea( cart.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) ) - rpoly = poly |> Repair{8}() + rpoly = poly |> Repair(8) @test nvertices(rpoly) == 4 @test vertices(rpoly) == cart.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) # degenerate triangle with repeated vertices poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) - rpoly = poly |> Repair{8}() + rpoly = poly |> Repair(8) @test !hasholes(rpoly) @test rings(rpoly) == [Ring(cart(0, 0))] @test vertices(rpoly) == [cart(0, 0)] end - @testset "Repair{9}" begin + @testset "Repair(9)" begin quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - repair = Repair{9}() + repair = Repair(9) rquad, cache = TB.apply(repair, quad) @test rquad isa Quadrangle @test rquad == quad @@ -1802,16 +1802,16 @@ inner1 = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) inner2 = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) poly = PolyArea([outer, inner1, inner2]) - repair = Repair{9}() + repair = Repair(9) rpoly, cache = TB.apply(repair, poly) @test rpoly == PolyArea([outer, inner2, inner1]) end - @testset "Repair{10}" begin + @testset "Repair(10)" begin outer = Ring(cart.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) inner = Ring(cart.([(1, 1), (1, 2), (2, 2), (2, 1)])) poly = PolyArea(outer, inner) - repair = Repair{10}() + repair = Repair(10) rpoly, cache = TB.apply(repair, poly) @test nvertices(rpoly) == nvertices(poly) @test length(first(rings(rpoly))) > length(first(rings(poly))) @@ -1819,34 +1819,34 @@ @test opoly == poly end - @testset "Repair{11}" begin + @testset "Repair(11)" begin outer = cart.([(0, 0), (0, 2), (2, 2), (2, 0)]) inner = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) poly = PolyArea(outer, inner) - repair = Repair{11}() + repair = Repair(11) rpoly, cache = TB.apply(repair, poly) router, rinner = rings(rpoly) @test router == Ring(cart.([(0, 0), (2, 0), (2, 2), (0, 2)])) @test rinner == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) end - @testset "Repair{12}" begin + @testset "Repair(12)" begin poly = PolyArea(cart.([(0, 0), (1, 0)])) - repair = Repair{12}() + repair = Repair(12) rpoly, cache = TB.apply(repair, poly) @test rpoly == PolyArea(cart.([(0, 0), (0.5, 0.0), (1, 0)])) outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) inner = cart.([(1, 2), (2, 3)]) poly = PolyArea(outer, inner) - repair = Repair{12}() + repair = Repair(12) rpoly, cache = TB.apply(repair, poly) @test rpoly == PolyArea(outer) end @testset "Repair fallbacks" begin quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - repair = Repair{10}() + repair = Repair(10) rquad, cache = TB.apply(repair, quad) @test rquad isa Quadrangle @test rquad == quad @@ -1854,14 +1854,14 @@ poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) multi = Multi([poly1, poly2]) - repair = Repair{11}() + repair = Repair(11) rmulti, cache = TB.apply(repair, multi) @test rmulti == Multi([repair(poly1), repair(poly2)]) poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) gset = GeometrySet([poly1, poly2]) - repair = Repair{11}() + repair = Repair(11) rgset, cache = TB.apply(repair, gset) @test rgset == GeometrySet([repair(poly1), repair(poly2)]) end @@ -1888,7 +1888,7 @@ # unique and bridges poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) - cpoly = poly |> Repair{0}() |> Bridge() + cpoly = poly |> Repair(0) |> Bridge() @test cpoly == PolyArea(cart.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) # basic ngon tests From 35b999de7ddfbeb11f4106c9076e2a63e704c664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 8 Aug 2024 08:14:00 -0300 Subject: [PATCH 253/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9b7bee78e..886584467 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.1" +version = "0.48.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 78fc13355d7075a94756c8ec0360dbfb90f29b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 8 Aug 2024 11:27:22 -0300 Subject: [PATCH 254/423] Refactor viz for TransformedGrid (#990) --- ext/grid/transformed.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index c5bbc9fea..9da161255 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -19,12 +19,12 @@ function isoptimized(::Type, t::Affine{2}) end vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) = - transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) + viztransfgrid!(plot, M, pdim, edim) vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{3}, edim::Val{3}) = - transformedgrid!(plot, plot -> vizmesh!(plot, M, pdim, edim)) + viztransfgrid!(plot, M, pdim, edim) -function transformedgrid!(plot, fallback) +function viztransfgrid!(plot, M, pdim, edim) tgrid = plot[:object] grid = Makie.@lift parent($tgrid) trans = Makie.@lift Meshes.transform($tgrid) @@ -38,7 +38,7 @@ function transformedgrid!(plot, fallback) viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) else - fallback(plot) + vizmesh!(plot, M, pdim, edim) end end From 7b5fcad1de0479ef284698a93e6a866a9f4e0945 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 8 Aug 2024 13:18:54 -0300 Subject: [PATCH 255/423] Fix show of big meshes (#991) * Fix show of big meshes * Fix code --- src/ioutils.jl | 23 +++++++++-------------- src/mesh.jl | 6 ++---- src/polytopes/polyarea.jl | 2 +- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/ioutils.jl b/src/ioutils.jl index e6294301a..407d8086b 100644 --- a/src/ioutils.jl +++ b/src/ioutils.jl @@ -11,27 +11,22 @@ function prettyname(T::Type) replace(name, r".+\." => "") end -# helper function to print a large indexable collection -# in multiple lines with a given tabulation -function printelms(io::IO, elms, tab="") - N = length(elms) - I, J = N > 10 ? (5, N - 4) : (N - 1, N) +# helper function to print the elements of an object +# in multiple lines with a given number of elements, getter and tabulation +function printelms(io::IO, obj; nelms=length(obj), getelm=getindex, tab="") + I, J = nelms > 10 ? (5, nelms - 4) : (nelms - 1, nelms) for i in 1:I - println(io, "$(tab)├─ $(elms[i])") + println(io, "$(tab)├─ $(getelm(obj, i))") end - if N > 10 + if nelms > 10 println(io, "$(tab)⋮") end - for i in J:(N - 1) - println(io, "$(tab)├─ $(elms[i])") + for i in J:(nelms - 1) + println(io, "$(tab)├─ $(getelm(obj, i))") end - print(io, "$(tab)└─ $(elms[N])") + print(io, "$(tab)└─ $(getelm(obj, nelms))") end -# helper function to print a large iterable -# calling the printelms function -printitr(io::IO, itr, tab="") = printelms(io, collect(itr), tab) - # helper function to print the polygons vertices function printverts(io::IO, verts) ioctx = IOContext(io, :compact => true) diff --git a/src/mesh.jl b/src/mesh.jl index 92a94df9c..9ff68fa98 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -126,17 +126,15 @@ topoconvert(TP::Type{<:Topology}, m::Mesh) = SimpleMesh(vertices(m), convert(TP, function Base.show(io::IO, ::MIME"text/plain", m::Mesh) t = topology(m) - verts = vertices(m) - elems = elements(t) nvert = nvertices(m) nelms = nelements(m) summary(io, m) println(io) println(io, " $nvert vertices") - printelms(io, verts, " ") + printelms(io, m, nelms=nvert, getelm=vertex, tab=" ") println(io) println(io, " $nelms elements") - printitr(io, elems, " ") + printelms(io, t, nelms=nelms, getelm=element, tab=" ") end """ diff --git a/src/polytopes/polyarea.jl b/src/polytopes/polyarea.jl index 4d0e6b162..ee0a8f4ee 100644 --- a/src/polytopes/polyarea.jl +++ b/src/polytopes/polyarea.jl @@ -69,6 +69,6 @@ function Base.show(io::IO, ::MIME"text/plain", p::PolyArea) if length(rings) > 1 println(io) println(io, " inner") - printelms(io, @view(rings[2:end]), " ") + printelms(io, @view(rings[2:end]), tab=" ") end end From 14cb7b6d532e2789aaccbc761cb4456867b793bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 8 Aug 2024 14:25:45 -0300 Subject: [PATCH 256/423] Fix fallback for Base.in predicate --- src/predicates/in.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 85b789b74..4472e9a75 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -7,7 +7,7 @@ Tells whether or not the `point` is in the `geometry`. """ -Base.in(p::Point, g::Geometry) = sideof(p, boundary(g)) == IN +Base.in(p::Point, g::Geometry) = sideof(p, boundary(g)) != OUT Base.in(p₁::Point, p₂::Point) = p₁ == p₂ From a089e10e522a1d8d7c48f54a99aa0841522f3b92 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:07:11 -0300 Subject: [PATCH 257/423] Use 'paramdim' in grid methods instead of 'embeddim' (#992) --- src/mesh.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index 9ff68fa98..7b3df46c3 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -174,11 +174,13 @@ function XYZ end Base.size(g::Grid) = size(topology(g)) +paramdim(g::Grid) = length(size(g)) + vertex(g::Grid, ind::Int) = vertex(g, CartesianIndices(size(g) .+ 1)[ind]) vertex(g::Grid, ijk::Dims) = vertex(g, LinearIndices(size(g) .+ 1)[ijk...]) -Base.minimum(g::Grid) = vertex(g, ntuple(i -> 1, embeddim(g))) +Base.minimum(g::Grid) = vertex(g, ntuple(i -> 1, paramdim(g))) Base.maximum(g::Grid) = vertex(g, size(g) .+ 1) Base.extrema(g::Grid) = minimum(g), maximum(g) @@ -201,7 +203,7 @@ Base.getindex(g::Grid, ijk::Int...) = element(g, LinearIndices(size(g))[ijk...]) @propagate_inbounds function Base.getindex(g::Grid, ijk...) dims = size(g) - ranges = ntuple(i -> _asrange(dims[i], ijk[i]), embeddim(g)) + ranges = ntuple(i -> _asrange(dims[i], ijk[i]), paramdim(g)) getindex(g, CartesianIndices(ranges)) end From 858c84faffda05fd14df3f594b60f58bd559679d Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:31:13 -0300 Subject: [PATCH 258/423] Add support for `TransformedGrid` in `RegularCoarsening` and `RegularRefinement` (#994) * Add support for 'TransformedGrid' in 'RegularCoarsening' and 'RegularRefinement' * Fix typo --- src/coarsening/regular.jl | 3 +++ src/refinement/regular.jl | 3 +++ test/coarsening.jl | 4 ++++ test/refinement.jl | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 21909594f..49c3af0e3 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -42,3 +42,6 @@ function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where { XYZₜ = ntuple(i -> XYZₛ[i][rngs...], embeddim(grid)) StructuredGrid{Datum}(XYZₜ) end + +coarsen(grid::TransformedGrid, method::RegularCoarsening) = + TransformedGrid(coarsen(parent(grid), method), transform(grid)) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 3c229b549..e7d8fba2e 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -38,6 +38,9 @@ function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {D StructuredGrid{Datum}(XYZ′) end +refine(grid::TransformedGrid, method::RegularRefinement) = + TransformedGrid(refine(parent(grid), method), transform(grid)) + function _refinedims(x, f) x′ = mapreduce(vcat, 1:(length(x) - 1)) do i range(x[i], x[i + 1], f + 1)[begin:(end - 1)] diff --git a/test/coarsening.jl b/test/coarsening.jl index b9d6072bb..1bde9fdc2 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -10,6 +10,8 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid + tfgrid = Meshes.TransformedGrid(grid, Identity()) + @test coarsen(tfgrid, RegularCoarsening(2)) == coarsen(grid, RegularCoarsening(2)) grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 5)) @@ -25,5 +27,7 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid + tfgrid = Meshes.TransformedGrid(grid, Identity()) + @test coarsen(tfgrid, RegularCoarsening(2, 4, 5)) == coarsen(grid, RegularCoarsening(2, 4, 5)) end end diff --git a/test/refinement.jl b/test/refinement.jl index 5f9a84b17..43867e7a7 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -53,6 +53,8 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test refine(sgrid, RegularRefinement(2)) == tsgrid + tfgrid = Meshes.TransformedGrid(grid, Identity()) + @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) # 3D grids grid = cartgrid(3, 3, 3) @@ -64,6 +66,8 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test refine(sgrid, RegularRefinement(2)) == tsgrid + tfgrid = Meshes.TransformedGrid(grid, Identity()) + @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) end @testset "CatmullClark" begin From 8b48de40d09f056ca7a43c8ab8e5b073f568004c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 8 Aug 2024 17:50:55 -0300 Subject: [PATCH 259/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 886584467..4d6ad9f3c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.2" +version = "0.48.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 52e86a2944717bb286b4932c0a0e9c9e639657fb Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 9 Aug 2024 13:29:50 +0200 Subject: [PATCH 260/423] Make sideof(::Point, ::Ring) serial when convenient (#993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial implementation * apply formatting * add consistency test between serial and parallel * other formatting stuff * hopefully last format * review style suggestions * More adjustments * Refactor test * More adjustments * More adjustments --------- Co-authored-by: Júlio Hoffimann --- src/sideof.jl | 169 ++++++++++++++++++++++++++++--------------------- test/sideof.jl | 7 ++ 2 files changed, 103 insertions(+), 73 deletions(-) diff --git a/src/sideof.jl b/src/sideof.jl index 6fb576b1a..29fad0a3d 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -58,86 +58,109 @@ Possible results are `IN`, `OUT` or `ON` the `ring`. """ function sideof(point::Point, ring::Ring) assertion(CoordRefSystems.ncoords(crs(point)) == 2, "points must have 2 coordinates") + if nvertices(ring) ≤ 1000 || Threads.nthreads() == 1 + _sideofserial(point, ring) + else + _sideofthreads(point, ring) + end +end + +function _sideofserial(p::Point, r::Ring) + v = vertices(r) + k = 0 + for i in eachindex(v) + ison, addk = _sideofcore(p, v[i], v[i + 1]) + ison && return ON + addk && (k += 1) + end + iseven(k) ? OUT : IN +end - v = vertices(ring) - n = nvertices(ring) +function _sideofthreads(p::Point, r::Ring) + v = vertices(r) + k = Threads.Atomic{Int}(0) + on = Threads.Atomic{Bool}(false) + Threads.@threads for i in eachindex(v) + ison, addk = _sideofcore(p, v[i], v[i + 1]) + (on[] = ison) && break + addk && Threads.atomic_add!(k, 1) + end + on[] ? ON : (iseven(k[]) ? OUT : IN) +end +function _sideofcore(p::Point, pᵢ::Point, pⱼ::Point) # flat coordinates of query point - p = flat(coords(point)) - xₚ, yₚ = p.x, p.y + cₚ = flat(coords(p)) + xₚ, yₚ = cₚ.x, cₚ.y + + # possible return values for readability + ISON = (true, false) # ison=true, addk=false + ADDK = (false, true) # ison=false, addk=true + NONE = (false, false) # ison=false, addk=false + + # flat coordinates of segment i -- i+1 + cᵢ = flat(coords(pᵢ)) + cⱼ = flat(coords(pⱼ)) + xᵢ, yᵢ = cᵢ.x, cᵢ.y + xⱼ, yⱼ = cⱼ.x, cⱼ.y + + v₁ = yᵢ - yₚ + v₂ = yⱼ - yₚ + + if (isnegative(v₁) && isnegative(v₂)) || (ispositive(v₁) && ispositive(v₂)) + # case 11, 26 + return NONE + end - k = Threads.Atomic{Int}(0) - ison = Threads.Atomic{Bool}(false) - Threads.@threads for i in 1:n - # premature termination if point is on ring - ison[] && break - - # flat coordinates of segment i -- i+1 - pᵢ = flat(coords(v[i])) - pⱼ = flat(coords(v[i + 1])) - xᵢ, yᵢ = pᵢ.x, pᵢ.y - xⱼ, yⱼ = pⱼ.x, pⱼ.y - - v₁ = yᵢ - yₚ - v₂ = yⱼ - yₚ - - if (isnegative(v₁) && isnegative(v₂)) || (ispositive(v₁) && ispositive(v₂)) - # case 11, 26 - continue + u₁ = xᵢ - xₚ + u₂ = xⱼ - xₚ + + if ispositive(v₂) && isnonpositive(v₁) + # case 3, 9, 16, 21, 13, 24 + f = u₁ * v₂ - u₂ * v₁ + if ispositive(f) + # case 3, 9 + return ADDK + elseif isequalzero(f) + # case 16, 21 + return ISON end - - u₁ = xᵢ - xₚ - u₂ = xⱼ - xₚ - - if ispositive(v₂) && isnonpositive(v₁) - # case 3, 9, 16, 21, 13, 24 - f = u₁ * v₂ - u₂ * v₁ - if ispositive(f) - # case 3, 9 - Threads.atomic_add!(k, 1) - elseif isequalzero(f) - # case 16, 21 - ison[] = true - end - elseif ispositive(v₁) && isnonpositive(v₂) - # case 4, 10, 19, 20, 12, 25 - f = u₁ * v₂ - u₂ * v₁ - if isnegative(f) - # case 4, 10 - Threads.atomic_add!(k, 1) - elseif isequalzero(f) - # case 19, 20 - ison[] = true - end - elseif isequalzero(v₂) && isnegative(v₁) - # case 7, 14, 17 - f = u₁ * v₂ - u₂ * v₁ - if isequalzero(f) - # case 17 - ison[] = true - end - elseif isequalzero(v₁) && isnegative(v₂) - # case 8, 15, 18 - f = u₁ * v₂ - u₂ * v₁ - if isequalzero(f) - # case 18 - ison[] = true - end - elseif isequalzero(v₁) && isequalzero(v₂) - # case 1, 2, 5, 6, 22, 23 - if isnonpositive(u₂) && isnonnegative(u₁) - # case 1 - ison[] = true - elseif isnonpositive(u₁) && isnonnegative(u₂) - # case 2 - ison[] = true - end + elseif ispositive(v₁) && isnonpositive(v₂) + # case 4, 10, 19, 20, 12, 25 + f = u₁ * v₂ - u₂ * v₁ + if isnegative(f) + # case 4, 10 + return ADDK + elseif isequalzero(f) + # case 19, 20 + return ISON + end + elseif isequalzero(v₂) && isnegative(v₁) + # case 7, 14, 17 + f = u₁ * v₂ - u₂ * v₁ + if isequalzero(f) + # case 17 + return ISON + end + elseif isequalzero(v₁) && isnegative(v₂) + # case 8, 15, 18 + f = u₁ * v₂ - u₂ * v₁ + if isequalzero(f) + # case 18 + return ISON + end + elseif isequalzero(v₁) && isequalzero(v₂) + # case 1, 2, 5, 6, 22, 23 + if isnonpositive(u₂) && isnonnegative(u₁) + # case 1 + return ISON + elseif isnonpositive(u₁) && isnonnegative(u₂) + # case 2 + return ISON end end - - # when `ison` is true, the value of `k` might be incorrectly set by multiple threads, - # however that does not matter in the following return statement - ison[] ? ON : (iseven(k[]) ? OUT : IN) + # case 5, 6, 7, 8, 12, 13, 14, 15, 22, 23, 24, 25 + return NONE end # ----- diff --git a/test/sideof.jl b/test/sideof.jl index 50a5b5268..7d99b9d3d 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -62,4 +62,11 @@ connec = connect.([(1, 2, 3, 4)], [Tetrahedron]) mesh = SimpleMesh(points, connec) @test_throws AssertionError("winding number only defined for surface meshes") sideof(cart(0, 0, 0), mesh) + + # sideof serial vs threads + p = first(randpoint2(1)) + r = Ring(randpoint2(500)) + serial = Meshes._sideofserial(p, r) + threads = Meshes._sideofthreads(p, r) + @test serial == threads end From 1ee56ba2db14acaa5161d99e8f4ca1bde9991988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 9 Aug 2024 08:30:17 -0300 Subject: [PATCH 261/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4d6ad9f3c..70ab0a5a7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.3" +version = "0.48.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 715f446afd61da677bceee4843593466a17ee921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 9 Aug 2024 15:21:44 -0300 Subject: [PATCH 262/423] Optimize centroid of TransformedMesh (#995) * Optimize centroid of TransformedMesh * Update test/mesh.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/mesh/transformedmesh.jl | 2 ++ test/mesh.jl | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index b8a619f7c..5d9d013d8 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -33,6 +33,8 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) +centroid(m::TransformedMesh, ind::Int) = m.transform(centroid(m.mesh, ind)) + # alias to improve readability in IO methods const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid,TR<:Transform} = TransformedMesh{M,C,GridTopology{Dim},G,TR} diff --git a/test/mesh.jl b/test/mesh.jl index c41b7c60f..c8c8da7bd 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -759,6 +759,13 @@ @test minimum(sub) == cart(0, 2) @test maximum(sub) == cart(10, 7) + # optimization of centroid + trans = Rotate(T(π / 4)) + cgrid = cartgrid(10, 10) + tmesh = Meshes.TransformedMesh(cgrid, trans) + centr = centroid(tmesh, 1) + @test @allocated(centroid(tmesh, 1)) < 50 + @test sprint(show, tgrid) == "10×10 TransformedGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), tgrid) == """ From f2d4f7011cbf8f387acc6365d2482a385b142abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 9 Aug 2024 15:22:09 -0300 Subject: [PATCH 263/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 70ab0a5a7..cbbd21edc 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.4" +version = "0.48.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f4591243c568104181e176872109b4e9bf635629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 10 Aug 2024 08:22:13 -0300 Subject: [PATCH 264/423] Refactor viz of transf grid (#997) --- ext/grid/transformed.jl | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 9da161255..507abfa44 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -2,29 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -isoptimized(::Type, ::TB.Identity) = true -isoptimized(CRS::Type, t::TB.SequentialTransform) = all(tᵢ -> isoptimized(CRS, tᵢ), t) - -isoptimized(::Type, ::GeometricTransform) = false - -isoptimized(::Type{<:Cartesian2D}, ::Proj{<:Projected}) = true -isoptimized(::Type{<:Projected}, ::Proj{<:Cartesian2D}) = true - -isoptimized(::Type, ::Rotate{<:Angle2d}) = true -isoptimized(::Type, ::Translate) = true -isoptimized(::Type, ::Scale) = true -function isoptimized(::Type, t::Affine{2}) - A, _ = TB.parameters(t) - isdiag(A) || isrotation(A) -end - -vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{2}, edim::Val{2}) = - viztransfgrid!(plot, M, pdim, edim) - -vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val{3}, edim::Val{3}) = - viztransfgrid!(plot, M, pdim, edim) - -function viztransfgrid!(plot, M, pdim, edim) +function vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val, edim::Val) tgrid = plot[:object] grid = Makie.@lift parent($tgrid) trans = Makie.@lift Meshes.transform($tgrid) @@ -42,6 +20,22 @@ function viztransfgrid!(plot, M, pdim, edim) end end +isoptimized(::Type, ::TB.Identity) = true +isoptimized(CRS::Type, t::TB.SequentialTransform) = all(tᵢ -> isoptimized(CRS, tᵢ), t) + +isoptimized(::Type, ::GeometricTransform) = false + +isoptimized(::Type{<:Cartesian2D}, ::Proj{<:Projected}) = true +isoptimized(::Type{<:Projected}, ::Proj{<:Cartesian2D}) = true + +isoptimized(::Type, ::Rotate{<:Angle2d}) = true +isoptimized(::Type, ::Translate) = true +isoptimized(::Type, ::Scale) = true +function isoptimized(::Type, t::Affine{2}) + A, _ = TB.parameters(t) + isdiag(A) || isrotation(A) +end + makietransform!(plot, trans::Makie.Observable{<:TB.Identity}) = nothing makietransform!(plot, trans::Makie.Observable{<:Proj}) = nothing From 2afe69dde29d98511d5db515257e2dc381271155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 12 Aug 2024 10:18:22 -0300 Subject: [PATCH 265/423] Optimize == for TranformedMesh (#998) --- src/mesh/cartesiangrid.jl | 8 ++++---- src/mesh/transformedmesh.jl | 2 ++ test/mesh.jl | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/mesh/cartesiangrid.jl b/src/mesh/cartesiangrid.jl index f38456276..a882cdf0e 100644 --- a/src/mesh/cartesiangrid.jl +++ b/src/mesh/cartesiangrid.jl @@ -162,10 +162,10 @@ function Base.getindex(g::CartesianGrid, I::CartesianIndices) CartesianGrid(dims, g.origin, g.spacing, offset) end -==(g1::CartesianGrid, g2::CartesianGrid) = - g1.topology == g2.topology && - g1.spacing == g2.spacing && - Tuple(g1.origin - g2.origin) == (g1.offset .- g2.offset) .* g1.spacing +==(g₁::CartesianGrid, g₂::CartesianGrid) = + g₁.topology == g₂.topology && + g₁.spacing == g₂.spacing && + Tuple(g₁.origin - g₂.origin) == (g₁.offset .- g₂.offset) .* g₁.spacing # ----------- # IO METHODS diff --git a/src/mesh/transformedmesh.jl b/src/mesh/transformedmesh.jl index 5d9d013d8..d8cd80b0d 100644 --- a/src/mesh/transformedmesh.jl +++ b/src/mesh/transformedmesh.jl @@ -35,6 +35,8 @@ vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) centroid(m::TransformedMesh, ind::Int) = m.transform(centroid(m.mesh, ind)) +==(m₁::TransformedMesh, m₂::TransformedMesh) = m₁.mesh == m₂.mesh && m₁.transform == m₂.transform + # alias to improve readability in IO methods const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid,TR<:Transform} = TransformedMesh{M,C,GridTopology{Dim},G,TR} diff --git a/test/mesh.jl b/test/mesh.jl index c8c8da7bd..a32d2b2d8 100644 --- a/test/mesh.jl +++ b/test/mesh.jl @@ -766,6 +766,12 @@ centr = centroid(tmesh, 1) @test @allocated(centroid(tmesh, 1)) < 50 + # optimization of == + trans = Rotate(T(π / 4)) + cgrid = cartgrid(1000, 1000) + tmesh = Meshes.TransformedMesh(cgrid, trans) + @test tmesh == tmesh + @test sprint(show, tgrid) == "10×10 TransformedGrid" if T == Float32 @test sprint(show, MIME"text/plain"(), tgrid) == """ From 50d918011c663c16361f46340e30f50150cd587b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 12 Aug 2024 10:18:37 -0300 Subject: [PATCH 266/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cbbd21edc..28377fb52 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.5" +version = "0.48.6" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From bdfeacc4e0e512631f5f6f9168297b68eb4b1e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 13 Aug 2024 08:03:21 -0300 Subject: [PATCH 267/423] Organize geometry/domain files (#1000) --- src/domains.jl | 8 ++++---- src/{mesh.jl => domains/meshes.jl} | 10 +++++----- src/{mesh => domains/meshes}/cartesiangrid.jl | 0 src/{mesh => domains/meshes}/rectilineargrid.jl | 0 src/{mesh => domains/meshes}/simplemesh.jl | 0 src/{mesh => domains/meshes}/structuredgrid.jl | 0 src/{mesh => domains/meshes}/transformedmesh.jl | 0 src/{ => domains}/sets.jl | 0 src/{ => domains}/subdomains.jl | 0 src/{ => domains}/trajecs.jl | 0 src/geometries.jl | 6 +++--- src/{ => geometries}/multigeoms.jl | 0 src/{ => geometries}/polytopes.jl | 0 src/{ => geometries}/polytopes/hexahedron.jl | 0 src/{ => geometries}/polytopes/ngon.jl | 0 src/{ => geometries}/polytopes/polyarea.jl | 0 src/{ => geometries}/polytopes/pyramid.jl | 0 src/{ => geometries}/polytopes/ring.jl | 0 src/{ => geometries}/polytopes/rope.jl | 0 src/{ => geometries}/polytopes/segment.jl | 0 src/{ => geometries}/polytopes/tetrahedron.jl | 0 src/{ => geometries}/polytopes/wedge.jl | 0 src/{ => geometries}/primitives.jl | 0 src/{ => geometries}/primitives/ball.jl | 0 src/{ => geometries}/primitives/bezier.jl | 0 src/{ => geometries}/primitives/box.jl | 0 src/{ => geometries}/primitives/circle.jl | 0 src/{ => geometries}/primitives/cone.jl | 0 src/{ => geometries}/primitives/conesurface.jl | 0 src/{ => geometries}/primitives/cylinder.jl | 0 src/{ => geometries}/primitives/cylindersurface.jl | 0 src/{ => geometries}/primitives/disk.jl | 0 src/{ => geometries}/primitives/ellipsoid.jl | 0 src/{ => geometries}/primitives/frustum.jl | 0 src/{ => geometries}/primitives/frustumsurface.jl | 0 src/{ => geometries}/primitives/line.jl | 0 src/{ => geometries}/primitives/paraboloidsurface.jl | 0 src/{ => geometries}/primitives/plane.jl | 0 src/{ => geometries}/primitives/point.jl | 0 src/{ => geometries}/primitives/ray.jl | 0 src/{ => geometries}/primitives/sphere.jl | 0 src/{ => geometries}/primitives/torus.jl | 0 test/{mesh.jl => meshes.jl} | 0 test/runtests.jl | 2 +- 44 files changed, 13 insertions(+), 13 deletions(-) rename src/{mesh.jl => domains/meshes.jl} (97%) rename src/{mesh => domains/meshes}/cartesiangrid.jl (100%) rename src/{mesh => domains/meshes}/rectilineargrid.jl (100%) rename src/{mesh => domains/meshes}/simplemesh.jl (100%) rename src/{mesh => domains/meshes}/structuredgrid.jl (100%) rename src/{mesh => domains/meshes}/transformedmesh.jl (100%) rename src/{ => domains}/sets.jl (100%) rename src/{ => domains}/subdomains.jl (100%) rename src/{ => domains}/trajecs.jl (100%) rename src/{ => geometries}/multigeoms.jl (100%) rename src/{ => geometries}/polytopes.jl (100%) rename src/{ => geometries}/polytopes/hexahedron.jl (100%) rename src/{ => geometries}/polytopes/ngon.jl (100%) rename src/{ => geometries}/polytopes/polyarea.jl (100%) rename src/{ => geometries}/polytopes/pyramid.jl (100%) rename src/{ => geometries}/polytopes/ring.jl (100%) rename src/{ => geometries}/polytopes/rope.jl (100%) rename src/{ => geometries}/polytopes/segment.jl (100%) rename src/{ => geometries}/polytopes/tetrahedron.jl (100%) rename src/{ => geometries}/polytopes/wedge.jl (100%) rename src/{ => geometries}/primitives.jl (100%) rename src/{ => geometries}/primitives/ball.jl (100%) rename src/{ => geometries}/primitives/bezier.jl (100%) rename src/{ => geometries}/primitives/box.jl (100%) rename src/{ => geometries}/primitives/circle.jl (100%) rename src/{ => geometries}/primitives/cone.jl (100%) rename src/{ => geometries}/primitives/conesurface.jl (100%) rename src/{ => geometries}/primitives/cylinder.jl (100%) rename src/{ => geometries}/primitives/cylindersurface.jl (100%) rename src/{ => geometries}/primitives/disk.jl (100%) rename src/{ => geometries}/primitives/ellipsoid.jl (100%) rename src/{ => geometries}/primitives/frustum.jl (100%) rename src/{ => geometries}/primitives/frustumsurface.jl (100%) rename src/{ => geometries}/primitives/line.jl (100%) rename src/{ => geometries}/primitives/paraboloidsurface.jl (100%) rename src/{ => geometries}/primitives/plane.jl (100%) rename src/{ => geometries}/primitives/point.jl (100%) rename src/{ => geometries}/primitives/ray.jl (100%) rename src/{ => geometries}/primitives/sphere.jl (100%) rename src/{ => geometries}/primitives/torus.jl (100%) rename test/{mesh.jl => meshes.jl} (100%) diff --git a/src/domains.jl b/src/domains.jl index 20ce6fc3e..d203852a8 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -157,10 +157,10 @@ end # IMPLEMENTATIONS # ---------------- -include("sets.jl") -include("mesh.jl") -include("trajecs.jl") -include("subdomains.jl") +include("domains/sets.jl") +include("domains/meshes.jl") +include("domains/trajecs.jl") +include("domains/subdomains.jl") # ------------ # CONVERSIONS diff --git a/src/mesh.jl b/src/domains/meshes.jl similarity index 97% rename from src/mesh.jl rename to src/domains/meshes.jl index 7b3df46c3..1b2f4b993 100644 --- a/src/mesh.jl +++ b/src/domains/meshes.jl @@ -233,8 +233,8 @@ end # IMPLEMENTATIONS # ---------------- -include("mesh/cartesiangrid.jl") -include("mesh/rectilineargrid.jl") -include("mesh/structuredgrid.jl") -include("mesh/simplemesh.jl") -include("mesh/transformedmesh.jl") +include("meshes/cartesiangrid.jl") +include("meshes/rectilineargrid.jl") +include("meshes/structuredgrid.jl") +include("meshes/simplemesh.jl") +include("meshes/transformedmesh.jl") diff --git a/src/mesh/cartesiangrid.jl b/src/domains/meshes/cartesiangrid.jl similarity index 100% rename from src/mesh/cartesiangrid.jl rename to src/domains/meshes/cartesiangrid.jl diff --git a/src/mesh/rectilineargrid.jl b/src/domains/meshes/rectilineargrid.jl similarity index 100% rename from src/mesh/rectilineargrid.jl rename to src/domains/meshes/rectilineargrid.jl diff --git a/src/mesh/simplemesh.jl b/src/domains/meshes/simplemesh.jl similarity index 100% rename from src/mesh/simplemesh.jl rename to src/domains/meshes/simplemesh.jl diff --git a/src/mesh/structuredgrid.jl b/src/domains/meshes/structuredgrid.jl similarity index 100% rename from src/mesh/structuredgrid.jl rename to src/domains/meshes/structuredgrid.jl diff --git a/src/mesh/transformedmesh.jl b/src/domains/meshes/transformedmesh.jl similarity index 100% rename from src/mesh/transformedmesh.jl rename to src/domains/meshes/transformedmesh.jl diff --git a/src/sets.jl b/src/domains/sets.jl similarity index 100% rename from src/sets.jl rename to src/domains/sets.jl diff --git a/src/subdomains.jl b/src/domains/subdomains.jl similarity index 100% rename from src/subdomains.jl rename to src/domains/subdomains.jl diff --git a/src/trajecs.jl b/src/domains/trajecs.jl similarity index 100% rename from src/trajecs.jl rename to src/domains/trajecs.jl diff --git a/src/geometries.jl b/src/geometries.jl index 30c710fb2..baa6d6fdb 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -79,9 +79,9 @@ Base.summary(io::IO, geom::Geometry) = print(io, prettyname(geom)) # IMPLEMENTATIONS # ---------------- -include("primitives.jl") -include("polytopes.jl") -include("multigeoms.jl") +include("geometries/primitives.jl") +include("geometries/polytopes.jl") +include("geometries/multigeoms.jl") # ------------ # CONVERSIONS diff --git a/src/multigeoms.jl b/src/geometries/multigeoms.jl similarity index 100% rename from src/multigeoms.jl rename to src/geometries/multigeoms.jl diff --git a/src/polytopes.jl b/src/geometries/polytopes.jl similarity index 100% rename from src/polytopes.jl rename to src/geometries/polytopes.jl diff --git a/src/polytopes/hexahedron.jl b/src/geometries/polytopes/hexahedron.jl similarity index 100% rename from src/polytopes/hexahedron.jl rename to src/geometries/polytopes/hexahedron.jl diff --git a/src/polytopes/ngon.jl b/src/geometries/polytopes/ngon.jl similarity index 100% rename from src/polytopes/ngon.jl rename to src/geometries/polytopes/ngon.jl diff --git a/src/polytopes/polyarea.jl b/src/geometries/polytopes/polyarea.jl similarity index 100% rename from src/polytopes/polyarea.jl rename to src/geometries/polytopes/polyarea.jl diff --git a/src/polytopes/pyramid.jl b/src/geometries/polytopes/pyramid.jl similarity index 100% rename from src/polytopes/pyramid.jl rename to src/geometries/polytopes/pyramid.jl diff --git a/src/polytopes/ring.jl b/src/geometries/polytopes/ring.jl similarity index 100% rename from src/polytopes/ring.jl rename to src/geometries/polytopes/ring.jl diff --git a/src/polytopes/rope.jl b/src/geometries/polytopes/rope.jl similarity index 100% rename from src/polytopes/rope.jl rename to src/geometries/polytopes/rope.jl diff --git a/src/polytopes/segment.jl b/src/geometries/polytopes/segment.jl similarity index 100% rename from src/polytopes/segment.jl rename to src/geometries/polytopes/segment.jl diff --git a/src/polytopes/tetrahedron.jl b/src/geometries/polytopes/tetrahedron.jl similarity index 100% rename from src/polytopes/tetrahedron.jl rename to src/geometries/polytopes/tetrahedron.jl diff --git a/src/polytopes/wedge.jl b/src/geometries/polytopes/wedge.jl similarity index 100% rename from src/polytopes/wedge.jl rename to src/geometries/polytopes/wedge.jl diff --git a/src/primitives.jl b/src/geometries/primitives.jl similarity index 100% rename from src/primitives.jl rename to src/geometries/primitives.jl diff --git a/src/primitives/ball.jl b/src/geometries/primitives/ball.jl similarity index 100% rename from src/primitives/ball.jl rename to src/geometries/primitives/ball.jl diff --git a/src/primitives/bezier.jl b/src/geometries/primitives/bezier.jl similarity index 100% rename from src/primitives/bezier.jl rename to src/geometries/primitives/bezier.jl diff --git a/src/primitives/box.jl b/src/geometries/primitives/box.jl similarity index 100% rename from src/primitives/box.jl rename to src/geometries/primitives/box.jl diff --git a/src/primitives/circle.jl b/src/geometries/primitives/circle.jl similarity index 100% rename from src/primitives/circle.jl rename to src/geometries/primitives/circle.jl diff --git a/src/primitives/cone.jl b/src/geometries/primitives/cone.jl similarity index 100% rename from src/primitives/cone.jl rename to src/geometries/primitives/cone.jl diff --git a/src/primitives/conesurface.jl b/src/geometries/primitives/conesurface.jl similarity index 100% rename from src/primitives/conesurface.jl rename to src/geometries/primitives/conesurface.jl diff --git a/src/primitives/cylinder.jl b/src/geometries/primitives/cylinder.jl similarity index 100% rename from src/primitives/cylinder.jl rename to src/geometries/primitives/cylinder.jl diff --git a/src/primitives/cylindersurface.jl b/src/geometries/primitives/cylindersurface.jl similarity index 100% rename from src/primitives/cylindersurface.jl rename to src/geometries/primitives/cylindersurface.jl diff --git a/src/primitives/disk.jl b/src/geometries/primitives/disk.jl similarity index 100% rename from src/primitives/disk.jl rename to src/geometries/primitives/disk.jl diff --git a/src/primitives/ellipsoid.jl b/src/geometries/primitives/ellipsoid.jl similarity index 100% rename from src/primitives/ellipsoid.jl rename to src/geometries/primitives/ellipsoid.jl diff --git a/src/primitives/frustum.jl b/src/geometries/primitives/frustum.jl similarity index 100% rename from src/primitives/frustum.jl rename to src/geometries/primitives/frustum.jl diff --git a/src/primitives/frustumsurface.jl b/src/geometries/primitives/frustumsurface.jl similarity index 100% rename from src/primitives/frustumsurface.jl rename to src/geometries/primitives/frustumsurface.jl diff --git a/src/primitives/line.jl b/src/geometries/primitives/line.jl similarity index 100% rename from src/primitives/line.jl rename to src/geometries/primitives/line.jl diff --git a/src/primitives/paraboloidsurface.jl b/src/geometries/primitives/paraboloidsurface.jl similarity index 100% rename from src/primitives/paraboloidsurface.jl rename to src/geometries/primitives/paraboloidsurface.jl diff --git a/src/primitives/plane.jl b/src/geometries/primitives/plane.jl similarity index 100% rename from src/primitives/plane.jl rename to src/geometries/primitives/plane.jl diff --git a/src/primitives/point.jl b/src/geometries/primitives/point.jl similarity index 100% rename from src/primitives/point.jl rename to src/geometries/primitives/point.jl diff --git a/src/primitives/ray.jl b/src/geometries/primitives/ray.jl similarity index 100% rename from src/primitives/ray.jl rename to src/geometries/primitives/ray.jl diff --git a/src/primitives/sphere.jl b/src/geometries/primitives/sphere.jl similarity index 100% rename from src/primitives/sphere.jl rename to src/geometries/primitives/sphere.jl diff --git a/src/primitives/torus.jl b/src/geometries/primitives/torus.jl similarity index 100% rename from src/primitives/torus.jl rename to src/geometries/primitives/torus.jl diff --git a/test/mesh.jl b/test/meshes.jl similarity index 100% rename from test/mesh.jl rename to test/meshes.jl diff --git a/test/runtests.jl b/test/runtests.jl index 5c61c15d5..810ea9705 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -57,7 +57,7 @@ testfiles = [ "domains.jl", "subdomains.jl", "sets.jl", - "mesh.jl", + "meshes.jl", "trajecs.jl", "crs.jl", "utils.jl", From 052ccd5e9a5f85ad42ed910861b86100859a05ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 13 Aug 2024 09:13:01 -0300 Subject: [PATCH 268/423] Fix paramdim of primitives over ellipsoid (#1001) --- src/geometries/primitives/ball.jl | 4 +++- src/geometries/primitives/box.jl | 4 +++- src/geometries/primitives/sphere.jl | 4 +++- test/primitives.jl | 12 ++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/geometries/primitives/ball.jl b/src/geometries/primitives/ball.jl index f6b79075b..09eec61f4 100644 --- a/src/geometries/primitives/ball.jl +++ b/src/geometries/primitives/ball.jl @@ -23,7 +23,9 @@ Ball(center::Point) = Ball(center, oneunit(lentype(center))) Ball(center::Tuple) = Ball(Point(center)) -paramdim(B::Type{<:Ball}) = embeddim(B) +paramdim(::Type{<:Ball{𝔼{Dim}}}) where {Dim} = Dim + +paramdim(::Type{<:Ball{🌐}}) = 2 center(b::Ball) = b.center diff --git a/src/geometries/primitives/box.jl b/src/geometries/primitives/box.jl index 8cc04ade7..2b0a973be 100644 --- a/src/geometries/primitives/box.jl +++ b/src/geometries/primitives/box.jl @@ -41,7 +41,9 @@ Box(min::Point{M,C}, max::Point{M,C}) where {M<:Manifold,C<:CRS} = Box{M,C}(min, Box(min::Tuple, max::Tuple) = Box(Point(min), Point(max)) -paramdim(B::Type{<:Box}) = embeddim(B) +paramdim(::Type{<:Box{𝔼{Dim}}}) where {Dim} = Dim + +paramdim(::Type{<:Box{🌐}}) = 2 Base.minimum(b::Box) = b.min diff --git a/src/geometries/primitives/sphere.jl b/src/geometries/primitives/sphere.jl index d4f2b9062..c643822e7 100644 --- a/src/geometries/primitives/sphere.jl +++ b/src/geometries/primitives/sphere.jl @@ -60,7 +60,9 @@ end Sphere(p1::Tuple, p2::Tuple, p3::Tuple, p4::Tuple) = Sphere(Point(p1), Point(p2), Point(p3), Point(p4)) -paramdim(S::Type{<:Sphere}) = embeddim(S) - 1 +paramdim(::Type{<:Sphere{𝔼{Dim}}}) where {Dim} = Dim - 1 + +paramdim(::Type{<:Sphere{🌐}}) = 1 center(s::Sphere) = s.center diff --git a/test/primitives.jl b/test/primitives.jl index 2a1e32a76..67b0f9154 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -399,6 +399,10 @@ @test maximum(b) == cart(1, 1, 1) @test extrema(b) == (cart(0, 0, 0), cart(1, 1, 1)) + b = Box(latlon(0, 0), latlon(45, 90)) + @test embeddim(b) == 3 + @test paramdim(b) == 2 + b = Box(cart(0, 0), cart(1, 1)) equaltest(b) isapproxtest(b) @@ -512,6 +516,10 @@ @test Meshes.center(b) == cart(1, 2, 3) @test radius(b) == T(5) * u"m" + b = Ball(latlon(0, 0), T(5)) + @test embeddim(b) == 3 + @test paramdim(b) == 2 + b = Ball(cart(0, 0), T(1)) equaltest(b) isapproxtest(b) @@ -589,6 +597,10 @@ @test isnothing(boundary(s)) @test perimeter(s) == zero(ℳ) + s = Sphere(latlon(0, 0), T(1)) + @test embeddim(s) == 3 + @test paramdim(s) == 1 + s = Sphere(cart(0, 0), T(1)) equaltest(s) isapproxtest(s) From d55f05d2fc05d11aa09504b195b215a3f428ed13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 13 Aug 2024 16:22:55 -0300 Subject: [PATCH 269/423] Add TransformedGeometry (#1002) * Add TransformedGeometry * More adjustments * Organize code * Update vertex * Add functor implementation * More adjustments * Organize Multi code * Update code * More adjustments * More adjustments * Fix code * Add tests * Apply suggestions from code review * Apply suggestions * Apply suggestions * Add more tests * Fix code * Fix code --------- Co-authored-by: Elias Carvalho --- src/discretization.jl | 20 ++++- src/domains/meshes/transformedmesh.jl | 2 +- src/geometries.jl | 3 +- .../{multigeoms.jl => multigeom.jl} | 45 ++++++---- src/geometries/transformedgeom.jl | 82 +++++++++++++++++++ test/runtests.jl | 1 + test/transformedgeoms.jl | 57 +++++++++++++ 7 files changed, 188 insertions(+), 22 deletions(-) rename src/geometries/{multigeoms.jl => multigeom.jl} (82%) create mode 100644 src/geometries/transformedgeom.jl create mode 100644 test/transformedgeoms.jl diff --git a/src/discretization.jl b/src/discretization.jl index ebf0fbe7d..14124726f 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -68,13 +68,28 @@ discretize(parsurf::ParaboloidSurface) = discretize(parsurf, RegularDiscretizati discretize(multi::Multi) = mapreduce(discretize, merge, parent(multi)) +discretize(tgeom::TransformedGeometry) = transform(tgeom)(discretize(parent(tgeom))) + discretize(mesh::Mesh) = mesh +# ---------- +# FALLBACKS +# ---------- + +discretize(multi::Multi, method::DiscretizationMethod) = + mapreduce(geom -> discretize(geom, method), merge, parent(multi)) + +discretize(tgeom::TransformedGeometry, method::DiscretizationMethod) = + transform(tgeom)(discretize(parent(tgeom), method)) + # ----------------- # BOUNDARY METHODS # ----------------- -discretize(geometry::Geometry, method::BoundaryTriangulationMethod) = discretizewithin(boundary(geometry), method) +discretize(geometry, method::BoundaryTriangulationMethod) = discretizewithin(boundary(geometry), method) + +discretize(multi::Multi, method::BoundaryTriangulationMethod) = + mapreduce(geom -> discretize(geom, method), merge, parent(multi)) function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) # clean up polygon if necessary @@ -129,9 +144,6 @@ function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) end end -discretize(multi::Multi, method::BoundaryTriangulationMethod) = - mapreduce(geom -> discretize(geom, method), merge, parent(multi)) - function discretizewithin(ring::Ring, method::BoundaryTriangulationMethod) # collect vertices to get rid of static containers points = collect(vertices(ring)) diff --git a/src/domains/meshes/transformedmesh.jl b/src/domains/meshes/transformedmesh.jl index d8cd80b0d..2c959b8bd 100644 --- a/src/domains/meshes/transformedmesh.jl +++ b/src/domains/meshes/transformedmesh.jl @@ -35,7 +35,7 @@ vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) centroid(m::TransformedMesh, ind::Int) = m.transform(centroid(m.mesh, ind)) -==(m₁::TransformedMesh, m₂::TransformedMesh) = m₁.mesh == m₂.mesh && m₁.transform == m₂.transform +==(m₁::TransformedMesh, m₂::TransformedMesh) = m₁.transform == m₂.transform && m₁.mesh == m₂.mesh # alias to improve readability in IO methods const TransformedGrid{M<:Manifold,C<:CRS,Dim,G<:Grid,TR<:Transform} = TransformedMesh{M,C,GridTopology{Dim},G,TR} diff --git a/src/geometries.jl b/src/geometries.jl index baa6d6fdb..6bf812717 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -81,7 +81,8 @@ Base.summary(io::IO, geom::Geometry) = print(io, prettyname(geom)) include("geometries/primitives.jl") include("geometries/polytopes.jl") -include("geometries/multigeoms.jl") +include("geometries/multigeom.jl") +include("geometries/transformedgeom.jl") # ------------ # CONVERSIONS diff --git a/src/geometries/multigeoms.jl b/src/geometries/multigeom.jl similarity index 82% rename from src/geometries/multigeoms.jl rename to src/geometries/multigeom.jl index 1f4ee0d0b..4139685a8 100644 --- a/src/geometries/multigeoms.jl +++ b/src/geometries/multigeom.jl @@ -29,36 +29,49 @@ const MultiRope{M<:Manifold,C<:CRS} = Multi{M,C,<:Rope{M,C}} const MultiRing{M<:Manifold,C<:CRS} = Multi{M,C,<:Ring{M,C}} const MultiPolygon{M<:Manifold,C<:CRS} = Multi{M,C,<:Polygon{M,C}} const MultiPolyhedron{M<:Manifold,C<:CRS} = Multi{M,C,<:Polyhedron{M,C}} +const MultiPolytope{K,M<:Manifold,C<:CRS} = Multi{M,C,<:Polytope{K,M,C}} -paramdim(m::Multi) = maximum(paramdim, m.geoms) - -vertex(m::Multi, ind) = vertices(m)[ind] - -vertices(m::Multi) = [vertex for geom in m.geoms for vertex in vertices(geom)] - -nvertices(m::Multi) = sum(nvertices, m.geoms) +Base.parent(m::Multi) = m.geoms -Base.unique(m::Multi) = unique!(deepcopy(m)) +# --------- +# GEOMETRY +# --------- -function Base.unique!(m::Multi) - foreach(unique!, m.geoms) - m -end +paramdim(m::Multi) = maximum(paramdim, m.geoms) function centroid(m::Multi) cs = to.(centroid.(m.geoms)) withcrs(m, sum(cs) / length(cs)) end -rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] - -Base.parent(m::Multi) = m.geoms - ==(m₁::Multi, m₂::Multi) = m₁.geoms == m₂.geoms Base.isapprox(m₁::Multi, m₂::Multi; atol=atol(lentype(m₁)), kwargs...) = length(m₁.geoms) == length(m₂.geoms) && all(isapprox(g₁, g₂; atol, kwargs...) for (g₁, g₂) in zip(m₁.geoms, m₂.geoms)) +# --------- +# POLYTOPE +# --------- + +vertex(m::MultiPolytope, ind) = vertices(m)[ind] + +vertices(m::MultiPolytope) = [vertex for geom in m.geoms for vertex in vertices(geom)] + +nvertices(m::MultiPolytope) = sum(nvertices, m.geoms) + +Base.unique(m::MultiPolytope) = unique!(deepcopy(m)) + +function Base.unique!(m::MultiPolytope) + foreach(unique!, m.geoms) + m +end + +# -------- +# POLYGON +# -------- + +rings(m::MultiPolygon) = [ring for poly in m.geoms for ring in rings(poly)] + # ----------- # IO METHODS # ----------- diff --git a/src/geometries/transformedgeom.jl b/src/geometries/transformedgeom.jl new file mode 100644 index 000000000..58c9373d7 --- /dev/null +++ b/src/geometries/transformedgeom.jl @@ -0,0 +1,82 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + TransformedGeometry(geometry, transform) + +Lazy representation of a geometric `transform` applied to a `geometry`. +""" +struct TransformedGeometry{M<:Manifold,C<:CRS,G<:Geometry,T<:Transform} <: Geometry{M,C} + geometry::G + transform::T + + function TransformedGeometry{M,C}(geometry::G, transform::T) where {M<:Manifold,C<:CRS,G<:Geometry,T<:Transform} + new{M,C,G,T}(geometry, transform) + end +end + +function TransformedGeometry(g::Geometry, t::Transform) + p = t(centroid(g)) + TransformedGeometry{manifold(p),crs(p)}(g, t) +end + +# specialize constructor to avoid deep structures +TransformedGeometry(g::TransformedGeometry, t::Transform) = TransformedGeometry(g.geometry, g.transform → t) + +# type aliases for convenience +const TransformedPoint{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Point,T} +const TransformedSegment{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Segment,T} +const TransformedRope{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Rope,T} +const TransformedRing{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Ring,T} +const TransformedPolygon{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Polygon,T} +const TransformedPolyhedron{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Polyhedron,T} +const TransformedPolytope{M<:Manifold,C<:CRS,T<:Transform} = TransformedGeometry{M,C,<:Polytope,T} + +Base.parent(g::TransformedGeometry) = g.geometry + +transform(g::TransformedGeometry) = g.transform + +# --------- +# GEOMETRY +# --------- + +paramdim(g::TransformedGeometry) = paramdim(g.geometry) + +centroid(g::TransformedGeometry) = g.transform(centroid(g.geometry)) + +==(g₁::TransformedGeometry, g₂::TransformedGeometry) = g₁.transform == g₂.transform && g₁.geometry == g₂.geometry + +Base.isapprox(g₁::TransformedGeometry, g₂::TransformedGeometry; atol=atol(lentype(g₁)), kwargs...) = + isapprox(g₁.geometry, g₂.geometry; atol, kwargs...) && g₁.transform == g₂.transform + +(g::TransformedGeometry)(uvw...) = g.transform(g.geometry(uvw...)) + +# --------- +# POLYTOPE +# --------- + +vertex(p::TransformedPolytope, ind) = p.transform(vertex(p.geometry, ind)) + +vertices(p::TransformedPolytope) = map(p.transform, vertices(p.geometry)) + +nvertices(p::TransformedPolytope) = nvertices(p.geometry) + +Base.unique(p::TransformedPolytope) = unique!(deepcopy(p)) + +Base.unique!(p::TransformedPolytope) = (unique!(p.geometry); p) + +# -------- +# POLYGON +# -------- + +rings(p::TransformedPolygon) = map(p.transform, rings(p.geometry)) + +# ----------- +# IO METHODS +# ----------- + +function Base.summary(io::IO, g::TransformedGeometry) + name = prettyname(g.geometry) + print(io, "Transformed$name") +end diff --git a/test/runtests.jl b/test/runtests.jl index 810ea9705..2d222e0e2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -51,6 +51,7 @@ testfiles = [ "primitives.jl", "polytopes.jl", "multigeoms.jl", + "transformedgeoms.jl", "connectivities.jl", "topologies.jl", "toporelations.jl", diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl new file mode 100644 index 000000000..34cc46eef --- /dev/null +++ b/test/transformedgeoms.jl @@ -0,0 +1,57 @@ +@testset "TransformedGeometry" begin + b = Box(cart(0, 0), cart(1, 1)) + t = Translate(T(1), T(2)) + tb = Meshes.TransformedGeometry(b, t) + @test parent(tb) == b + @test Meshes.transform(tb) == t + t2 = Scale(T(2), T(3)) + tb2 = Meshes.TransformedGeometry(tb, t2) + @test Meshes.transform(tb2) == (t → t2) + @test paramdim(tb) == paramdim(b) + @test centroid(tb) == t(centroid(b)) + @test discretize(tb) == t(discretize(b)) + @test tb(T(0.5), T(0.5)) == t(b(T(0.5), T(0.5))) + t3 = Scale(T(2), T(2)) + tb3 = Meshes.TransformedGeometry(b, t3) + @test measure(tb3) == 4 * measure(b) + equaltest(tb) + isapproxtest(tb) + + b = Ball(latlon(0, 0), T(1)) + t = Proj(Cartesian) + tb = Meshes.TransformedGeometry(b, t) + @test paramdim(tb) == paramdim(b) + @test centroid(tb) == t(centroid(b)) + equaltest(tb) + isapproxtest(tb) + + s = Sphere(latlon(0, 0), T(1)) + t = Proj(Cartesian) + ts = Meshes.TransformedGeometry(s, t) + @test paramdim(ts) == paramdim(s) + @test centroid(ts) == t(centroid(s)) + equaltest(ts) + isapproxtest(ts) + + s = Segment(cart(0, 0), cart(1, 1)) + t = Translate(T(1), T(2)) + ts = Meshes.TransformedGeometry(s, t) + @test vertex(ts, 1) == t(vertex(s, 1)) + @test vertices(ts) == t.(vertices(s)) + @test nvertices(ts) == nvertices(s) + equaltest(ts) + isapproxtest(ts) + + p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + t = Translate(T(1), T(2)) + tp = Meshes.TransformedGeometry(p, t) + @test vertex(tp, 1) == t(vertex(p, 1)) + @test vertices(tp) == t.(vertices(p)) + @test nvertices(tp) == nvertices(p) + @test rings(tp) == t.(rings(p)) + p2 = PolyArea(cart(0, 0), cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + tp2 = Meshes.TransformedGeometry(p2, t) + @test unique(tp2) == tp + equaltest(tp) + isapproxtest(tp) +end From a071c39750740e1d7748e1a72b49ffd8df18477d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 13 Aug 2024 18:05:19 -0300 Subject: [PATCH 270/423] Optimize simplexify of simplicial mesh (#1003) * Optimize simplexify of simplicial mesh * Replace Tuple by ntuple * More adjustments --- src/discretization.jl | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index 14124726f..c96fd4f02 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -215,26 +215,29 @@ simplexify(poly::Polyhedron) = discretize(poly, ManualDiscretization()) simplexify(multi::Multi) = mapreduce(simplexify, merge, parent(multi)) function simplexify(mesh::Mesh) + # retrieve vertices and topology points = vertices(mesh) - elems = elements(mesh) topo = topology(mesh) - connec = elements(topo) + + # check if there is something to do + all(issimplex, elements(topo)) && return mesh # initialize vector of global indices ginds = Vector{Int}[] # simplexify each element and append global indices - for (e, c) in zip(elems, connec) - # simplexify single element - mesh′ = simplexify(e) - topo′ = topology(mesh′) - connec′ = elements(topo′) + for connec in elements(topo) + # materialize element and indices + elem = materialize(connec, points) + inds = indices(connec) - # global indices - inds = indices(c) + # simplexify element + mesh′ = simplexify(elem) + topo′ = topology(mesh′) + connecs′ = elements(topo′) # convert from local to global indices - einds = [[inds[i] for i in indices(c′)] for c′ in connec′] + einds = [[inds[i] for i in indices(c′)] for c′ in connecs′] # save global indices append!(ginds, einds) @@ -242,11 +245,12 @@ function simplexify(mesh::Mesh) # simplex type for parametric dimension PL = paramdim(mesh) == 2 ? Triangle : Tetrahedron + NV = nvertices(PL) # new connectivities - newconnec = connect.(Tuple.(ginds), PL) + newconnecs = [connect(ntuple(i -> inds[i], NV), PL) for inds in ginds] - SimpleMesh(points, newconnec) + SimpleMesh(points, newconnecs) end # ---------------- From a151711da62938bf5daffaff1ea77c29de1f04cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 13 Aug 2024 18:05:36 -0300 Subject: [PATCH 271/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 28377fb52..cf1505930 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.6" +version = "0.48.7" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From a0cc0f151bb2c4dd03bb7b1c9d7dad8952a6017c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 14 Aug 2024 09:58:09 -0300 Subject: [PATCH 272/423] Refactor centroid (#1004) --- src/Meshes.jl | 29 ++++--- src/centroid.jl | 80 +++++++++++++++++++ src/complement.jl | 2 +- src/domains.jl | 23 ------ src/domains/meshes/cartesiangrid.jl | 5 -- src/domains/meshes/rectilineargrid.jl | 7 -- src/domains/meshes/transformedmesh.jl | 2 - src/domains/sets.jl | 4 - src/domains/subdomains.jl | 6 +- src/domains/trajecs.jl | 8 +- src/geometries.jl | 7 -- src/geometries/multigeom.jl | 5 -- src/geometries/polytopes.jl | 7 -- src/geometries/polytopes/polyarea.jl | 2 - src/geometries/polytopes/segment.jl | 5 -- src/geometries/primitives/box.jl | 2 - src/geometries/primitives/cylinder.jl | 2 - src/geometries/primitives/cylindersurface.jl | 8 +- .../primitives/paraboloidsurface.jl | 10 --- src/geometries/primitives/point.jl | 2 - src/geometries/transformedgeom.jl | 2 - src/partitioning/bisectfraction.jl | 2 +- src/refinement/trisubdivision.jl | 2 +- src/transforms/repair.jl | 2 +- src/transforms/stdcoords.jl | 2 +- test/polytopes.jl | 8 +- test/primitives.jl | 31 ++++--- 27 files changed, 128 insertions(+), 137 deletions(-) create mode 100644 src/centroid.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index b7debc8df..bfc94e80a 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -98,11 +98,12 @@ include("neighborsearch.jl") include("predicates.jl") # operations +include("centroid.jl") +include("measures.jl") +include("boundary.jl") include("winding.jl") include("sideof.jl") include("orientation.jl") -include("measures.jl") -include("boundary.jl") include("merging.jl") include("clipping.jl") include("clamping.jl") @@ -148,8 +149,6 @@ export paramdim, crs, manifold, - center, - centroid, # primitives Primitive, @@ -179,6 +178,7 @@ export DeCasteljau, coords, to, + center, radius, radii, plane, @@ -396,6 +396,18 @@ export ⪯, ⪰, + # centroids + centroid, + + # measures + measure, + area, + volume, + perimeter, + + # boundary + boundary, + # winding number winding, @@ -414,15 +426,6 @@ export CW, CCW, - # measures - measure, - area, - volume, - perimeter, - - # boundary - boundary, - # clipping ClippingMethod, SutherlandHodgman, diff --git a/src/centroid.jl b/src/centroid.jl new file mode 100644 index 000000000..340b17743 --- /dev/null +++ b/src/centroid.jl @@ -0,0 +1,80 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + centroid(geometry) + +The centroid of the `geometry`. +""" +centroid(g::Geometry) = center(g) # some geometries have a natural center + +centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / nvertices(p)) + +centroid(p::Point) = p + +centroid(p::Polygon) = centroid(first(rings(p))) + +centroid(b::Box) = withcrs(b, sum(to, extrema(b)) / 2) + +centroid(p::Plane) = p(0, 0) + +centroid(c::Cylinder) = centroid(boundary(c)) + +function centroid(c::CylinderSurface) + a = centroid(bottom(c)) + b = centroid(top(c)) + withcrs(c, (to(a) + to(b)) / 2) +end + +function centroid(p::ParaboloidSurface) + c = apex(p) + r = radius(p) + f = focallength(p) + z = r^2 / 4f + x = zero(z) + y = zero(z) + c + Vec(x, y, z / 2) +end + +centroid(m::Multi) = centroid(GeometrySet(parent(m))) + +centroid(g::TransformedGeometry) = transform(g)(centroid(parent(g))) + +""" + centroid(domain) + +The centroid of the `domain`. +""" +function centroid(d::Domain) + vector(i) = to(centroid(d, i)) + volume(i) = measure(element(d, i)) + n = nelements(d) + x = vector.(1:n) + w = volume.(1:n) + all(iszero, w) && (w = ones(eltype(w), n)) + withcrs(d, sum(w .* x) / sum(w)) +end + +""" + centroid(domain, ind) + +The centroid of the `ind`-th element of the `domain`. +""" +centroid(d::Domain, ind::Int) = centroid(d[ind]) + +centroid(d::SubDomain, ind::Int) = centroid(parent(d), parentindices(d)[ind]) + +function centroid(g::CartesianGrid, ind::Int) + ijk = elem2cart(topology(g), ind) + vertex(g, ijk) + Vec(spacing(g) ./ 2) +end + +function centroid(g::RectilinearGrid, ind::Int) + ijk = elem2cart(topology(g), ind) + p1 = vertex(g, ijk) + p2 = vertex(g, ijk .+ 1) + withcrs(g, (to(p1) + to(p2)) / 2) +end + +centroid(m::TransformedMesh, ind::Int) = transform(m)(centroid(parent(m), ind)) diff --git a/src/complement.jl b/src/complement.jl index c8e692622..17604712e 100644 --- a/src/complement.jl +++ b/src/complement.jl @@ -13,7 +13,7 @@ respect to its bounding box. function _boxboundary(g) ℒ = lentype(g) b = boundingbox(g) - c = to(center(b)) + c = to(centroid(b)) l = sides(b) α = (l .+ 2atol(ℒ)) ./ l t = Translate(-c...) → Scale(α) → Translate(c...) diff --git a/src/domains.jl b/src/domains.jl index d203852a8..3ae0efcd9 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -97,29 +97,6 @@ Return the length type of the `domain`. lentype(::Type{<:Domain{M,CRS}}) where {M,CRS} = lentype(CRS) lentype(d::Domain) = lentype(typeof(d)) -""" - centroid(domain, ind) - -Return the centroid of the `ind`-th element in the `domain`. -""" -centroid(d::Domain, ind::Int) = centroid(d[ind]) - -""" - centroid(domain) - -Return the centroid of the `domain`, i.e. the centroid of all -its element's centroids. -""" -function centroid(d::Domain) - vector(i) = to(centroid(d, i)) - volume(i) = measure(element(d, i)) - n = nelements(d) - x = vector.(1:n) - w = volume.(1:n) - all(iszero, w) && (w = ones(eltype(w), n)) - withcrs(d, sum(w .* x) / sum(w)) -end - """ extrema(domain) diff --git a/src/domains/meshes/cartesiangrid.jl b/src/domains/meshes/cartesiangrid.jl index a882cdf0e..daaf968ec 100644 --- a/src/domains/meshes/cartesiangrid.jl +++ b/src/domains/meshes/cartesiangrid.jl @@ -150,11 +150,6 @@ end XYZ(g::CartesianGrid) = XYZ(xyz(g)) -function centroid(g::CartesianGrid, ind::Int) - ijk = elem2cart(topology(g), ind) - vertex(g, ijk) + Vec(spacing(g) ./ 2) -end - function Base.getindex(g::CartesianGrid, I::CartesianIndices) @boundscheck _checkbounds(g, I) dims = size(I) diff --git a/src/domains/meshes/rectilineargrid.jl b/src/domains/meshes/rectilineargrid.jl index d8cb57c3f..d5e55d22c 100644 --- a/src/domains/meshes/rectilineargrid.jl +++ b/src/domains/meshes/rectilineargrid.jl @@ -53,13 +53,6 @@ xyz(g::RectilinearGrid) = g.xyz XYZ(g::RectilinearGrid) = XYZ(xyz(g)) -function centroid(g::RectilinearGrid, ind::Int) - ijk = elem2cart(topology(g), ind) - p1 = vertex(g, ijk) - p2 = vertex(g, ijk .+ 1) - withcrs(g, (to(p1) + to(p2)) / 2) -end - function Base.getindex(g::RectilinearGrid{Datum}, I::CartesianIndices) where {Datum} @boundscheck _checkbounds(g, I) dims = size(I) diff --git a/src/domains/meshes/transformedmesh.jl b/src/domains/meshes/transformedmesh.jl index 2c959b8bd..ecfd8dcd0 100644 --- a/src/domains/meshes/transformedmesh.jl +++ b/src/domains/meshes/transformedmesh.jl @@ -33,8 +33,6 @@ topology(m::TransformedMesh) = topology(m.mesh) vertex(m::TransformedMesh, ind::Int) = m.transform(vertex(m.mesh, ind)) -centroid(m::TransformedMesh, ind::Int) = m.transform(centroid(m.mesh, ind)) - ==(m₁::TransformedMesh, m₂::TransformedMesh) = m₁.transform == m₂.transform && m₁.mesh == m₂.mesh # alias to improve readability in IO methods diff --git a/src/domains/sets.jl b/src/domains/sets.jl index 20f82cc3f..8b6b13ebf 100644 --- a/src/domains/sets.jl +++ b/src/domains/sets.jl @@ -62,7 +62,3 @@ PointSet(coords::Vararg{TP}) where {TP<:Tuple} = PointSet(collect(coords)) # constructor with iterator of points PointSet(points) = PointSet(map(identity, points)) - -centroid(d::PointSet, ind::Int) = d[ind] - -centroid(d::PointSet) = withcrs(d, sum(to, d) / nelements(d)) diff --git a/src/domains/subdomains.jl b/src/domains/subdomains.jl index ac6ab701d..794dcab9b 100644 --- a/src/domains/subdomains.jl +++ b/src/domains/subdomains.jl @@ -19,6 +19,10 @@ end # specialize constructor to avoid infinite loops SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[inds]) +Base.parent(d::SubDomain) = d.domain + +Base.parentindices(d::SubDomain) = d.inds + """ SubGrid{M,CRS,Dim} @@ -35,8 +39,6 @@ element(d::SubDomain, ind::Int) = element(d.domain, d.inds[ind]) nelements(d::SubDomain) = length(d.inds) -centroid(d::SubDomain, ind::Int) = centroid(d.domain, d.inds[ind]) - # specializations Base.eltype(d::SubDomain) = eltype(d.domain) diff --git a/src/domains/trajecs.jl b/src/domains/trajecs.jl index 22999248a..9c3bab238 100644 --- a/src/domains/trajecs.jl +++ b/src/domains/trajecs.jl @@ -40,7 +40,7 @@ function element(t::CylindricalTrajectory, ind::Int) if ind == 1 # head of trajectory # points at cylinder planes - p₂ = center(Segment(c[ind], c[ind + 1])) + p₂ = centroid(Segment(c[ind], c[ind + 1])) p₁ = p₂ - 2 * (p₂ - c[ind]) # normals to cylinder planes @@ -48,7 +48,7 @@ function element(t::CylindricalTrajectory, ind::Int) n₁ = n₂ elseif ind == n # tail of trajectory # points at cylinder planes - p₁ = center(Segment(c[ind - 1], c[ind])) + p₁ = centroid(Segment(c[ind - 1], c[ind])) p₂ = p₁ + 2 * (c[ind] - p₁) # normals to cylinder planes @@ -56,8 +56,8 @@ function element(t::CylindricalTrajectory, ind::Int) n₂ = n₁ else # middle of trajectory # points at cylinder planes - p₁ = center(Segment(c[ind - 1], c[ind])) - p₂ = center(Segment(c[ind], c[ind + 1])) + p₁ = centroid(Segment(c[ind - 1], c[ind])) + p₂ = centroid(Segment(c[ind], c[ind + 1])) # normals to cylinder planes n₁ = c[ind] - c[ind - 1] diff --git a/src/geometries.jl b/src/geometries.jl index 6bf812717..b50e2c54e 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -54,13 +54,6 @@ Return the length type of the `geometry`. lentype(::Type{<:Geometry{M,CRS}}) where {M,CRS} = lentype(CRS) lentype(g::Geometry) = lentype(typeof(g)) -""" - centroid(geometry) - -Return the centroid of the `geometry`. -""" -centroid(g::Geometry) = center(g) - """ extrema(geometry) diff --git a/src/geometries/multigeom.jl b/src/geometries/multigeom.jl index 4139685a8..451e6f747 100644 --- a/src/geometries/multigeom.jl +++ b/src/geometries/multigeom.jl @@ -39,11 +39,6 @@ Base.parent(m::Multi) = m.geoms paramdim(m::Multi) = maximum(paramdim, m.geoms) -function centroid(m::Multi) - cs = to.(centroid.(m.geoms)) - withcrs(m, sum(cs) / length(cs)) -end - ==(m₁::Multi, m₂::Multi) = m₁.geoms == m₂.geoms Base.isapprox(m₁::Multi, m₂::Multi; atol=atol(lentype(m₁)), kwargs...) = diff --git a/src/geometries/polytopes.jl b/src/geometries/polytopes.jl index 5bfd6059a..641a286bf 100644 --- a/src/geometries/polytopes.jl +++ b/src/geometries/polytopes.jl @@ -250,13 +250,6 @@ Return the number of vertices in the `polytope`. """ nvertices(p::Polytope) = nvertices(typeof(p)) -""" - centroid(polytope) - -Return the centroid of the `polytope`. -""" -centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / length(vertices(p))) - """ unique(polytope) diff --git a/src/geometries/polytopes/polyarea.jl b/src/geometries/polytopes/polyarea.jl index ee0a8f4ee..16c912b90 100644 --- a/src/geometries/polytopes/polyarea.jl +++ b/src/geometries/polytopes/polyarea.jl @@ -35,8 +35,6 @@ vertices(p::PolyArea) = mapreduce(vertices, vcat, p.rings) nvertices(p::PolyArea) = mapreduce(nvertices, +, p.rings) -centroid(p::PolyArea) = centroid(first(p.rings)) - rings(p::PolyArea) = p.rings function Base.unique!(p::PolyArea) diff --git a/src/geometries/polytopes/segment.jl b/src/geometries/polytopes/segment.jl index 588302cc4..0c1623afe 100644 --- a/src/geometries/polytopes/segment.jl +++ b/src/geometries/polytopes/segment.jl @@ -21,11 +21,6 @@ Base.maximum(s::Segment) = s.vertices[2] Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] -function center(s::Segment) - a, b = extrema(s) - withcrs(s, (to(a) + to(b)) / 2) -end - ==(s₁::Segment, s₂::Segment) = s₁.vertices == s₂.vertices Base.isapprox(s₁::Segment, s₂::Segment; atol=atol(lentype(s₁)), kwargs...) = diff --git a/src/geometries/primitives/box.jl b/src/geometries/primitives/box.jl index 2b0a973be..d24355d6b 100644 --- a/src/geometries/primitives/box.jl +++ b/src/geometries/primitives/box.jl @@ -51,8 +51,6 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -center(b::Box) = withcrs(b, (to(b.max) + to(b.min)) / 2) - diagonal(b::Box) = norm(b.max - b.min) sides(b::Box) = Tuple(b.max - b.min) diff --git a/src/geometries/primitives/cylinder.jl b/src/geometries/primitives/cylinder.jl index 55302dc27..1ef022877 100644 --- a/src/geometries/primitives/cylinder.jl +++ b/src/geometries/primitives/cylinder.jl @@ -60,8 +60,6 @@ bottom(c::Cylinder) = c.bot top(c::Cylinder) = c.top -center(c::Cylinder) = center(boundary(c)) - axis(c::Cylinder) = axis(boundary(c)) isright(c::Cylinder) = isright(boundary(c)) diff --git a/src/geometries/primitives/cylindersurface.jl b/src/geometries/primitives/cylindersurface.jl index b2ef64639..e7ad728a9 100644 --- a/src/geometries/primitives/cylindersurface.jl +++ b/src/geometries/primitives/cylindersurface.jl @@ -60,12 +60,6 @@ bottom(c::CylinderSurface) = c.bot top(c::CylinderSurface) = c.top -function center(c::CylinderSurface) - a = to(c.bot(0, 0)) - b = to(c.top(0, 0)) - withcrs(c, (a .+ b) ./ 2) -end - axis(c::CylinderSurface) = Line(c.bot(0, 0), c.top(0, 0)) function isright(c::CylinderSurface) @@ -101,7 +95,7 @@ function (c::CylinderSurface)(φ, z) a = axis(c) d = a(T(1)) - a(T(0)) h = norm(d) - o = center(c) + o = centroid(c) # rotation to align z axis with cylinder axis Q = urotbetween(d, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) diff --git a/src/geometries/primitives/paraboloidsurface.jl b/src/geometries/primitives/paraboloidsurface.jl index 0acee3309..fc74e1b14 100644 --- a/src/geometries/primitives/paraboloidsurface.jl +++ b/src/geometries/primitives/paraboloidsurface.jl @@ -91,16 +91,6 @@ function axis(p::ParaboloidSurface) Line(p.apex, p.apex + Vec(zero(f), zero(f), f)) end -function centroid(p::ParaboloidSurface) - c = p.apex - r = p.radius - f = p.focallength - z = r^2 / 4f - x = zero(z) - y = zero(z) - c + Vec(x, y, z / 2) -end - ==(p₁::ParaboloidSurface, p₂::ParaboloidSurface) = p₁.apex == p₂.apex && p₁.radius == p₂.radius && p₁.focallength == p₂.focallength diff --git a/src/geometries/primitives/point.jl b/src/geometries/primitives/point.jl index f5caa837d..89b006b8a 100644 --- a/src/geometries/primitives/point.jl +++ b/src/geometries/primitives/point.jl @@ -50,8 +50,6 @@ Base.convert(::Type{Point{M,CRS}}, p::Point{M,CRS}) where {M,CRS} = p paramdim(::Type{<:Point}) = 0 -center(p::Point) = p - ==(A::Point, B::Point) = to(A) == to(B) Base.isapprox(A::Point, B::Point; atol=atol(lentype(A)), kwargs...) = isapprox(to(A), to(B); atol, kwargs...) diff --git a/src/geometries/transformedgeom.jl b/src/geometries/transformedgeom.jl index 58c9373d7..bc2b4fb38 100644 --- a/src/geometries/transformedgeom.jl +++ b/src/geometries/transformedgeom.jl @@ -43,8 +43,6 @@ transform(g::TransformedGeometry) = g.transform paramdim(g::TransformedGeometry) = paramdim(g.geometry) -centroid(g::TransformedGeometry) = g.transform(centroid(g.geometry)) - ==(g₁::TransformedGeometry, g₂::TransformedGeometry) = g₁.transform == g₂.transform && g₁.geometry == g₂.geometry Base.isapprox(g₁::TransformedGeometry, g₂::TransformedGeometry; atol=atol(lentype(g₁)), kwargs...) = diff --git a/src/partitioning/bisectfraction.jl b/src/partitioning/bisectfraction.jl index 1786a8b80..4987a8583 100644 --- a/src/partitioning/bisectfraction.jl +++ b/src/partitioning/bisectfraction.jl @@ -27,7 +27,7 @@ function partitioninds(rng::AbstractRNG, domain::Domain, method::BisectFractionP bbox = boundingbox(domain) n = method.normal f = method.fraction - c = to(center(bbox)) + c = to(centroid(bbox)) d = diagonal(bbox) # maximum number of bisections diff --git a/src/refinement/trisubdivision.jl b/src/refinement/trisubdivision.jl index c63bc66d3..01c0fabdb 100644 --- a/src/refinement/trisubdivision.jl +++ b/src/refinement/trisubdivision.jl @@ -38,7 +38,7 @@ function refine(mesh, ::TriSubdivision) for eind in 1:nfacets(t) i, j = sort(∂₁₀(eind)) edge = Segment(points[i], points[j]) - push!(points, center(edge)) + push!(points, centroid(edge)) midpoints[(i, j)] = (np += 1) end diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index 3b6402f55..fc79d239b 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -198,7 +198,7 @@ function apply(::Repair{12}, poly::PolyArea) oring = first(r) outer = if nvertices(oring) == 2 A, B = vertices(oring) - P = center(Segment(A, B)) + P = centroid(Segment(A, B)) Ring(A, P, B) else oring diff --git a/src/transforms/stdcoords.jl b/src/transforms/stdcoords.jl index 7dee2e7ed..2ce7baa4d 100644 --- a/src/transforms/stdcoords.jl +++ b/src/transforms/stdcoords.jl @@ -34,7 +34,7 @@ reapply(t::StdCoords, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) function _stdcoords(t, g) b = boundingbox(g) - t = Translate(to(center(b))...) + t = Translate(to(centroid(b))...) s = Scale(ustrip.(sides(b))) inverse(t) → inverse(s) end diff --git a/test/polytopes.jl b/test/polytopes.jl index cf887a850..098420d0f 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -55,8 +55,8 @@ s = Segment(cart(0, 0, 0), cart(1, 1, 1)) @test boundary(s) == Multi([cart(0, 0, 0), cart(1, 1, 1)]) @test perimeter(s) == zero(T) * u"m" - @test center(s) == cart(0.5, 0.5, 0.5) - @test Meshes.lentype(center(s)) == ℳ + @test centroid(s) == cart(0.5, 0.5, 0.5) + @test Meshes.lentype(centroid(s)) == ℳ # unitful coordinates x1 = T(0)u"m" @@ -65,8 +65,8 @@ @test boundary(s) == Multi([Point(x1, x1, x1), Point(x2, x2, x2)]) @test perimeter(s) == 0u"m" xm = T(0.5)u"m" - @test center(s) == Point(xm, xm, xm) - @test Meshes.lentype(center(s)) == typeof(xm) + @test centroid(s) == Point(xm, xm, xm) + @test Meshes.lentype(centroid(s)) == typeof(xm) # CRS propagation s = Segment(merc(0, 0), merc(1, 1)) diff --git a/test/primitives.jl b/test/primitives.jl index 67b0f9154..1ea39c72b 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -99,8 +99,7 @@ p2 = convert(P, p1) @test p2 isa P - # center and centroid - @test Meshes.center(cart(1, 1)) == cart(1, 1) + # centroid @test centroid(cart(1, 1)) == cart(1, 1) # measure of points is zero @@ -423,7 +422,7 @@ b = Box(cart(1, 1), cart(2, 2)) @test sides(b) == (T(1) * u"m", T(1) * u"m") - @test Meshes.center(b) == cart(1.5, 1.5) + @test centroid(b) == cart(1.5, 1.5) @test diagonal(b) == √T(2) * u"m" b = Box(cart(1, 2), cart(3, 4)) @@ -490,7 +489,7 @@ # CRS propagation b = Box(merc(0, 0), merc(1, 1)) - @test crs(center(b)) === crs(b) + @test crs(centroid(b)) === crs(b) b = Box(cart(0, 0), cart(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" @@ -513,7 +512,7 @@ @test paramdim(b) == 3 @test crs(b) <: Cartesian{NoDatum} @test Meshes.lentype(b) == ℳ - @test Meshes.center(b) == cart(1, 2, 3) + @test center(b) == cart(1, 2, 3) @test radius(b) == T(5) * u"m" b = Ball(latlon(0, 0), T(5)) @@ -591,7 +590,7 @@ @test paramdim(s) == 2 @test crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ - @test Meshes.center(s) == cart(0, 0, 0) + @test center(s) == cart(0, 0, 0) @test radius(s) == T(1) * u"m" @test extrema(s) == (cart(-1, -1, -1), cart(1, 1, 1)) @test isnothing(boundary(s)) @@ -612,7 +611,7 @@ @test embeddim(s) == 2 @test paramdim(s) == 1 @test Meshes.lentype(s) == ℳ - @test Meshes.center(s) == cart(0, 0) + @test center(s) == cart(0, 0) @test radius(s) == T(1) * u"m" @test extrema(s) == (cart(-1, -1), cart(1, 1)) @test isnothing(boundary(s)) @@ -646,13 +645,13 @@ # 2D sphere passing through 3 points s = Sphere(cart(0, 0), cart(0.5, 0), cart(1, 1)) - @test Meshes.center(s) == cart(0.25, 0.75) + @test center(s) == cart(0.25, 0.75) @test radius(s) == T(0.7905694150420949) * u"m" s = Sphere(cart(0, 0), cart(1, 0), cart(0, 1)) - @test Meshes.center(s) == cart(0.5, 0.5) + @test center(s) == cart(0.5, 0.5) @test radius(s) == T(0.7071067811865476) * u"m" s = Sphere(cart(0, 0), cart(1, 0), cart(1, 1)) - @test Meshes.center(s) == cart(0.5, 0.5) + @test center(s) == cart(0.5, 0.5) @test radius(s) == T(0.7071067811865476) * u"m" # 3D sphere passing through 4 points @@ -661,7 +660,7 @@ @test cart(5, 0, 1) ∈ s @test cart(1, 1, 1) ∈ s @test cart(3, 2, 1) ∈ s - O = Meshes.center(s) + O = center(s) r = radius(s) @test isapprox(r, norm(cart(0, 0, 0) - O)) @@ -729,7 +728,7 @@ @test crs(d) <: Cartesian{NoDatum} @test Meshes.lentype(d) == ℳ @test plane(d) == p - @test Meshes.center(d) == cart(0, 0, 0) + @test center(d) == cart(0, 0, 0) @test radius(d) == T(2) * u"m" @test normal(d) == vector(0, 0, 1) @test measure(d) == T(π) * T(2)^2 * u"m^2" @@ -768,7 +767,7 @@ @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test plane(c) == p - @test Meshes.center(c) == cart(0, 0, 0) + @test center(c) == cart(0, 0, 0) @test radius(c) == T(2) * u"m" @test measure(c) == 2 * T(π) * T(2) * u"m" @test length(c) == measure(c) @@ -868,7 +867,6 @@ @test radius(c) == T(1) * u"m" @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test center(c) == cart(0.0, 0.0, 0.5) @test centroid(c) == cart(0.0, 0.0, 0.5) @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) @test isright(c) @@ -910,7 +908,6 @@ @test radius(c) == T(2) * u"m" @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test center(c) == cart(0.0, 0.0, 0.5) @test centroid(c) == cart(0.0, 0.0, 0.5) @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) @test isright(c) @@ -951,7 +948,7 @@ c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) c = CylinderSurface(Point(c1), Point(c2), T(1)) - @test crs(center(c)) === crs(c) + @test crs(centroid(c)) === crs(c) c = CylinderSurface(T(1)) @test sprint(show, c) == @@ -1239,7 +1236,7 @@ @test paramdim(t) == 2 @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ - @test Meshes.center(t) == cart(1, 1, 1) + @test center(t) == cart(1, 1, 1) @test normal(t) == vector(1, 0, 0) @test radii(t) == (T(2) * u"m", T(1) * u"m") @test axis(t) == Line(cart(1, 1, 1), cart(2, 1, 1)) From 9ffa296cd4b31603ffcd6ae8df6bae6b53af5af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 14 Aug 2024 10:03:15 -0300 Subject: [PATCH 273/423] Quick fix dup method --- src/domains/subdomains.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/domains/subdomains.jl b/src/domains/subdomains.jl index 794dcab9b..d95d1c566 100644 --- a/src/domains/subdomains.jl +++ b/src/domains/subdomains.jl @@ -19,10 +19,6 @@ end # specialize constructor to avoid infinite loops SubDomain(d::SubDomain, inds::AbstractVector{Int}) = SubDomain(d.domain, d.inds[inds]) -Base.parent(d::SubDomain) = d.domain - -Base.parentindices(d::SubDomain) = d.inds - """ SubGrid{M,CRS,Dim} From 3fa7859e260976bb22b1d0091460bc3c94060fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 15 Aug 2024 17:35:32 -0300 Subject: [PATCH 274/423] Generalize transforms with Primitive (#1005) * Genearlize transforms with Primitive * More adjustments * More adjustments * More adjustments * More adjustments * Use isparametrized in TransformedGeometry ctor * More adjustments * Update show * Update 'isapprox' of domains * Add 'translate' helper * Apply suggestions * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * Add show tests * Specialize \to for affine transforms * Update 'isapprox' * Optimize |Stretch * Fix some tests * Update 'Shadow' implementation * Apply suggestions * Apply suggestions * Apply suggestions * Fix tests * Apply suggestions * Apply suggestions * Apply suggestions * More adjustments * Update 'isapprox' * Apply suggestions --------- Co-authored-by: Elias Carvalho --- Project.toml | 2 +- src/boundary.jl | 2 + src/centroid.jl | 4 +- src/domains.jl | 3 +- src/geometries.jl | 13 ++++ src/geometries/primitives.jl | 13 ---- src/geometries/primitives/ball.jl | 6 +- src/geometries/primitives/box.jl | 2 +- src/geometries/primitives/sphere.jl | 6 +- src/geometries/transformedgeom.jl | 33 +++++++-- src/measures.jl | 6 +- src/pointification.jl | 4 ++ src/predicates/isparametrized.jl | 21 +++--- src/transforms.jl | 46 ++++++++++++- src/transforms/affine.jl | 10 +-- src/transforms/lengthunit.jl | 30 +++++---- src/transforms/proj.jl | 6 +- src/transforms/rotate.jl | 8 +-- src/transforms/scale.jl | 26 ++++---- src/transforms/shadow.jl | 60 +++++++++-------- src/transforms/stretch.jl | 24 ++++--- src/transforms/translate.jl | 10 +-- test/predicates.jl | 6 +- test/transformedgeoms.jl | 18 +++-- test/transforms.jl | 100 ++++++++-------------------- 25 files changed, 248 insertions(+), 211 deletions(-) diff --git a/Project.toml b/Project.toml index cf1505930..b1212d6db 100644 --- a/Project.toml +++ b/Project.toml @@ -43,6 +43,6 @@ ScopedValues = "1.2" SparseArrays = "1.9" StaticArrays = "1.0" StatsBase = "0.33, 0.34" -TransformsBase = "1.4.1" +TransformsBase = "1.6" Unitful = "1.17" julia = "1.9" diff --git a/src/boundary.jl b/src/boundary.jl index b0bc61130..1321dca11 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -114,3 +114,5 @@ function boundary(m::Multi) valid = filter(!isnothing, bounds) isempty(valid) ? nothing : reduce(merge, valid) end + +boundary(g::TransformedGeometry) = transform(g)(boundary(parent(g))) diff --git a/src/centroid.jl b/src/centroid.jl index 340b17743..e8d8090a5 100644 --- a/src/centroid.jl +++ b/src/centroid.jl @@ -9,12 +9,12 @@ The centroid of the `geometry`. """ centroid(g::Geometry) = center(g) # some geometries have a natural center -centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / nvertices(p)) - centroid(p::Point) = p centroid(p::Polygon) = centroid(first(rings(p))) +centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / nvertices(p)) + centroid(b::Box) = withcrs(b, sum(to, extrema(b)) / 2) centroid(p::Plane) = p(0, 0) diff --git a/src/domains.jl b/src/domains.jl index 3ae0efcd9..26996a5a2 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -31,7 +31,8 @@ function nelements end ==(d1::Domain, d2::Domain) = nelements(d1) == nelements(d2) && all(d1[i] == d2[i] for i in 1:nelements(d1)) -Base.isapprox(d1::Domain, d2::Domain) = nelements(d1) == nelements(d2) && all(d1[i] ≈ d2[i] for i in 1:nelements(d1)) +Base.isapprox(d1::Domain, d2::Domain; kwargs...) = + nelements(d1) == nelements(d2) && all(isapprox(d1[i], d2[i]; kwargs...) for i in 1:nelements(d1)) Base.getindex(d::Domain, ind::Int) = element(d, ind) diff --git a/src/geometries.jl b/src/geometries.jl index b50e2c54e..7e5f5b615 100644 --- a/src/geometries.jl +++ b/src/geometries.jl @@ -68,6 +68,19 @@ Base.extrema(g::Geometry) = extrema(boundingbox(g)) Base.summary(io::IO, geom::Geometry) = print(io, prettyname(geom)) +function Base.show(io::IO, geom::Geometry) + name = prettyname(geom) + ioctx = IOContext(io, :compact => true) + print(io, "$name(") + printfields(ioctx, geom, singleline=true) + print(io, ")") +end + +function Base.show(io::IO, ::MIME"text/plain", geom::Geometry) + summary(io, geom) + printfields(io, geom) +end + # ---------------- # IMPLEMENTATIONS # ---------------- diff --git a/src/geometries/primitives.jl b/src/geometries/primitives.jl index 7af61b8d2..34ad3d82a 100644 --- a/src/geometries/primitives.jl +++ b/src/geometries/primitives.jl @@ -12,19 +12,6 @@ See . """ abstract type Primitive{M<:Manifold,C<:CRS} <: Geometry{M,C} end -function Base.show(io::IO, geom::Primitive) - name = prettyname(geom) - ioctx = IOContext(io, :compact => true) - print(io, "$name(") - printfields(ioctx, geom, singleline=true) - print(io, ")") -end - -function Base.show(io::IO, ::MIME"text/plain", geom::Primitive) - summary(io, geom) - printfields(io, geom) -end - include("primitives/point.jl") include("primitives/ray.jl") include("primitives/line.jl") diff --git a/src/geometries/primitives/ball.jl b/src/geometries/primitives/ball.jl index 09eec61f4..22e991eb2 100644 --- a/src/geometries/primitives/ball.jl +++ b/src/geometries/primitives/ball.jl @@ -36,9 +36,7 @@ radius(b::Ball) = b.radius Base.isapprox(b₁::Ball, b₂::Ball; atol=atol(lentype(b₁)), kwargs...) = isapprox(b₁.center, b₂.center; atol, kwargs...) && isapprox(b₁.radius, b₂.radius; atol, kwargs...) -(b::Ball)(args...) = _ball(Val(embeddim(b)), b, args...) - -function _ball(::Val{2}, b, ρ, φ) +function (b::Ball{𝔼{2}})(ρ, φ) T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, φ), "b(ρ, φ) is not defined for ρ, φ outside [0, 1]².")) @@ -52,7 +50,7 @@ function _ball(::Val{2}, b, ρ, φ) c + Vec(x, y) end -function _ball(::Val{3}, b, ρ, θ, φ) +function (b::Ball{𝔼{3}})(ρ, θ, φ) T = numtype(lentype(b)) if (ρ < 0 || ρ > 1) || (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((ρ, θ, φ), "b(ρ, θ, φ) is not defined for ρ, θ, φ outside [0, 1]³.")) diff --git a/src/geometries/primitives/box.jl b/src/geometries/primitives/box.jl index d24355d6b..3421cdb57 100644 --- a/src/geometries/primitives/box.jl +++ b/src/geometries/primitives/box.jl @@ -60,7 +60,7 @@ sides(b::Box) = Tuple(b.max - b.min) Base.isapprox(b₁::Box, b₂::Box; atol=atol(lentype(b₁)), kwargs...) = isapprox(b₁.min, b₂.min; atol, kwargs...) && isapprox(b₁.max, b₂.max; atol, kwargs...) -function (b::Box)(uv...) +function (b::Box{<:𝔼})(uv...) if !all(x -> 0 ≤ x ≤ 1, uv) throw(DomainError(uv, "b(u, v, ...) is not defined for u, v, ... outside [0, 1]ⁿ.")) end diff --git a/src/geometries/primitives/sphere.jl b/src/geometries/primitives/sphere.jl index c643822e7..c04a7ddbd 100644 --- a/src/geometries/primitives/sphere.jl +++ b/src/geometries/primitives/sphere.jl @@ -73,9 +73,7 @@ radius(s::Sphere) = s.radius Base.isapprox(s₁::Sphere, s₂::Sphere; atol=atol(lentype(s₁)), kwargs...) = isapprox(s₁.center, s₂.center; atol, kwargs...) && isapprox(s₁.radius, s₂.radius; atol, kwargs...) -(s::Sphere)(args...) = _sphere(Val(embeddim(s)), s, args...) - -function _sphere(::Val{2}, s, φ) +function (s::Sphere{𝔼{2}})(φ) T = numtype(lentype(s)) if (φ < 0 || φ > 1) throw(DomainError(φ, "s(φ) is not defined for φ outside [0, 1].")) @@ -88,7 +86,7 @@ function _sphere(::Val{2}, s, φ) c + Vec(x, y) end -function _sphere(::Val{3}, s, θ, φ) +function (s::Sphere{𝔼{3}})(θ, φ) T = numtype(lentype(s)) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "s(θ, φ) is not defined for θ, φ outside [0, 1]².")) diff --git a/src/geometries/transformedgeom.jl b/src/geometries/transformedgeom.jl index bc2b4fb38..aa105cffd 100644 --- a/src/geometries/transformedgeom.jl +++ b/src/geometries/transformedgeom.jl @@ -17,7 +17,9 @@ struct TransformedGeometry{M<:Manifold,C<:CRS,G<:Geometry,T<:Transform} <: Geome end function TransformedGeometry(g::Geometry, t::Transform) - p = t(centroid(g)) + D = paramdim(g) + T = numtype(lentype(g)) + p = t(isparametrized(g) ? g(ntuple(i -> zero(T), D)...) : centroid(g)) TransformedGeometry{manifold(p),crs(p)}(g, t) end @@ -43,10 +45,20 @@ transform(g::TransformedGeometry) = g.transform paramdim(g::TransformedGeometry) = paramdim(g.geometry) -==(g₁::TransformedGeometry, g₂::TransformedGeometry) = g₁.transform == g₂.transform && g₁.geometry == g₂.geometry +==(g₁::TransformedGeometry, g₂::TransformedGeometry) = _isequal(g₁, g₂) + +==(g₁::TransformedGeometry, g₂::Geometry) = _isequal(g₁, g₂) + +==(g₁::Geometry, g₂::TransformedGeometry) = _isequal(g₁, g₂) Base.isapprox(g₁::TransformedGeometry, g₂::TransformedGeometry; atol=atol(lentype(g₁)), kwargs...) = - isapprox(g₁.geometry, g₂.geometry; atol, kwargs...) && g₁.transform == g₂.transform + _isapprox(g₁, g₂; atol, kwargs...) + +Base.isapprox(g₁::TransformedGeometry, g₂::Geometry; atol=atol(lentype(g₁)), kwargs...) = + _isapprox(g₁, g₂; atol, kwargs...) + +Base.isapprox(g₁::Geometry, g₂::TransformedGeometry; atol=atol(lentype(g₁)), kwargs...) = + _isapprox(g₁, g₂; atol, kwargs...) (g::TransformedGeometry)(uvw...) = g.transform(g.geometry(uvw...)) @@ -74,7 +86,16 @@ rings(p::TransformedPolygon) = map(p.transform, rings(p.geometry)) # IO METHODS # ----------- -function Base.summary(io::IO, g::TransformedGeometry) - name = prettyname(g.geometry) - print(io, "Transformed$name") +prettyname(g::TransformedGeometry) = "Transformed$(prettyname(g.geometry))" + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_isequal(g₁, g₂) = pointify(g₁) == pointify(g₂) + +function _isapprox(g₁, g₂; kwargs...) + ps₁ = pointify(g₁) + ps₂ = pointify(g₂) + length(ps₁) == length(ps₂) && all(isapprox(p₁, p₂; atol, kwargs...) for (p₁, p₂) in zip(ps₁, ps₂)) end diff --git a/src/measures.jl b/src/measures.jl index 20097bfa2..c54eee3a1 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -25,17 +25,17 @@ measure(l::Line) = typemax(lentype(l)) measure(p::Plane) = typemax(lentype(p))^2 -measure(b::Box) = prod(maximum(b) - minimum(b)) +measure(b::Box{<:𝔼}) = prod(maximum(b) - minimum(b)) # https://en.wikipedia.org/wiki/Volume_of_an_n-ball -function measure(b::Ball) +function measure(b::Ball{<:𝔼}) T = numtype(lentype(b)) r, n = radius(b), embeddim(b) T(π)^T(n / 2) * r^n / gamma(T(n / 2) + 1) end # https://en.wikipedia.org/wiki/N-sphere#Volume_and_surface_area -function measure(s::Sphere) +function measure(s::Sphere{<:𝔼}) T = numtype(lentype(s)) r, n = radius(s), embeddim(s) 2 * T(π)^T(n / 2) * r^(n - 1) / gamma(T(n / 2)) diff --git a/src/pointification.jl b/src/pointification.jl index 1b7bbca3b..464dd875c 100644 --- a/src/pointification.jl +++ b/src/pointification.jl @@ -20,6 +20,8 @@ pointify(p::Polytope) = collect(vertices(p)) pointify(m::Multi) = pointify(parent(m)) +pointify(g::TransformedGeometry) = map(transform(g), pointify(parent(g))) + pointify(geoms) = mapreduce(pointify, vcat, geoms) # ---------------- @@ -32,6 +34,8 @@ pointify(s::Sphere) = _rsample(s) pointify(t::Torus) = _rsample(t) +pointify(c::CylinderSurface) = _rsample(c) + pointify(p::PolyArea) = vertices(p) pointify(r::Ring) = vertices(r) diff --git a/src/predicates/isparametrized.jl b/src/predicates/isparametrized.jl index 863512177..178b73d39 100644 --- a/src/predicates/isparametrized.jl +++ b/src/predicates/isparametrized.jl @@ -3,18 +3,15 @@ # ------------------------------------------------------------------ """ - isparametrized(geometry) + isparametrized(object) -Tells whether or not the `geometry` is parametrized, -i.e. can be called as `geometry(u₁, u₂, ..., uₙ)` with -local coordinates `(u₁, u₂, ..., uₙ) ∈ [0,1]ⁿ` where -`n` is the parametric dimension. +Tells whether or not the geometric `object` is parametrized, i.e. +can be called as `object(u₁, u₂, ..., uₙ)` with local coordinates +`(u₁, u₂, ..., uₙ) ∈ [0,1]ⁿ` where `n` is the parametric dimension. See also [`paramdim`](@ref). """ -function isparametrized end - -isparametrized(g::Geometry) = isparametrized(typeof(g)) +isparametrized(g) = isparametrized(typeof(g)) isparametrized(::Type{<:Geometry}) = false @@ -28,11 +25,11 @@ isparametrized(::Type{<:Plane}) = true isparametrized(::Type{<:BezierCurve}) = true -isparametrized(::Type{<:Box}) = true +isparametrized(::Type{<:Box{<:𝔼}}) = true -isparametrized(::Type{<:Ball}) = true +isparametrized(::Type{<:Ball{<:𝔼}}) = true -isparametrized(::Type{<:Sphere}) = true +isparametrized(::Type{<:Sphere{<:𝔼}}) = true isparametrized(::Type{<:Ellipsoid}) = true @@ -60,6 +57,6 @@ isparametrized(::Type{<:Tetrahedron}) = true isparametrized(::Type{<:Hexahedron}) = true -isparametrized(d::Domain) = isparametrized(typeof(d)) +isparametrized(::Type{<:TransformedGeometry{M,C,G}}) where {M,C,G} = isparametrized(G) isparametrized(::Type{<:Domain}) = false diff --git a/src/transforms.jl b/src/transforms.jl index 3d9019ec6..6ea448e91 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -72,8 +72,8 @@ end # stop recursion at non-geometric types applycoord(::CoordinateTransform, x) = x -# special treatment for Point -applycoord(t::CoordinateTransform, p::Point) = withcrs(p, applycoord(t, to(p))) +# special treatment for TransformedGeometry +applycoord(t::CoordinateTransform, g::TransformedGeometry) = TransformedGeometry(g, t) # special treatment for TransformedMesh applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) @@ -100,3 +100,45 @@ include("transforms/within.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") + +# -------------- +# OPTIMIZATIONS +# -------------- + +function →(t₁::Rotate, t₂::Rotate) + rot₁ = parameters(t₁).rot + rot₂ = parameters(t₂).rot + Rotate(rot₂ * rot₁) +end + +function →(t₁::Translate, t₂::Translate) + offsets₁ = parameters(t₁).offsets + offsets₂ = parameters(t₂).offsets + Translate(offsets₁ .+ offsets₂) +end + +function →(t₁::Scale, t₂::Scale) + factors₁ = parameters(t₁).factors + factors₂ = parameters(t₂).factors + Scale(factors₁ .* factors₂) +end + +function →(t₁::Affine, t₂::Affine) + A₁ = parameters(t₁).A + A₂ = parameters(t₂).A + b₁ = parameters(t₁).b + b₂ = parameters(t₂).b + Affine(A₂ * A₁, A₂ * b₁ + b₂) +end + +function →(t₁::Stretch, t₂::Stretch) + factors₁ = parameters(t₁).factors + factors₂ = parameters(t₂).factors + Stretch(factors₁ .* factors₂) +end + +function →(t₁::Rotate, t₂::Translate) + rot = parameters(t₁).rot + offsets = parameters(t₂).offsets + Affine(rot, SVector(offsets)) +end diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 757f06a9e..eaa2f0738 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -53,19 +53,15 @@ function inverse(t::Affine) Affine(A, b) end -applycoord(t::Affine, v::Vec) = t.A * v +applycoord(t::Affine, p::Point) = withcrs(p, muladd(t.A, to(p), t.b)) -applycoord(t::Affine, p::Point) = withcrs(p, t.A * to(p) + t.b) +applycoord(t::Affine, v::Vec) = t.A * v # -------------- # SPECIAL CASES # -------------- -applycoord(t::Affine, b::Box) = _applycoord(t, b, Val(embeddim(b))) - -_applycoord(t::Affine, b::Box, ::Val{2}) = applycoord(t, convert(Quadrangle, b)) - -_applycoord(t::Affine, b::Box, ::Val{3}) = applycoord(t, convert(Hexahedron, b)) +applycoord(t::Affine, b::Box) = TransformedGeometry(b, t) applycoord(t::Affine, g::CartesianGrid) = TransformedGrid(g, t) diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index 8ab39c91a..a2c78bb59 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -20,9 +20,25 @@ end parameters(t::LengthUnit) = (; unit=t.unit) +applycoord(t::LengthUnit, p::Point) = Point(_lenunit(coords(p), t.unit)) + applycoord(t::LengthUnit, v::Vec) = uconvert.(t.unit, v) -applycoord(t::LengthUnit, p::Point) = Point(_lenunit(coords(p), t.unit)) +# -------------- +# SPECIAL CASES +# -------------- + +applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len) + +applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens) + +applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g))) + +applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- function _lenunit(c::Cartesian, u) d = datum(c) @@ -50,15 +66,3 @@ function _lenunit(c::Spherical, u) end _lenunit(c, _) = throw(ArgumentError("the length unit of $(prettyname(c)) cannot be changed")) - -# -------------- -# SPECIAL CASES -# -------------- - -applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len) - -applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens) - -applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g))) - -applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index e21e3091b..36eb2a484 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -29,14 +29,16 @@ Proj(code::Type{<:ESRI}) = Proj{CoordRefSystems.get(code)}() parameters(::Proj{CRS}) where {CRS} = (; CRS) -applycoord(::Proj, v::Vec) = v - applycoord(::Proj{CRS}, p::Point) where {CRS} = Point(convert(CRS, coords(p))) +applycoord(::Proj, v::Vec) = v + # -------------- # SPECIAL CASES # -------------- +applycoord(t::Proj, g::Primitive) = TransformedGeometry(g, t) + applycoord(t::Proj, g::RectilinearGrid) = applycoord(t, convert(SimpleMesh, g)) applycoord(t::Proj, g::StructuredGrid) = applycoord(t, convert(SimpleMesh, g)) diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 580d85cad..76dd10f73 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -61,17 +61,15 @@ isinvertible(::Type{<:Rotate}) = true inverse(t::Rotate) = Rotate(inv(t.rot)) +applycoord(t::Rotate, p::Point) = withcrs(p, applycoord(t, to(p))) + applycoord(t::Rotate, v::Vec) = urotapply(t.rot, v) # -------------- # SPECIAL CASES # -------------- -applycoord(t::Rotate, b::Box) = _applycoord(t, b, Val(embeddim(b))) - -_applycoord(t::Rotate, b::Box, ::Val{2}) = applycoord(t, convert(Quadrangle, b)) - -_applycoord(t::Rotate, b::Box, ::Val{3}) = applycoord(t, convert(Hexahedron, b)) +applycoord(t::Rotate, b::Box) = TransformedGeometry(b, t) applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 67b88a0dc..c734c4142 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -38,35 +38,35 @@ isinvertible(::Type{<:Scale}) = true inverse(t::Scale) = Scale(1 ./ t.factors) +applycoord(t::Scale, p::Point) = withcrs(p, applycoord(t, to(p))) + applycoord(t::Scale, v::Vec) = t.factors .* v # -------------- # SPECIAL CASES # -------------- -applycoord(t::Scale, b::Ball) = applycoord(t, discretize(b)) - -applycoord(t::Scale{Dim}, s::Sphere) where {Dim} = _applycoord(t, s, Val(Dim), Val(embeddim(s))) +applycoord(t::Scale, b::Ball) = TransformedGeometry(b, t) -_applycoord(t::Scale, s::Sphere, ::Val, ::Val) = applycoord(t, discretize(s)) +applycoord(t::Scale, s::Sphere) = TransformedGeometry(s, t) -_applycoord(t::Scale, s::Sphere, ::Val{1}, ::Val) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) +applycoord(t::Scale{1}, s::Sphere) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) -_applycoord(t::Scale, s::Sphere, ::Val{3}, ::Val{3}) = Ellipsoid(t.factors .* radius(s), applycoord(t, center(s))) +applycoord(t::Scale{3}, s::Sphere{𝔼{3}}) = Ellipsoid(t.factors .* radius(s), applycoord(t, center(s))) -applycoord(t::Scale, e::Ellipsoid) = applycoord(t, discretize(e)) +applycoord(t::Scale, e::Ellipsoid) = TransformedGeometry(e, t) -applycoord(t::Scale, d::Disk) = applycoord(t, discretize(d)) +applycoord(t::Scale, d::Disk) = TransformedGeometry(d, t) -applycoord(t::Scale, c::Circle) = applycoord(t, discretize(c)) +applycoord(t::Scale, c::Circle) = TransformedGeometry(c, t) -applycoord(t::Scale, c::Cylinder) = applycoord(t, discretize(c)) +applycoord(t::Scale, c::Cylinder) = TransformedGeometry(c, t) -applycoord(t::Scale, c::CylinderSurface) = applycoord(t, discretize(c)) +applycoord(t::Scale, c::CylinderSurface) = TransformedGeometry(c, t) -applycoord(t::Scale, p::ParaboloidSurface) = applycoord(t, discretize(p)) +applycoord(t::Scale, p::ParaboloidSurface) = TransformedGeometry(p, t) -applycoord(t::Scale, tr::Torus) = applycoord(t, discretize(tr)) +applycoord(t::Scale, tr::Torus) = TransformedGeometry(tr, t) function applycoord(t::Scale, g::CartesianGrid) dims = size(g) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index c0dbee205..c33e964ce 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -33,6 +33,40 @@ apply(t::Shadow, v::Vec) = _shadow(v, _sort(t.dims)), nothing apply(t::Shadow, g::GeometryOrDomain) = _shadow(g, _sort(t.dims)), nothing +# -------------- +# SPECIAL CASES +# -------------- + +apply(::Shadow, ::Plane) = throw(ArgumentError("Shadow transform doesn't yet support planes")) + +apply(t::Shadow, e::Ellipsoid) = TransformedGeometry(e, t), nothing + +apply(t::Shadow, d::Disk) = TransformedGeometry(d, t), nothing + +apply(t::Shadow, c::Circle) = TransformedGeometry(c, t), nothing + +apply(t::Shadow, c::Cylinder) = TransformedGeometry(c, t), nothing + +apply(t::Shadow, c::CylinderSurface) = TransformedGeometry(c, t), nothing + +apply(t::Shadow, c::Cone) = TransformedGeometry(c, t), nothing + +apply(t::Shadow, c::ConeSurface) = TransformedGeometry(c, t), nothing + +apply(t::Shadow, f::Frustum) = TransformedGeometry(f, t), nothing + +apply(t::Shadow, f::FrustumSurface) = TransformedGeometry(f, t), nothing + +apply(t::Shadow, p::ParaboloidSurface) = TransformedGeometry(p, t), nothing + +apply(t::Shadow, tr::Torus) = TransformedGeometry(tr, t), nothing + +apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))), nothing + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + function _index(d) if d == 'x' 1 @@ -51,32 +85,6 @@ _shadow(v::Vec, dims) = v[dims] _shadow(p::Point, dims) = withcrs(p, to(p)[dims]) -_shadow(::Plane, _) = throw(ArgumentError("Shadow transform doesn't yet support planes")) - -_shadow(e::Ellipsoid, dims) = _shadow(discretize(e), dims) - -_shadow(d::Disk, dims) = _shadow(discretize(d), dims) - -_shadow(c::Circle, dims) = _shadow(discretize(c), dims) - -_shadow(c::Cylinder, dims) = _shadow(discretize(c), dims) - -_shadow(c::CylinderSurface, dims) = _shadow(discretize(c), dims) - -_shadow(c::Cone, dims) = _shadow(discretize(c), dims) - -_shadow(c::ConeSurface, dims) = _shadow(discretize(c), dims) - -_shadow(f::Frustum, dims) = _shadow(discretize(f), dims) - -_shadow(f::FrustumSurface, dims) = _shadow(discretize(f), dims) - -_shadow(p::ParaboloidSurface, dims) = _shadow(discretize(p), dims) - -_shadow(t::Torus, dims) = _shadow(discretize(t), dims) - -_shadow(t::CylindricalTrajectory, dims) = _shadow(GeometrySet(collect(t)), dims) - function _shadow(g::CartesianGrid, dims) sz = size(g)[dims] or = _shadow(minimum(g), dims) diff --git a/src/transforms/stretch.jl b/src/transforms/stretch.jl index a47f2b600..0b79e553e 100644 --- a/src/transforms/stretch.jl +++ b/src/transforms/stretch.jl @@ -42,17 +42,9 @@ function apply(t::Stretch, g::GeometryOrDomain) n, (p, c) end -revert(t::Stretch, g::GeometryOrDomain, c) = revert(c[1], g, c[2]) +revert(::Stretch, g::GeometryOrDomain, c) = revert(c[1], g, c[2]) -reapply(t::Stretch, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) - -function _stretch(t, g) - o = to(_origin(g)) - Translate(-o...) → Scale(t.factors) → Translate(o...) -end - -_origin(g) = centroid(g) -_origin(p::Plane) = p(0, 0) +reapply(::Stretch, g::GeometryOrDomain, c) = reapply(c[1], g, c[2]) # -------------- # SPECIAL CASES @@ -63,3 +55,15 @@ apply(t::Stretch, v::Vec) = apply(Scale(t.factors), v) revert(t::Stretch, v::Vec, c) = revert(Scale(t.factors), v, c) reapply(t::Stretch, v::Vec, c) = reapply(Scale(t.factors), v, c) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function _stretch(t, g) + o = to(_origin(g)) + Translate(-o...) → Scale(t.factors) → Translate(o...) +end + +_origin(g) = centroid(g) +_origin(p::Plane) = p(0, 0) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index 30a674ef7..73aee8486 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -29,13 +29,13 @@ isinvertible(::Type{<:Translate}) = true inverse(t::Translate) = Translate(-1 .* t.offsets) -applycoord(t::Translate, v::Vec) = v - applycoord(t::Translate, p::Point) = p + Vec(t.offsets) -# ---------------- -# SPECIALIZATIONS -# ---------------- +applycoord(::Translate, v::Vec) = v + +# -------------- +# SPECIAL CASES +# -------------- apply(t::Translate, g::RectilinearGrid{Datum}) where {Datum} = RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], embeddim(g))), nothing diff --git a/test/predicates.jl b/test/predicates.jl index 33235ecae..45ff05e97 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -117,9 +117,9 @@ @test isparametrized(Ray) @test isparametrized(Line) @test isparametrized(Plane) - @test isparametrized(Box) - @test isparametrized(Ball) - @test isparametrized(Sphere) + @test isparametrized(Box{<:𝔼}) + @test isparametrized(Ball{<:𝔼}) + @test isparametrized(Sphere{<:𝔼}) @test isparametrized(Ellipsoid) @test isparametrized(Disk) @test isparametrized(Circle) diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl index 34cc46eef..94ffccf4b 100644 --- a/test/transformedgeoms.jl +++ b/test/transformedgeoms.jl @@ -8,9 +8,11 @@ tb2 = Meshes.TransformedGeometry(tb, t2) @test Meshes.transform(tb2) == (t → t2) @test paramdim(tb) == paramdim(b) + @test tb == tb + @test tb ≈ tb + @test tb(T(0.5), T(0.5)) == t(b(T(0.5), T(0.5))) @test centroid(tb) == t(centroid(b)) @test discretize(tb) == t(discretize(b)) - @test tb(T(0.5), T(0.5)) == t(b(T(0.5), T(0.5))) t3 = Scale(T(2), T(2)) tb3 = Meshes.TransformedGeometry(b, t3) @test measure(tb3) == 4 * measure(b) @@ -22,16 +24,12 @@ tb = Meshes.TransformedGeometry(b, t) @test paramdim(tb) == paramdim(b) @test centroid(tb) == t(centroid(b)) - equaltest(tb) - isapproxtest(tb) s = Sphere(latlon(0, 0), T(1)) t = Proj(Cartesian) ts = Meshes.TransformedGeometry(s, t) @test paramdim(ts) == paramdim(s) @test centroid(ts) == t(centroid(s)) - equaltest(ts) - isapproxtest(ts) s = Segment(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) @@ -54,4 +52,14 @@ @test unique(tp2) == tp equaltest(tp) isapproxtest(tp) + + b = Box(cart(0, 0), cart(1, 1)) + t = Translate(T(1), T(2)) + tb = Meshes.TransformedGeometry(b, t) + @test sprint(show, tb) == + "TransformedBox(geometry: Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)), transform: Translate(offsets: (1.0 m, 2.0 m)))" + @test sprint(show, MIME"text/plain"(), tb) == """ + TransformedBox + ├─ geometry: Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)) + └─ transform: Translate(offsets: (1.0 m, 2.0 m))""" end diff --git a/test/transforms.jl b/test/transforms.jl index 5559d663a..b051e49eb 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -45,16 +45,12 @@ f = Rotate(Angle2d(T(π / 2))) g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) - @test r isa Quadrangle @test r ≈ Quadrangle(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) - q = TB.revert(f, r, c) - @test q isa Quadrangle - @test q ≈ convert(Quadrangle, g) + @test TB.revert(f, r, c) ≈ g f = Rotate(vector(1, 0, 0), vector(0, 1, 0)) g = Box(cart(0, 0, 0), cart(1, 1, 1)) r, c = TB.apply(f, g) - @test r isa Hexahedron @test r ≈ Hexahedron( cart(0, 0, 0), cart(0, 1, 0), @@ -66,7 +62,6 @@ cart(-1, 0, 1) ) h = TB.revert(f, r, c) - @test h isa Hexahedron @test h ≈ convert(Hexahedron, g) # ---------- @@ -174,7 +169,6 @@ f = Rotate(Angle2d(T(π / 2))) d = cartgrid(10, 10) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -185,7 +179,6 @@ f = Rotate(Angle2d(T(π / 2))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -196,7 +189,6 @@ f = Rotate(Angle2d(T(π / 2))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -450,16 +442,12 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) - @test r isa Quadrangle @test r ≈ Quadrangle(cart(1, 1), cart(1, 2), cart(0, 2), cart(0, 1)) - q = TB.revert(f, r, c) - @test q isa Quadrangle - @test q ≈ convert(Quadrangle, g) + @test TB.revert(f, r, c) ≈ g f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) g = Box(cart(0, 0, 0), cart(1, 1, 1)) r, c = TB.apply(f, g) - @test r isa Hexahedron @test r ≈ Hexahedron( cart(1, 2, 3), cart(1, 2, 2), @@ -471,7 +459,6 @@ cart(2, 3, 3) ) h = TB.revert(f, r, c) - @test h isa Hexahedron @test h ≈ convert(Hexahedron, g) # --------- @@ -547,7 +534,6 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = cartgrid(10, 10) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -558,7 +544,6 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -569,7 +554,6 @@ f = Affine(Angle2d(T(π / 2)), T[1, 1]) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa Meshes.TransformedGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d @@ -676,10 +660,9 @@ g = Ball(cart(1, 2), T(3)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # ------- # SPHERE @@ -689,19 +672,16 @@ g = Sphere(cart(1, 2), T(3)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m f = Scale(T(1), T(2), T(3)) g = Sphere(cart(1, 2, 3), T(4)) r, c = TB.apply(f, g) @test r isa Ellipsoid @test r ≈ Ellipsoid(T.((4, 8, 12)), cart(1, 4, 9)) - m = TB.revert(f, r, c) - @test m isa SimpleMesh - @test m ≈ discretize(g) + @test discretize(TB.revert(f, r, c)) ≈ discretize(g) f = Scale(T(2)) g = Sphere(cart(1, 2), T(3)) @@ -725,10 +705,9 @@ g = Ellipsoid(T.((1, 2, 3))) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # ----- # DISK @@ -738,10 +717,9 @@ g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # ------- # CIRCLE @@ -751,10 +729,9 @@ g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # ---------------- # CYLINDERSURFACE @@ -764,10 +741,9 @@ g = CylinderSurface(T(1)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # ------------------ # PARABOLOIDSURFACE @@ -777,9 +753,8 @@ g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) - @test TB.revert(f, r, c) ≈ m + @test discretize(r) ≈ f(m) + @test discretize(TB.revert(f, r, c)) ≈ m # ------ # TORUS @@ -789,10 +764,9 @@ g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) @test centroid(r) ≈ f(centroid(g)) - @test TB.revert(f, r, c) ≈ m + @test discretize(TB.revert(f, r, c)) ≈ m # --------- # TRIANGLE @@ -1149,7 +1123,6 @@ f = Proj(Polar) g = Box(cart(0, 0), cart(1, 1)) r, c = TB.apply(f, g) - @test r isa Box @test r ≈ Box(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) # --------- @@ -1171,15 +1144,6 @@ r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) - # ------ - # PLANE - # ------ - - f = Proj(Cylindrical) - g = Plane(cart(1, 1, 1), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Plane(Point(Cylindrical(T(√2), T(π / 4), T(1))), vector(0, 0, 1)) - # --------- # CYLINDER # --------- @@ -1228,7 +1192,6 @@ f = Proj(Polar) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa SimpleMesh @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) # --------------- @@ -1238,7 +1201,6 @@ f = Proj(Polar) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa SimpleMesh @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) # ----------- @@ -1490,8 +1452,7 @@ g = Ellipsoid(T.((1, 2, 3))) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ----- # DISK @@ -1501,8 +1462,7 @@ g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ------- # CIRCLE @@ -1512,8 +1472,7 @@ g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ---------------- # CYLINDERSURFACE @@ -1523,8 +1482,7 @@ g = CylinderSurface(T(1)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ------------ # CONESURFACE @@ -1535,8 +1493,7 @@ g = ConeSurface(Disk(p, T(2)), cart(0, 0, 1)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # --------------- # FRUSTUMSURFACE @@ -1548,8 +1505,7 @@ g = FrustumSurface(Disk(pb, T(1)), Disk(pt, T(2))) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ------------------ # PARABOLOIDSURFACE @@ -1559,8 +1515,7 @@ g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # ------ # TORUS @@ -1570,8 +1525,7 @@ g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) m = discretize(g) r, c = TB.apply(f, g) - @test r isa SimpleMesh - @test r ≈ f(m) + @test discretize(r) ≈ f(m) # --------- # TRIANGLE From 2d14f4b2059bed0da2985a2f67109b7ecc75d1da Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:28:38 -0300 Subject: [PATCH 275/423] Update `Proj` implementation (#1007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update 'Proj' implementation * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Fix ambiguities * Fix tests * Apply suggestions * Update comments --------- Co-authored-by: Júlio Hoffimann --- src/Meshes.jl | 1 + src/transforms/proj.jl | 20 ++++++++++++++++++-- test/meshes.jl | 2 +- test/transforms.jl | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index bfc94e80a..2989165b7 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -20,6 +20,7 @@ using Distances: Haversine, SphericalAngle using Distances: evaluate, result_type using Rotations: Rotation, QuatRotation, Angle2d using Rotations: rotation_between +using CoordRefSystems: Basic, Projected, Geographic using NearestNeighbors: KDTree, BallTree using NearestNeighbors: knn, inrange using DelaunayTriangulation: triangulate, voronoi diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 36eb2a484..a8e6bd03c 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -29,7 +29,15 @@ Proj(code::Type{<:ESRI}) = Proj{CoordRefSystems.get(code)}() parameters(::Proj{CRS}) where {CRS} = (; CRS) -applycoord(::Proj{CRS}, p::Point) where {CRS} = Point(convert(CRS, coords(p))) +# convert the CRS and preserve the manifold +applycoord(::Proj{CRS}, p::Point{<:🌐}) where {CRS<:Basic} = Point{🌐}(convert(CRS, coords(p))) + +# convert the CRS and (possibly) change the manifold +applycoord(::Proj{CRS}, p::Point{<:🌐}) where {CRS<:Projected} = _proj(CRS, p) +applycoord(::Proj{CRS}, p::Point{<:🌐}) where {CRS<:Geographic} = _proj(CRS, p) +applycoord(::Proj{CRS}, p::Point{<:𝔼}) where {CRS<:Basic} = _proj(CRS, p) +applycoord(::Proj{CRS}, p::Point{<:𝔼}) where {CRS<:Projected} = _proj(CRS, p) +applycoord(::Proj{CRS}, p::Point{<:𝔼}) where {CRS<:Geographic} = _proj(CRS, p) applycoord(::Proj, v::Vec) = v @@ -37,7 +45,9 @@ applycoord(::Proj, v::Vec) = v # SPECIAL CASES # -------------- -applycoord(t::Proj, g::Primitive) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Projected}, g::Primitive{<:🌐}) = TransformedGeometry(g, t) + +applycoord(t::Proj{<:Geographic}, g::Primitive{<:𝔼}) = TransformedGeometry(g, t) applycoord(t::Proj, g::RectilinearGrid) = applycoord(t, convert(SimpleMesh, g)) @@ -54,3 +64,9 @@ function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS}) where {CRS} println(io) print(io, "└─ CRS: $CRS") end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_proj(CRS, p) = Point(convert(CRS, coords(p))) diff --git a/test/meshes.jl b/test/meshes.jl index a32d2b2d8..75a275961 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -726,7 +726,7 @@ mesh = SimpleMesh(points, connec) trans = Proj(Cartesian) tmesh = Meshes.TransformedMesh(mesh, trans) - @test manifold(tmesh) === 𝔼{3} + @test manifold(tmesh) === 🌐 @test crs(tmesh) <: Cartesian trans = Proj(Polar) tgrid = Meshes.TransformedMesh(grid, trans) diff --git a/test/transforms.jl b/test/transforms.jl index b051e49eb..dbc7148ba 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1107,6 +1107,20 @@ r, c = TB.apply(f, g) @test r ≈ Point(Polar(T(√2), T(π / 4))) + # change the manifold + f = Proj(Mercator) + g = latlon(0, 0) + r, c = TB.apply(f, g) + @test manifold(r) === 𝔼{2} + @test r ≈ merc(0, 0) + + # preserve the manifold + f = Proj(Cartesian) + g = latlon(0, 0) + r, c = TB.apply(f, g) + @test manifold(r) === 🌐 + @test r ≈ cart(6378137.0, 0, 0) + # -------- # SEGMENT # -------- @@ -1213,6 +1227,20 @@ d = SimpleMesh(p, c) r, c = TB.apply(f, d) @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # -------------- + # SPECIAL CASES + # -------------- + + f = Proj(Mercator) + g = Box(latlon(0, 180), latlon(45, 90)) + r, c = TB.apply(f, g) + @test manifold(r) === 𝔼{2} + + f = Proj(LatLon) + g = Box(merc(0, 0), merc(1, 1)) + r, c = TB.apply(f, g) + @test manifold(r) === 🌐 end @testset "LengthUnit" begin From 04a31f792fda50ae866d6c52c30dfd0c2636fb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 16 Aug 2024 13:22:37 -0300 Subject: [PATCH 276/423] Optimize topological relations (#1006) * Refactor refinement algorithms to support tuples * Return tuples in Boundary relation * Optimize Adjacency * More adjustments * Return tuple in Adjacency relation * Optimize Coboundary * Return tuple in Coboundary --------- Co-authored-by: Elias Carvalho --- ext/mesh.jl | 4 +- src/matrices.jl | 34 +- src/refinement/catmullclark.jl | 37 +- src/refinement/quad.jl | 22 +- src/refinement/tri.jl | 13 +- src/refinement/trisubdivision.jl | 4 +- src/topologies/grid.jl | 4 +- src/toporelations/adjacency.jl | 31 +- src/toporelations/boundary.jl | 34 +- src/toporelations/coboundary.jl | 32 +- test/toporelations.jl | 806 +++++++++++++++---------------- 11 files changed, 520 insertions(+), 501 deletions(-) diff --git a/ext/mesh.jl b/ext/mesh.jl index 64779bfbd..9c4a54b73 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -163,7 +163,9 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) # interleaved with a sentinel index inds = Int[] for i in 1:nfacets(t) - append!(inds, ∂(i)) + for j in ∂(i) + push!(inds, j) + end push!(inds, nvert + 1) end diff --git a/src/matrices.jl b/src/matrices.jl index 864681498..1f525e080 100644 --- a/src/matrices.jl +++ b/src/matrices.jl @@ -17,8 +17,10 @@ Optionally, specify the `kind` of discretization. ## Available discretizations -* `:uniform` - `Lᵢⱼ = 1 / |𝒩(i)|, ∀j ∈ 𝒩(i)` -* `:cotangent` - `Lᵢⱼ = cot(αᵢⱼ) + cot(βᵢⱼ), ∀j ∈ 𝒩(i)` +* `:uniform` - `Lᵢⱼ = 1 / |𝒜(i)|, ∀j ∈ 𝒜(i)` +* `:cotangent` - `Lᵢⱼ = cot(αᵢⱼ) + cot(βᵢⱼ), ∀j ∈ 𝒜(i)` + +where `𝒜(i)` is the adjacency relation at vertex `i`. ## References @@ -38,7 +40,7 @@ function laplacematrix(mesh; kind=nothing) 𝒯 = adjusttopo(topology(mesh)) # retrieve adjacency relation - 𝒩 = Adjacency{0}(𝒯) + 𝒜 = Adjacency{0}(𝒯) # initialize matrix n = nvertices(mesh) @@ -46,18 +48,18 @@ function laplacematrix(mesh; kind=nothing) # fill matrix if 𝒦 == :uniform - uniformlaplacian!(L, 𝒩) + uniformlaplacian!(L, 𝒜) elseif 𝒦 == :cotangent - cotangentlaplacian!(L, 𝒩, vertices(mesh)) + cotangentlaplacian!(L, 𝒜, vertices(mesh)) end L end -function uniformlaplacian!(L, 𝒩) +function uniformlaplacian!(L, 𝒜) n = size(L, 1) for i in 1:n - js = 𝒩(i) + js = 𝒜(i) for j in js L[i, j] = 1 / length(js) end @@ -65,19 +67,22 @@ function uniformlaplacian!(L, 𝒩) end end -function cotangentlaplacian!(L, 𝒩, v) +function cotangentlaplacian!(L, 𝒜, v) n = size(L, 1) for i in 1:n - js = CircularVector(𝒩(i)) - for k in 1:length(js) - j₋, j, j₊ = js[k - 1], js[k], js[k + 1] + js = 𝒜(i) + m = length(js) + for k in 1:m + j₋ = js[mod1(k - 1, m)] + j = js[mod1(k, m)] + j₊ = js[mod1(k + 1, m)] vᵢ, vⱼ = v[i], v[j] v₋, v₊ = v[j₋], v[j₊] αᵢⱼ = ∠(vⱼ, v₋, vᵢ) βᵢⱼ = ∠(vᵢ, v₊, vⱼ) L[i, j] = cot(αᵢⱼ) + cot(βᵢⱼ) end - L[i, i] = -sum(L[i, js]) + L[i, i] = -sum(j -> L[i, j], js) end end @@ -100,7 +105,7 @@ function measurematrix(mesh) D = paramdim(mesh) # retrieve coboundary relation - ∂ = Coboundary{0,D}(𝒯) + 𝒞 = Coboundary{0,D}(𝒯) # pre-compute all measures A = measure.(mesh) @@ -111,7 +116,8 @@ function measurematrix(mesh) # fill matrix for i in 1:n - Aᵢ = sum(A[∂(i)]) / 3 + js = 𝒞(i) + Aᵢ = sum(j -> A[j], js) / 3 M[i, i] = 2Aᵢ end diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index 70969890b..c02a6c466 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -32,8 +32,8 @@ function refine(mesh, ::CatmullClark) # add centroids of elements ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem - ps = view(points, ∂₂₀(elem)) - cₒ = sum(to, ps) / length(ps) + is = ∂₂₀(elem) + cₒ = sum(j -> to(points[j]), is) / length(is) withcrs(mesh, cₒ) end @@ -41,11 +41,11 @@ function refine(mesh, ::CatmullClark) ∂₁₂ = Coboundary{1,2}(t) ∂₁₀ = Boundary{1,0}(t) fpts = map(1:nfacets(t)) do edge - ps = view(epts, ∂₁₂(edge)) - qs = view(points, ∂₁₀(edge)) - ∑p = sum(to, ps) - ∑q = sum(to, qs) - M = length(ps) + length(qs) + is = ∂₁₂(edge) + js = ∂₁₀(edge) + ∑p = sum(i -> to(epts[i]), is) + ∑q = sum(j -> to(points[j]), js) + M = length(is) + length(js) withcrs(mesh, (∑p + ∑q) / M) end @@ -57,16 +57,13 @@ function refine(mesh, ::CatmullClark) P = to(points[u]) # average of centroids - ps = view(epts, ∂₀₂(u)) - F = sum(to, ps) / length(ps) + is = ∂₀₂(u) + F = sum(i -> to(epts[i]), is) / length(is) # average of midpoints vs = ∂₀₀(u) n = length(vs) - R = sum(vs) do v - uv = view(points, [u, v]) - sum(to, uv) / 2 - end / n + R = sum(v -> to(points[u]) + to(points[v]), vs) / 2n withcrs(mesh, (F + 2R + (n - 3)P) / n) end @@ -81,13 +78,15 @@ function refine(mesh, ::CatmullClark) ∂₂₁ = Boundary{2,1}(t) newconnec = Connectivity{Quadrangle,4}[] for elem in 1:nelements(t) - verts = CircularVector(∂₂₀(elem)) - edges = CircularVector(∂₂₁(elem)) - for i in 1:length(edges) + verts = ∂₂₀(elem) + edges = ∂₂₁(elem) + nv = length(verts) + ne = length(edges) + for i in 1:ne u = elem + offset₁ - v = edges[i] + offset₂ - w = verts[i + 1] - z = edges[i + 1] + offset₂ + v = edges[mod1(i, ne)] + offset₂ + w = verts[mod1(i + 1, nv)] + z = edges[mod1(i + 1, ne)] + offset₂ quad = connect((u, v, w, z)) push!(newconnec, quad) end diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index bc6cfc6e3..4b93ad933 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -21,16 +21,16 @@ function refine(mesh, ::QuadRefinement) # add centroids of elements ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem - ps = view(points, ∂₂₀(elem)) - cₒ = sum(to, ps) / length(ps) + is = ∂₂₀(elem) + cₒ = sum(j -> to(points[j]), is) / length(is) withcrs(mesh, cₒ) end # add midpoints of edges ∂₁₀ = Boundary{1,0}(t) fpts = map(1:nfacets(t)) do edge - ps = view(points, ∂₁₀(edge)) - cₒ = sum(to, ps) / length(ps) + is = ∂₁₀(edge) + cₒ = sum(i -> to(points[i]), is) / length(is) withcrs(mesh, cₒ) end @@ -47,13 +47,15 @@ function refine(mesh, ::QuadRefinement) ∂₂₁ = Boundary{2,1}(t) newconnec = Connectivity{Quadrangle,4}[] for elem in 1:nelements(t) - verts = CircularVector(∂₂₀(elem)) - edges = CircularVector(∂₂₁(elem)) - for i in 1:length(edges) + verts = ∂₂₀(elem) + edges = ∂₂₁(elem) + nv = length(verts) + ne = length(edges) + for i in 1:ne u = elem + offset₁ - v = edges[i] + offset₂ - w = verts[i + 1] - z = edges[i + 1] + offset₂ + v = edges[mod1(i, ne)] + offset₂ + w = verts[mod1(i + 1, nv)] + z = edges[mod1(i + 1, ne)] + offset₂ quad = connect((u, v, w, z)) push!(newconnec, quad) end diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 87ab62e7e..235f09c69 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -24,8 +24,8 @@ function refine(mesh, ::TriRefinement) # add centroids of elements ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem - ps = view(points, ∂₂₀(elem)) - cₒ = sum(to, ps) / length(ps) + is = ∂₂₀(elem) + cₒ = sum(i -> to(points[i]), is) / length(is) withcrs(mesh, cₒ) end @@ -40,11 +40,12 @@ function refine(mesh, ::TriRefinement) # connect vertices into new triangles newconnec = Connectivity{Triangle,3}[] for elem in 1:nelements(t) - verts = CircularVector(∂₂₀(elem)) - for i in 1:length(verts) + verts = ∂₂₀(elem) + nv = length(verts) + for i in 1:nv u = elem + offset - v = verts[i] - w = verts[i + 1] + v = verts[mod1(i, nv)] + w = verts[mod1(i + 1, nv)] tri = connect((u, v, w)) push!(newconnec, tri) end diff --git a/src/refinement/trisubdivision.jl b/src/refinement/trisubdivision.jl index 01c0fabdb..f7d8692ca 100644 --- a/src/refinement/trisubdivision.jl +++ b/src/refinement/trisubdivision.jl @@ -36,10 +36,10 @@ function refine(mesh, ::TriSubdivision) midpoints = Dict{Tuple{Int,Int},Int}() ∂₁₀ = Boundary{1,0}(t) for eind in 1:nfacets(t) - i, j = sort(∂₁₀(eind)) + i, j = ∂₁₀(eind) edge = Segment(points[i], points[j]) push!(points, centroid(edge)) - midpoints[(i, j)] = (np += 1) + midpoints[_ordered(i, j)] = (np += 1) end # construct subtriangles of faces diff --git a/src/topologies/grid.jl b/src/topologies/grid.jl index 2408e5f5c..98da6005e 100644 --- a/src/topologies/grid.jl +++ b/src/topologies/grid.jl @@ -120,7 +120,7 @@ nvertices(t::GridTopology) = prod(t.dims .+ t.open) function element(t::GridTopology{D}, ind) where {D} ∂ = Boundary{D,0}(t) T = elementtype(t) - connect(Tuple(∂(ind)), T) + connect(∂(ind), T) end nelements(t::GridTopology) = prod(t.dims) @@ -128,7 +128,7 @@ nelements(t::GridTopology) = prod(t.dims) function facet(t::GridTopology{D}, ind) where {D} ∂ = Boundary{D - 1,0}(t) T = facettype(t) - connect(Tuple(∂(ind)), T) + connect(∂(ind), T) end nfacets(t::GridTopology{1}) = t.dims[1] + t.open[1] diff --git a/src/toporelations/adjacency.jl b/src/toporelations/adjacency.jl index 04ee5ea80..26bbd75fa 100644 --- a/src/toporelations/adjacency.jl +++ b/src/toporelations/adjacency.jl @@ -34,6 +34,7 @@ function (𝒜::Adjacency{0,D,T})(ind::Integer) where {D,T<:GridTopology} # construct topology for vertices vtopo = GridTopology(dims .+ 1, cycl) 𝒜vert = Adjacency{D}(vtopo) + 𝒜vert(ind) end @@ -44,25 +45,25 @@ function (𝒜::Adjacency{D,D,T})(ind::Integer) where {D,T<:GridTopology} cycl = isperiodic(topo) cind = elem2cart(topo, ind) - # offsets along each dimension - offsets = [ntuple(i -> i == d ? s : 0, D) for d in 1:D for s in (-1, 1)] + inds = Int[] + for d in 1:D, s in (-1, 1) + # offset along each dimension + offset = ntuple(i -> i == d ? s : 0, D) - ninds = NTuple{D,Int}[] - for offset in offsets # apply offset to center index sind = cind .+ offset # wrap indices in case of periodic dimension - wrap(i) = mod1(sind[i], dims[i]) - wind = ntuple(i -> cycl[i] ? wrap(i) : sind[i], D) + wind = ntuple(D) do i + cycl[i] ? mod1(sind[i], dims[i]) : sind[i] + end # discard invalid indices valid(i) = 1 ≤ wind[i] ≤ dims[i] - all(valid, 1:D) && push!(ninds, wind) + all(valid, 1:D) && push!(inds, cart2elem(topo, wind...)) end - # return linear index of element - [cart2elem(topo, ind...) for ind in ninds] + ntuple(i -> inds[i], length(inds)) end # ------------------- @@ -74,13 +75,13 @@ function (𝒜::Adjacency{0,2,T})(vert::Integer) where {T<:HalfEdgeTopology} e = half4vert(𝒜.topology, vert) # initialize result - vertices = [e.half.head] + inds = [e.half.head] # search in CCW orientation p = e.prev h = p.half while !isnothing(h.elem) && h != e - push!(vertices, p.head) + push!(inds, p.head) p = h.prev h = p.half end @@ -88,18 +89,18 @@ function (𝒜::Adjacency{0,2,T})(vert::Integer) where {T<:HalfEdgeTopology} # if border edge is hit if isnothing(h.elem) # add last arm manually - push!(vertices, p.head) + push!(inds, p.head) # search in CW orientation h = e.half while !isnothing(h.elem) n = h.next h = n.half - pushfirst!(vertices, h.head) + pushfirst!(inds, h.head) end end - vertices + ntuple(i -> inds[i], length(inds)) end # adjacent elements in a 2D half-edge topology @@ -117,5 +118,5 @@ function (𝒜::Adjacency{2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} n = n.next end - inds + ntuple(i -> inds[i], length(inds)) end diff --git a/src/toporelations/boundary.jl b/src/toporelations/boundary.jl index da36718de..e4b71d369 100644 --- a/src/toporelations/boundary.jl +++ b/src/toporelations/boundary.jl @@ -63,7 +63,7 @@ function (∂::Boundary{3,2,3,T})(ind::Integer) where {T<:GridTopology} i5 += oz i6 += oz - [i1, i2, i3, i4, i5, i6] + (i1, i2, i3, i4, i5, i6) end # vertices of hexahedron on 3D grid @@ -85,12 +85,13 @@ function (∂::Boundary{3,0,3,T})(ind::Integer) where {T<:GridTopology} i6 = cart2corner(t, i₊, j, k₊) i7 = cart2corner(t, i₊, j₊, k₊) i8 = cart2corner(t, i, j₊, k₊) - [i1, i2, i3, i4, i5, i6, i7, i8] + + (i1, i2, i3, i4, i5, i6, i7, i8) end # vertices of quadrangle on 3D grid function (∂::Boundary{2,0,3,T})(ind::Integer) where {T<:GridTopology} - @error "not implemented" + throw(ErrorException("not implemented")) end # segments making up quadrangles in 2D grid @@ -122,7 +123,7 @@ function (∂::Boundary{2,1,2,T})(ind::Integer) where {T<:GridTopology} i3 += oy i4 += oy - [i1, i2, i3, i4] + (i1, i2, i3, i4) end # vertices of quadrangle on 2D grid @@ -139,7 +140,8 @@ function (∂::Boundary{2,0,2,T})(ind::Integer) where {T<:GridTopology} i2 = cart2corner(t, i₊, j) i3 = cart2corner(t, i₊, j₊) i4 = cart2corner(t, i, j₊) - [i1, i2, i3, i4] + + (i1, i2, i3, i4) end # vertices of segment on 2D grid @@ -164,7 +166,7 @@ function (∂::Boundary{1,0,2,T})(ind::Integer) where {T<:GridTopology} i2 = cart2corner(t, i₊, j) end - [i1, i2] + (i1, i2) end # vertices of segment on 1D grid @@ -176,7 +178,7 @@ function (∂::Boundary{1,0,1,T})(ind::Integer) where {T<:GridTopology} i1 = ind i2 = c ? mod1(ind + 1, n) : ind + 1 - [i1, i2] + (i1, i2) end # ------------------- @@ -186,17 +188,23 @@ end function (∂::Boundary{2,1,2,T})(elem::Integer) where {T<:HalfEdgeTopology} t = ∂.topology l = loop(half4elem(t, elem)) - v = CircularVector(l) - [edge4pair(t, (v[i], v[i + 1])) for i in 1:length(v)] + n = length(l) + ntuple(n) do i + edge4pair(t, (l[mod1(i, n)], l[mod1(i + 1, n)])) + end end function (∂::Boundary{2,0,2,T})(elem::Integer) where {T<:HalfEdgeTopology} - loop(half4elem(∂.topology, elem)) + t = ∂.topology + l = loop(half4elem(t, elem)) + n = length(l) + ntuple(i -> l[i], n) end function (∂::Boundary{1,0,2,T})(edge::Integer) where {T<:HalfEdgeTopology} - e = half4edge(∂.topology, edge) - [e.head, e.half.head] + t = ∂.topology + e = half4edge(t, edge) + (e.head, e.half.head) end # ---------------- @@ -204,5 +212,5 @@ end # ---------------- function (∂::Boundary{D,0,D,T})(ind::Integer) where {D,T<:SimpleTopology} - collect(connec4elem(∂.topology, ind)) + connec4elem(∂.topology, ind) end diff --git a/src/toporelations/coboundary.jl b/src/toporelations/coboundary.jl index 6e5014e21..9d20fe7a2 100644 --- a/src/toporelations/coboundary.jl +++ b/src/toporelations/coboundary.jl @@ -32,25 +32,22 @@ function (𝒞::Coboundary{0,D,D,T})(ind::Integer) where {D,T<:GridTopology} cycl = isperiodic(topo) cind = corner2cart(topo, ind) - # offsets along each dimension - offsets = CartesianIndices(ntuple(i -> -1:0, D)) - - ninds = NTuple{D,Int}[] - for offset in offsets + inds = Int[] + for offset in CartesianIndices(ntuple(i -> -1:0, D)) # apply offset to center index sind = cind .+ Tuple(offset) # wrap indices in case of periodic dimension - wrap(i) = mod1(sind[i], dims[i]) - wind = ntuple(i -> cycl[i] ? wrap(i) : sind[i], D) + wind = ntuple(D) do i + cycl[i] ? mod1(sind[i], dims[i]) : sind[i] + end # discard invalid indices valid(i) = 1 ≤ wind[i] ≤ dims[i] - all(valid, 1:D) && push!(ninds, wind) + all(valid, 1:D) && push!(inds, cart2elem(topo, wind...)) end - # return linear index of element - [cart2elem(topo, ind...) for ind in ninds] + ntuple(i -> inds[i], length(inds)) end # ------------------- @@ -61,7 +58,10 @@ end function (𝒞::Coboundary{0,1,2,T})(ind::Integer) where {T<:HalfEdgeTopology} t = 𝒞.topology 𝒜 = Adjacency{0}(t) - [edge4pair(t, (ind, other)) for other in 𝒜(ind)] + o = 𝒜(ind) + ntuple(length(o)) do i + edge4pair(t, (ind, o[i])) + end end # elements sharing a vertex in 2D mesh @@ -69,13 +69,13 @@ function (𝒞::Coboundary{0,2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} e = half4vert(𝒞.topology, ind) # initialize result - elements = [e.elem] + inds = [e.elem] # search in CCW orientation p = e.prev h = p.half while !isnothing(h.elem) && h != e - push!(elements, h.elem) + push!(inds, h.elem) p = h.prev h = p.half end @@ -85,17 +85,17 @@ function (𝒞::Coboundary{0,2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} # search in CW orientation h = e.half while !isnothing(h.elem) - pushfirst!(elements, h.elem) + pushfirst!(inds, h.elem) n = h.next h = n.half end end - elements + ntuple(i -> inds[i], length(inds)) end # elements sharing a segment in 2D mesh function (𝒞::Coboundary{1,2,2,T})(ind::Integer) where {T<:HalfEdgeTopology} e = half4edge(𝒞.topology, ind) - isnothing(e.half.elem) ? [e.elem] : [e.elem, e.half.elem] + isnothing(e.half.elem) ? (e.elem,) : (e.elem, e.half.elem) end diff --git a/test/toporelations.jl b/test/toporelations.jl index f27169846..a398f775f 100644 --- a/test/toporelations.jl +++ b/test/toporelations.jl @@ -3,434 +3,434 @@ # 3 grid t = GridTopology(3) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 2] - @test ∂(2) == [2, 3] - @test ∂(3) == [3, 4] + @test ∂(1) == (1, 2) + @test ∂(2) == (2, 3) + @test ∂(3) == (3, 4) 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [1, 2] - @test 𝒞(3) == [2, 3] - @test 𝒞(4) == [3] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2, 3) + @test 𝒞(4) == (3,) 𝒜 = Adjacency{1}(t) - @test 𝒜(1) == [2] - @test 𝒜(2) == [1, 3] - @test 𝒜(3) == [2] + @test 𝒜(1) == (2,) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2,) 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == [2] - @test 𝒜(2) == [1, 3] - @test 𝒜(3) == [2, 4] - @test 𝒜(4) == [3] + @test 𝒜(1) == (2,) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2, 4) + @test 𝒜(4) == (3,) # 2x3 grid t = GridTopology(2, 3) ∂ = Boundary{2,0}(t) - @test ∂(1) == [1, 2, 5, 4] - @test ∂(2) == [2, 3, 6, 5] - @test ∂(3) == [4, 5, 8, 7] - @test ∂(4) == [5, 6, 9, 8] - @test ∂(5) == [7, 8, 11, 10] - @test ∂(6) == [8, 9, 12, 11] + @test ∂(1) == (1, 2, 5, 4) + @test ∂(2) == (2, 3, 6, 5) + @test ∂(3) == (4, 5, 8, 7) + @test ∂(4) == (5, 6, 9, 8) + @test ∂(5) == (7, 8, 11, 10) + @test ∂(6) == (8, 9, 12, 11) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [1, 2] - @test 𝒞(3) == [2] - @test 𝒞(4) == [1, 3] - @test 𝒞(5) == [1, 2, 3, 4] - @test 𝒞(6) == [2, 4] - @test 𝒞(7) == [3, 5] - @test 𝒞(8) == [3, 4, 5, 6] - @test 𝒞(9) == [4, 6] - @test 𝒞(10) == [5] - @test 𝒞(11) == [5, 6] - @test 𝒞(12) == [6] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3, 5) + @test 𝒞(8) == (3, 4, 5, 6) + @test 𝒞(9) == (4, 6) + @test 𝒞(10) == (5,) + @test 𝒞(11) == (5, 6) + @test 𝒞(12) == (6,) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 4] - @test ∂(2) == [2, 5] - @test ∂(3) == [3, 6] - @test ∂(4) == [4, 7] - @test ∂(5) == [5, 8] - @test ∂(6) == [6, 9] - @test ∂(7) == [7, 10] - @test ∂(8) == [8, 11] - @test ∂(9) == [9, 12] - @test ∂(10) == [1, 2] - @test ∂(11) == [4, 5] - @test ∂(12) == [7, 8] - @test ∂(13) == [10, 11] - @test ∂(14) == [2, 3] - @test ∂(15) == [5, 6] - @test ∂(16) == [8, 9] - @test ∂(17) == [11, 12] + @test ∂(1) == (1, 4) + @test ∂(2) == (2, 5) + @test ∂(3) == (3, 6) + @test ∂(4) == (4, 7) + @test ∂(5) == (5, 8) + @test ∂(6) == (6, 9) + @test ∂(7) == (7, 10) + @test ∂(8) == (8, 11) + @test ∂(9) == (9, 12) + @test ∂(10) == (1, 2) + @test ∂(11) == (4, 5) + @test ∂(12) == (7, 8) + @test ∂(13) == (10, 11) + @test ∂(14) == (2, 3) + @test ∂(15) == (5, 6) + @test ∂(16) == (8, 9) + @test ∂(17) == (11, 12) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [2, 3] - @test 𝒜(2) == [1, 4] - @test 𝒜(3) == [4, 1, 5] - @test 𝒜(4) == [3, 2, 6] - @test 𝒜(5) == [6, 3] - @test 𝒜(6) == [5, 4] + @test 𝒜(1) == (2, 3) + @test 𝒜(2) == (1, 4) + @test 𝒜(3) == (4, 1, 5) + @test 𝒜(4) == (3, 2, 6) + @test 𝒜(5) == (6, 3) + @test 𝒜(6) == (5, 4) # 2x2 grid t = GridTopology(2, 2) ∂ = Boundary{2,0}(t) - @test ∂(1) == [1, 2, 5, 4] - @test ∂(2) == [2, 3, 6, 5] - @test ∂(3) == [4, 5, 8, 7] - @test ∂(4) == [5, 6, 9, 8] + @test ∂(1) == (1, 2, 5, 4) + @test ∂(2) == (2, 3, 6, 5) + @test ∂(3) == (4, 5, 8, 7) + @test ∂(4) == (5, 6, 9, 8) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [1, 2] - @test 𝒞(3) == [2] - @test 𝒞(4) == [1, 3] - @test 𝒞(5) == [1, 2, 3, 4] - @test 𝒞(6) == [2, 4] - @test 𝒞(7) == [3] - @test 𝒞(8) == [3, 4] - @test 𝒞(9) == [4] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (4,) 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == [2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 6] - @test 𝒜(4) == [5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 3, 9] - @test 𝒜(7) == [8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 6] + @test 𝒜(1) == (2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 6) + @test 𝒜(4) == (5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 3, 9) + @test 𝒜(7) == (8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 6) # 2x2 (periodic x aperiodic) grid t = GridTopology((2, 2), (true, false)) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 3] - @test ∂(2) == [2, 4] - @test ∂(3) == [3, 5] - @test ∂(4) == [4, 6] - @test ∂(5) == [1, 2] - @test ∂(6) == [3, 4] - @test ∂(7) == [5, 6] - @test ∂(8) == [2, 1] - @test ∂(9) == [4, 3] - @test ∂(10) == [6, 5] + @test ∂(1) == (1, 3) + @test ∂(2) == (2, 4) + @test ∂(3) == (3, 5) + @test ∂(4) == (4, 6) + @test ∂(5) == (1, 2) + @test ∂(6) == (3, 4) + @test ∂(7) == (5, 6) + @test ∂(8) == (2, 1) + @test ∂(9) == (4, 3) + @test ∂(10) == (6, 5) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [2, 1] - @test 𝒞(2) == [1, 2] - @test 𝒞(3) == [2, 1, 4, 3] - @test 𝒞(4) == [1, 2, 3, 4] - @test 𝒞(5) == [4, 3] - @test 𝒞(6) == [3, 4] + @test 𝒞(1) == (2, 1) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2, 1, 4, 3) + @test 𝒞(4) == (1, 2, 3, 4) + @test 𝒞(5) == (4, 3) + @test 𝒞(6) == (3, 4) # 2x2 (aperiodic x periodic) grid t = GridTopology((2, 2), (false, true)) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 4] - @test ∂(2) == [2, 5] - @test ∂(3) == [3, 6] - @test ∂(4) == [4, 1] - @test ∂(5) == [5, 2] - @test ∂(6) == [6, 3] - @test ∂(7) == [1, 2] - @test ∂(8) == [4, 5] - @test ∂(9) == [2, 3] - @test ∂(10) == [5, 6] + @test ∂(1) == (1, 4) + @test ∂(2) == (2, 5) + @test ∂(3) == (3, 6) + @test ∂(4) == (4, 1) + @test ∂(5) == (5, 2) + @test ∂(6) == (6, 3) + @test ∂(7) == (1, 2) + @test ∂(8) == (4, 5) + @test ∂(9) == (2, 3) + @test ∂(10) == (5, 6) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [3, 1] - @test 𝒞(2) == [3, 4, 1, 2] - @test 𝒞(3) == [4, 2] - @test 𝒞(4) == [1, 3] - @test 𝒞(5) == [1, 2, 3, 4] - @test 𝒞(6) == [2, 4] + @test 𝒞(1) == (3, 1) + @test 𝒞(2) == (3, 4, 1, 2) + @test 𝒞(3) == (4, 2) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) # 2x2 (periodic x periodic) grid t = GridTopology((2, 2), (true, true)) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 3] - @test ∂(2) == [2, 4] - @test ∂(3) == [3, 1] - @test ∂(4) == [4, 2] - @test ∂(5) == [1, 2] - @test ∂(6) == [3, 4] - @test ∂(7) == [2, 1] - @test ∂(8) == [4, 3] + @test ∂(1) == (1, 3) + @test ∂(2) == (2, 4) + @test ∂(3) == (3, 1) + @test ∂(4) == (4, 2) + @test ∂(5) == (1, 2) + @test ∂(6) == (3, 4) + @test ∂(7) == (2, 1) + @test ∂(8) == (4, 3) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [4, 3, 2, 1] - @test 𝒞(2) == [3, 4, 1, 2] - @test 𝒞(3) == [2, 1, 4, 3] - @test 𝒞(4) == [1, 2, 3, 4] + @test 𝒞(1) == (4, 3, 2, 1) + @test 𝒞(2) == (3, 4, 1, 2) + @test 𝒞(3) == (2, 1, 4, 3) + @test 𝒞(4) == (1, 2, 3, 4) # 3x3 grid t = GridTopology(3, 3) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 6] - @test 𝒜(4) == [5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 3, 9] - @test 𝒜(7) == [8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 6] + @test 𝒜(1) == (2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 6) + @test 𝒜(4) == (5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 3, 9) + @test 𝒜(7) == (8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 6) # 3x3 grid (periodic x aperiodic) grid t = GridTopology((3, 3), (true, false)) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [3, 2, 4] - @test 𝒜(2) == [1, 3, 5] - @test 𝒜(3) == [2, 1, 6] - @test 𝒜(4) == [6, 5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 4, 3, 9] - @test 𝒜(7) == [9, 8, 4] - @test 𝒜(8) == [7, 9, 5] - @test 𝒜(9) == [8, 7, 6] + @test 𝒜(1) == (3, 2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 1, 6) + @test 𝒜(4) == (6, 5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 4, 3, 9) + @test 𝒜(7) == (9, 8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 7, 6) # 3x3 grid (periodic x periodic) grid t = GridTopology((3, 3), (true, true)) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [3, 2, 7, 4] - @test 𝒜(2) == [1, 3, 8, 5] - @test 𝒜(3) == [2, 1, 9, 6] - @test 𝒜(4) == [6, 5, 1, 7] - @test 𝒜(5) == [4, 6, 2, 8] - @test 𝒜(6) == [5, 4, 3, 9] - @test 𝒜(7) == [9, 8, 4, 1] - @test 𝒜(8) == [7, 9, 5, 2] - @test 𝒜(9) == [8, 7, 6, 3] + @test 𝒜(1) == (3, 2, 7, 4) + @test 𝒜(2) == (1, 3, 8, 5) + @test 𝒜(3) == (2, 1, 9, 6) + @test 𝒜(4) == (6, 5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 4, 3, 9) + @test 𝒜(7) == (9, 8, 4, 1) + @test 𝒜(8) == (7, 9, 5, 2) + @test 𝒜(9) == (8, 7, 6, 3) # 3x4 grid t = GridTopology(3, 4) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 17, 18] - @test ∂(2) == [2, 3, 22, 23] - @test ∂(3) == [3, 4, 27, 28] - @test ∂(4) == [5, 6, 18, 19] - @test ∂(5) == [6, 7, 23, 24] - @test ∂(6) == [7, 8, 28, 29] - @test ∂(7) == [9, 10, 19, 20] - @test ∂(8) == [10, 11, 24, 25] - @test ∂(9) == [11, 12, 29, 30] - @test ∂(10) == [13, 14, 20, 21] - @test ∂(11) == [14, 15, 25, 26] - @test ∂(12) == [15, 16, 30, 31] + @test ∂(1) == (1, 2, 17, 18) + @test ∂(2) == (2, 3, 22, 23) + @test ∂(3) == (3, 4, 27, 28) + @test ∂(4) == (5, 6, 18, 19) + @test ∂(5) == (6, 7, 23, 24) + @test ∂(6) == (7, 8, 28, 29) + @test ∂(7) == (9, 10, 19, 20) + @test ∂(8) == (10, 11, 24, 25) + @test ∂(9) == (11, 12, 29, 30) + @test ∂(10) == (13, 14, 20, 21) + @test ∂(11) == (14, 15, 25, 26) + @test ∂(12) == (15, 16, 30, 31) # 3x4 (periodic x aperiodic) grid t = GridTopology((3, 4), (true, false)) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 13, 14] - @test ∂(2) == [2, 3, 18, 19] - @test ∂(3) == [3, 1, 23, 24] - @test ∂(4) == [4, 5, 14, 15] - @test ∂(5) == [5, 6, 19, 20] - @test ∂(6) == [6, 4, 24, 25] - @test ∂(7) == [7, 8, 15, 16] - @test ∂(8) == [8, 9, 20, 21] - @test ∂(9) == [9, 7, 25, 26] - @test ∂(10) == [10, 11, 16, 17] - @test ∂(11) == [11, 12, 21, 22] - @test ∂(12) == [12, 10, 26, 27] + @test ∂(1) == (1, 2, 13, 14) + @test ∂(2) == (2, 3, 18, 19) + @test ∂(3) == (3, 1, 23, 24) + @test ∂(4) == (4, 5, 14, 15) + @test ∂(5) == (5, 6, 19, 20) + @test ∂(6) == (6, 4, 24, 25) + @test ∂(7) == (7, 8, 15, 16) + @test ∂(8) == (8, 9, 20, 21) + @test ∂(9) == (9, 7, 25, 26) + @test ∂(10) == (10, 11, 16, 17) + @test ∂(11) == (11, 12, 21, 22) + @test ∂(12) == (12, 10, 26, 27) # 3x4 (aperiodic x periodic) grid t = GridTopology((3, 4), (false, true)) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 17, 18] - @test ∂(2) == [2, 3, 21, 22] - @test ∂(3) == [3, 4, 25, 26] - @test ∂(4) == [5, 6, 18, 19] - @test ∂(5) == [6, 7, 22, 23] - @test ∂(6) == [7, 8, 26, 27] - @test ∂(7) == [9, 10, 19, 20] - @test ∂(8) == [10, 11, 23, 24] - @test ∂(9) == [11, 12, 27, 28] - @test ∂(10) == [13, 14, 20, 17] - @test ∂(11) == [14, 15, 24, 21] - @test ∂(12) == [15, 16, 28, 25] + @test ∂(1) == (1, 2, 17, 18) + @test ∂(2) == (2, 3, 21, 22) + @test ∂(3) == (3, 4, 25, 26) + @test ∂(4) == (5, 6, 18, 19) + @test ∂(5) == (6, 7, 22, 23) + @test ∂(6) == (7, 8, 26, 27) + @test ∂(7) == (9, 10, 19, 20) + @test ∂(8) == (10, 11, 23, 24) + @test ∂(9) == (11, 12, 27, 28) + @test ∂(10) == (13, 14, 20, 17) + @test ∂(11) == (14, 15, 24, 21) + @test ∂(12) == (15, 16, 28, 25) # 3x4 (periodic x periodic) grid t = GridTopology((3, 4), (true, true)) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 2, 13, 14] - @test ∂(2) == [2, 3, 17, 18] - @test ∂(3) == [3, 1, 21, 22] - @test ∂(4) == [4, 5, 14, 15] - @test ∂(5) == [5, 6, 18, 19] - @test ∂(6) == [6, 4, 22, 23] - @test ∂(7) == [7, 8, 15, 16] - @test ∂(8) == [8, 9, 19, 20] - @test ∂(9) == [9, 7, 23, 24] - @test ∂(10) == [10, 11, 16, 13] - @test ∂(11) == [11, 12, 20, 17] - @test ∂(12) == [12, 10, 24, 21] + @test ∂(1) == (1, 2, 13, 14) + @test ∂(2) == (2, 3, 17, 18) + @test ∂(3) == (3, 1, 21, 22) + @test ∂(4) == (4, 5, 14, 15) + @test ∂(5) == (5, 6, 18, 19) + @test ∂(6) == (6, 4, 22, 23) + @test ∂(7) == (7, 8, 15, 16) + @test ∂(8) == (8, 9, 19, 20) + @test ∂(9) == (9, 7, 23, 24) + @test ∂(10) == (10, 11, 16, 13) + @test ∂(11) == (11, 12, 20, 17) + @test ∂(12) == (12, 10, 24, 21) # 2x2x2 grid t = GridTopology(2, 2, 2) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 13, 14, 25, 26] - @test ∂(2) == [2, 3, 16, 17, 28, 29] - @test ∂(3) == [4, 5, 14, 15, 31, 32] - @test ∂(4) == [5, 6, 17, 18, 34, 35] - @test ∂(5) == [7, 8, 19, 20, 26, 27] - @test ∂(6) == [8, 9, 22, 23, 29, 30] - @test ∂(7) == [10, 11, 20, 21, 32, 33] - @test ∂(8) == [11, 12, 23, 24, 35, 36] + @test ∂(1) == (1, 2, 13, 14, 25, 26) + @test ∂(2) == (2, 3, 16, 17, 28, 29) + @test ∂(3) == (4, 5, 14, 15, 31, 32) + @test ∂(4) == (5, 6, 17, 18, 34, 35) + @test ∂(5) == (7, 8, 19, 20, 26, 27) + @test ∂(6) == (8, 9, 22, 23, 29, 30) + @test ∂(7) == (10, 11, 20, 21, 32, 33) + @test ∂(8) == (11, 12, 23, 24, 35, 36) 𝒞 = Coboundary{0,3}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [1, 2] - @test 𝒞(3) == [2] - @test 𝒞(4) == [1, 3] - @test 𝒞(5) == [1, 2, 3, 4] - @test 𝒞(6) == [2, 4] - @test 𝒞(7) == [3] - @test 𝒞(8) == [3, 4] - @test 𝒞(9) == [4] - @test 𝒞(10) == [1, 5] - @test 𝒞(11) == [1, 2, 5, 6] - @test 𝒞(12) == [2, 6] - @test 𝒞(13) == [1, 3, 5, 7] - @test 𝒞(14) == [1, 2, 3, 4, 5, 6, 7, 8] - @test 𝒞(15) == [2, 4, 6, 8] - @test 𝒞(16) == [3, 7] - @test 𝒞(17) == [3, 4, 7, 8] - @test 𝒞(18) == [4, 8] - @test 𝒞(19) == [5] - @test 𝒞(20) == [5, 6] - @test 𝒞(21) == [6] - @test 𝒞(22) == [5, 7] - @test 𝒞(23) == [5, 6, 7, 8] - @test 𝒞(24) == [6, 8] - @test 𝒞(25) == [7] - @test 𝒞(26) == [7, 8] - @test 𝒞(27) == [8] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (4,) + @test 𝒞(10) == (1, 5) + @test 𝒞(11) == (1, 2, 5, 6) + @test 𝒞(12) == (2, 6) + @test 𝒞(13) == (1, 3, 5, 7) + @test 𝒞(14) == (1, 2, 3, 4, 5, 6, 7, 8) + @test 𝒞(15) == (2, 4, 6, 8) + @test 𝒞(16) == (3, 7) + @test 𝒞(17) == (3, 4, 7, 8) + @test 𝒞(18) == (4, 8) + @test 𝒞(19) == (5,) + @test 𝒞(20) == (5, 6) + @test 𝒞(21) == (6,) + @test 𝒞(22) == (5, 7) + @test 𝒞(23) == (5, 6, 7, 8) + @test 𝒞(24) == (6, 8) + @test 𝒞(25) == (7,) + @test 𝒞(26) == (7, 8) + @test 𝒞(27) == (8,) 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == [2, 3, 5] - @test 𝒜(2) == [1, 4, 6] - @test 𝒜(3) == [4, 1, 7] - @test 𝒜(4) == [3, 2, 8] - @test 𝒜(5) == [6, 7, 1] - @test 𝒜(6) == [5, 8, 2] - @test 𝒜(7) == [8, 5, 3] - @test 𝒜(8) == [7, 6, 4] + @test 𝒜(1) == (2, 3, 5) + @test 𝒜(2) == (1, 4, 6) + @test 𝒜(3) == (4, 1, 7) + @test 𝒜(4) == (3, 2, 8) + @test 𝒜(5) == (6, 7, 1) + @test 𝒜(6) == (5, 8, 2) + @test 𝒜(7) == (8, 5, 3) + @test 𝒜(8) == (7, 6, 4) # 2x2x2 (periodic x aperiodic x aperiodic) grid t = GridTopology((2, 2, 2), (true, false, false)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 9, 10, 21, 22] - @test ∂(2) == [2, 1, 12, 13, 24, 25] - @test ∂(3) == [3, 4, 10, 11, 27, 28] - @test ∂(4) == [4, 3, 13, 14, 30, 31] - @test ∂(5) == [5, 6, 15, 16, 22, 23] - @test ∂(6) == [6, 5, 18, 19, 25, 26] - @test ∂(7) == [7, 8, 16, 17, 28, 29] - @test ∂(8) == [8, 7, 19, 20, 31, 32] + @test ∂(1) == (1, 2, 9, 10, 21, 22) + @test ∂(2) == (2, 1, 12, 13, 24, 25) + @test ∂(3) == (3, 4, 10, 11, 27, 28) + @test ∂(4) == (4, 3, 13, 14, 30, 31) + @test ∂(5) == (5, 6, 15, 16, 22, 23) + @test ∂(6) == (6, 5, 18, 19, 25, 26) + @test ∂(7) == (7, 8, 16, 17, 28, 29) + @test ∂(8) == (8, 7, 19, 20, 31, 32) # 2x2x2 (aperiodic x periodic x aperiodic) grid t = GridTopology((2, 2, 2), (false, true, false)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 13, 14, 21, 22] - @test ∂(2) == [2, 3, 15, 16, 24, 25] - @test ∂(3) == [4, 5, 14, 13, 27, 28] - @test ∂(4) == [5, 6, 16, 15, 30, 31] - @test ∂(5) == [7, 8, 17, 18, 22, 23] - @test ∂(6) == [8, 9, 19, 20, 25, 26] - @test ∂(7) == [10, 11, 18, 17, 28, 29] - @test ∂(8) == [11, 12, 20, 19, 31, 32] + @test ∂(1) == (1, 2, 13, 14, 21, 22) + @test ∂(2) == (2, 3, 15, 16, 24, 25) + @test ∂(3) == (4, 5, 14, 13, 27, 28) + @test ∂(4) == (5, 6, 16, 15, 30, 31) + @test ∂(5) == (7, 8, 17, 18, 22, 23) + @test ∂(6) == (8, 9, 19, 20, 25, 26) + @test ∂(7) == (10, 11, 18, 17, 28, 29) + @test ∂(8) == (11, 12, 20, 19, 31, 32) # 2x2x2 (aperiodic x aperiodic x periodic) grid t = GridTopology((2, 2, 2), (false, false, true)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 13, 14, 25, 26] - @test ∂(2) == [2, 3, 16, 17, 27, 28] - @test ∂(3) == [4, 5, 14, 15, 29, 30] - @test ∂(4) == [5, 6, 17, 18, 31, 32] - @test ∂(5) == [7, 8, 19, 20, 26, 25] - @test ∂(6) == [8, 9, 22, 23, 28, 27] - @test ∂(7) == [10, 11, 20, 21, 30, 29] - @test ∂(8) == [11, 12, 23, 24, 32, 31] + @test ∂(1) == (1, 2, 13, 14, 25, 26) + @test ∂(2) == (2, 3, 16, 17, 27, 28) + @test ∂(3) == (4, 5, 14, 15, 29, 30) + @test ∂(4) == (5, 6, 17, 18, 31, 32) + @test ∂(5) == (7, 8, 19, 20, 26, 25) + @test ∂(6) == (8, 9, 22, 23, 28, 27) + @test ∂(7) == (10, 11, 20, 21, 30, 29) + @test ∂(8) == (11, 12, 23, 24, 32, 31) # 2x2x2 (periodic x periodic x aperiodic) grid t = GridTopology((2, 2, 2), (true, true, false)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 9, 10, 17, 18] - @test ∂(2) == [2, 1, 11, 12, 20, 21] - @test ∂(3) == [3, 4, 10, 9, 23, 24] - @test ∂(4) == [4, 3, 12, 11, 26, 27] - @test ∂(5) == [5, 6, 13, 14, 18, 19] - @test ∂(6) == [6, 5, 15, 16, 21, 22] - @test ∂(7) == [7, 8, 14, 13, 24, 25] - @test ∂(8) == [8, 7, 16, 15, 27, 28] + @test ∂(1) == (1, 2, 9, 10, 17, 18) + @test ∂(2) == (2, 1, 11, 12, 20, 21) + @test ∂(3) == (3, 4, 10, 9, 23, 24) + @test ∂(4) == (4, 3, 12, 11, 26, 27) + @test ∂(5) == (5, 6, 13, 14, 18, 19) + @test ∂(6) == (6, 5, 15, 16, 21, 22) + @test ∂(7) == (7, 8, 14, 13, 24, 25) + @test ∂(8) == (8, 7, 16, 15, 27, 28) # 2x2x2 (periodic x aperiodic x periodic) grid t = GridTopology((2, 2, 2), (true, false, true)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 9, 10, 21, 22] - @test ∂(2) == [2, 1, 12, 13, 23, 24] - @test ∂(3) == [3, 4, 10, 11, 25, 26] - @test ∂(4) == [4, 3, 13, 14, 27, 28] - @test ∂(5) == [5, 6, 15, 16, 22, 21] - @test ∂(6) == [6, 5, 18, 19, 24, 23] - @test ∂(7) == [7, 8, 16, 17, 26, 25] - @test ∂(8) == [8, 7, 19, 20, 28, 27] + @test ∂(1) == (1, 2, 9, 10, 21, 22) + @test ∂(2) == (2, 1, 12, 13, 23, 24) + @test ∂(3) == (3, 4, 10, 11, 25, 26) + @test ∂(4) == (4, 3, 13, 14, 27, 28) + @test ∂(5) == (5, 6, 15, 16, 22, 21) + @test ∂(6) == (6, 5, 18, 19, 24, 23) + @test ∂(7) == (7, 8, 16, 17, 26, 25) + @test ∂(8) == (8, 7, 19, 20, 28, 27) # 2x2x2 (aperiodic x periodic x periodic) grid t = GridTopology((2, 2, 2), (false, true, true)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 13, 14, 21, 22] - @test ∂(2) == [2, 3, 15, 16, 23, 24] - @test ∂(3) == [4, 5, 14, 13, 25, 26] - @test ∂(4) == [5, 6, 16, 15, 27, 28] - @test ∂(5) == [7, 8, 17, 18, 22, 21] - @test ∂(6) == [8, 9, 19, 20, 24, 23] - @test ∂(7) == [10, 11, 18, 17, 26, 25] - @test ∂(8) == [11, 12, 20, 19, 28, 27] + @test ∂(1) == (1, 2, 13, 14, 21, 22) + @test ∂(2) == (2, 3, 15, 16, 23, 24) + @test ∂(3) == (4, 5, 14, 13, 25, 26) + @test ∂(4) == (5, 6, 16, 15, 27, 28) + @test ∂(5) == (7, 8, 17, 18, 22, 21) + @test ∂(6) == (8, 9, 19, 20, 24, 23) + @test ∂(7) == (10, 11, 18, 17, 26, 25) + @test ∂(8) == (11, 12, 20, 19, 28, 27) # 2x2x2 (periodic x periodic x periodic) grid t = GridTopology((2, 2, 2), (true, true, true)) ∂ = Boundary{3,2}(t) - @test ∂(1) == [1, 2, 9, 10, 17, 18] - @test ∂(2) == [2, 1, 11, 12, 19, 20] - @test ∂(3) == [3, 4, 10, 9, 21, 22] - @test ∂(4) == [4, 3, 12, 11, 23, 24] - @test ∂(5) == [5, 6, 13, 14, 18, 17] - @test ∂(6) == [6, 5, 15, 16, 20, 19] - @test ∂(7) == [7, 8, 14, 13, 22, 21] - @test ∂(8) == [8, 7, 16, 15, 24, 23] + @test ∂(1) == (1, 2, 9, 10, 17, 18) + @test ∂(2) == (2, 1, 11, 12, 19, 20) + @test ∂(3) == (3, 4, 10, 9, 21, 22) + @test ∂(4) == (4, 3, 12, 11, 23, 24) + @test ∂(5) == (5, 6, 13, 14, 18, 17) + @test ∂(6) == (6, 5, 15, 16, 20, 19) + @test ∂(7) == (7, 8, 14, 13, 22, 21) + @test ∂(8) == (8, 7, 16, 15, 24, 23) # 2x3x2 grid t = GridTopology(2, 3, 2) ∂ = Boundary{3,0}(t) - @test ∂(1) == [1, 2, 5, 4, 13, 14, 17, 16] - @test ∂(2) == [2, 3, 6, 5, 14, 15, 18, 17] - @test ∂(3) == [4, 5, 8, 7, 16, 17, 20, 19] - @test ∂(12) == [20, 21, 24, 23, 32, 33, 36, 35] + @test ∂(1) == (1, 2, 5, 4, 13, 14, 17, 16) + @test ∂(2) == (2, 3, 6, 5, 14, 15, 18, 17) + @test ∂(3) == (4, 5, 8, 7, 16, 17, 20, 19) + @test ∂(12) == (20, 21, 24, 23, 32, 33, 36, 35) # 3x2x2 grid t = GridTopology(3, 2, 2) 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == [2, 4, 7] - @test 𝒜(2) == [1, 3, 5, 8] - @test 𝒜(3) == [2, 6, 9] - @test 𝒜(4) == [5, 1, 10] - @test 𝒜(5) == [4, 6, 2, 11] - @test 𝒜(6) == [5, 3, 12] - @test 𝒜(7) == [8, 10, 1] - @test 𝒜(8) == [7, 9, 11, 2] - @test 𝒜(9) == [8, 12, 3] - @test 𝒜(10) == [11, 7, 4] - @test 𝒜(11) == [10, 12, 8, 5] - @test 𝒜(12) == [11, 9, 6] + @test 𝒜(1) == (2, 4, 7) + @test 𝒜(2) == (1, 3, 5, 8) + @test 𝒜(3) == (2, 6, 9) + @test 𝒜(4) == (5, 1, 10) + @test 𝒜(5) == (4, 6, 2, 11) + @test 𝒜(6) == (5, 3, 12) + @test 𝒜(7) == (8, 10, 1) + @test 𝒜(8) == (7, 9, 11, 2) + @test 𝒜(9) == (8, 12, 3) + @test 𝒜(10) == (11, 7, 4) + @test 𝒜(11) == (10, 12, 8, 5) + @test 𝒜(12) == (11, 9, 6) # 3x2x2 (periodic x aperiodic x aperiodic) grid t = GridTopology((3, 2, 2), (true, false, false)) 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == [3, 2, 4, 7] - @test 𝒜(2) == [1, 3, 5, 8] - @test 𝒜(3) == [2, 1, 6, 9] - @test 𝒜(4) == [6, 5, 1, 10] - @test 𝒜(5) == [4, 6, 2, 11] - @test 𝒜(6) == [5, 4, 3, 12] - @test 𝒜(7) == [9, 8, 10, 1] - @test 𝒜(8) == [7, 9, 11, 2] - @test 𝒜(9) == [8, 7, 12, 3] - @test 𝒜(10) == [12, 11, 7, 4] - @test 𝒜(11) == [10, 12, 8, 5] - @test 𝒜(12) == [11, 10, 9, 6] + @test 𝒜(1) == (3, 2, 4, 7) + @test 𝒜(2) == (1, 3, 5, 8) + @test 𝒜(3) == (2, 1, 6, 9) + @test 𝒜(4) == (6, 5, 1, 10) + @test 𝒜(5) == (4, 6, 2, 11) + @test 𝒜(6) == (5, 4, 3, 12) + @test 𝒜(7) == (9, 8, 10, 1) + @test 𝒜(8) == (7, 9, 11, 2) + @test 𝒜(9) == (8, 7, 12, 3) + @test 𝒜(10) == (12, 11, 7, 4) + @test 𝒜(11) == (10, 12, 8, 5) + @test 𝒜(12) == (11, 10, 9, 6) # invalid relations t = GridTopology(2, 3) @@ -446,111 +446,111 @@ elems = connect.([(1, 2, 3), (4, 3, 2)]) t = HalfEdgeTopology(elems) ∂ = Boundary{2,0}(t) - @test ∂(1) == [2, 3, 1] - @test ∂(2) == [3, 2, 4] + @test ∂(1) == (2, 3, 1) + @test ∂(2) == (3, 2, 4) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 3, 2] - @test ∂(2) == [1, 4, 5] + @test ∂(1) == (1, 3, 2) + @test ∂(2) == (1, 4, 5) ∂ = Boundary{1,0}(t) - @test ∂(1) == [3, 2] - @test ∂(2) == [1, 2] - @test ∂(3) == [3, 1] - @test ∂(4) == [2, 4] - @test ∂(5) == [4, 3] + @test ∂(1) == (3, 2) + @test ∂(2) == (1, 2) + @test ∂(3) == (3, 1) + @test ∂(4) == (2, 4) + @test ∂(5) == (4, 3) 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == [2, 3] - @test 𝒞(2) == [4, 1, 2] - @test 𝒞(3) == [3, 1, 5] - @test 𝒞(4) == [5, 4] + @test 𝒞(1) == (2, 3) + @test 𝒞(2) == (4, 1, 2) + @test 𝒞(3) == (3, 1, 5) + @test 𝒞(4) == (5, 4) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [2, 1] - @test 𝒞(3) == [1, 2] - @test 𝒞(4) == [2] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (2, 1) + @test 𝒞(3) == (1, 2) + @test 𝒞(4) == (2,) 𝒞 = Coboundary{1,2}(t) - @test 𝒞(1) == [2, 1] - @test 𝒞(2) == [1] - @test 𝒞(3) == [1] - @test 𝒞(4) == [2] - @test 𝒞(5) == [2] + @test 𝒞(1) == (2, 1) + @test 𝒞(2) == (1,) + @test 𝒞(3) == (1,) + @test 𝒞(4) == (2,) + @test 𝒞(5) == (2,) 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == [2, 3] - @test 𝒜(2) == [4, 3, 1] - @test 𝒜(3) == [1, 2, 4] - @test 𝒜(4) == [3, 2] + @test 𝒜(1) == (2, 3) + @test 𝒜(2) == (4, 3, 1) + @test 𝒜(3) == (1, 2, 4) + @test 𝒜(4) == (3, 2) # 2 triangles + 2 quadrangles elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) t = HalfEdgeTopology(elems) ∂ = Boundary{2,0}(t) - @test ∂(1) == [1, 2, 6, 5] - @test ∂(2) == [6, 2, 4] - @test ∂(3) == [6, 4, 3, 5] - @test ∂(4) == [3, 1, 5] + @test ∂(1) == (1, 2, 6, 5) + @test ∂(2) == (6, 2, 4) + @test ∂(3) == (6, 4, 3, 5) + @test ∂(4) == (3, 1, 5) ∂ = Boundary{2,1}(t) - @test ∂(1) == [1, 3, 5, 6] - @test ∂(2) == [3, 9, 4] - @test ∂(3) == [4, 7, 8, 5] - @test ∂(4) == [2, 6, 8] + @test ∂(1) == (1, 3, 5, 6) + @test ∂(2) == (3, 9, 4) + @test ∂(3) == (4, 7, 8, 5) + @test ∂(4) == (2, 6, 8) ∂ = Boundary{1,0}(t) - @test ∂(1) == [1, 2] - @test ∂(2) == [3, 1] - @test ∂(3) == [6, 2] - @test ∂(4) == [4, 6] - @test ∂(5) == [5, 6] - @test ∂(6) == [1, 5] - @test ∂(7) == [4, 3] - @test ∂(8) == [3, 5] - @test ∂(9) == [2, 4] + @test ∂(1) == (1, 2) + @test ∂(2) == (3, 1) + @test ∂(3) == (6, 2) + @test ∂(4) == (4, 6) + @test ∂(5) == (5, 6) + @test ∂(6) == (1, 5) + @test ∂(7) == (4, 3) + @test ∂(8) == (3, 5) + @test ∂(9) == (2, 4) 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == [1, 6, 2] - @test 𝒞(2) == [9, 3, 1] - @test 𝒞(3) == [2, 8, 7] - @test 𝒞(4) == [7, 4, 9] - @test 𝒞(5) == [5, 8, 6] - @test 𝒞(6) == [3, 4, 5] + @test 𝒞(1) == (1, 6, 2) + @test 𝒞(2) == (9, 3, 1) + @test 𝒞(3) == (2, 8, 7) + @test 𝒞(4) == (7, 4, 9) + @test 𝒞(5) == (5, 8, 6) + @test 𝒞(6) == (3, 4, 5) 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == [1, 4] - @test 𝒞(2) == [2, 1] - @test 𝒞(3) == [4, 3] - @test 𝒞(4) == [3, 2] - @test 𝒞(5) == [3, 4, 1] - @test 𝒞(6) == [2, 3, 1] + @test 𝒞(1) == (1, 4) + @test 𝒞(2) == (2, 1) + @test 𝒞(3) == (4, 3) + @test 𝒞(4) == (3, 2) + @test 𝒞(5) == (3, 4, 1) + @test 𝒞(6) == (2, 3, 1) 𝒞 = Coboundary{1,2}(t) - @test 𝒞(1) == [1] - @test 𝒞(2) == [4] - @test 𝒞(3) == [2, 1] - @test 𝒞(4) == [2, 3] - @test 𝒞(5) == [3, 1] - @test 𝒞(6) == [4, 1] - @test 𝒞(7) == [3] - @test 𝒞(8) == [3, 4] - @test 𝒞(9) == [2] + @test 𝒞(1) == (1,) + @test 𝒞(2) == (4,) + @test 𝒞(3) == (2, 1) + @test 𝒞(4) == (2, 3) + @test 𝒞(5) == (3, 1) + @test 𝒞(6) == (4, 1) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (2,) 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == [2, 5, 3] - @test 𝒜(2) == [4, 6, 1] - @test 𝒜(3) == [1, 5, 4] - @test 𝒜(4) == [3, 6, 2] - @test 𝒜(5) == [6, 3, 1] - @test 𝒜(6) == [2, 4, 5] + @test 𝒜(1) == (2, 5, 3) + @test 𝒜(2) == (4, 6, 1) + @test 𝒜(3) == (1, 5, 4) + @test 𝒜(4) == (3, 6, 2) + @test 𝒜(5) == (6, 3, 1) + @test 𝒜(6) == (2, 4, 5) # 2 triangles + 2 quadrangles elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) t = HalfEdgeTopology(elems) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [2, 3, 4] - @test 𝒜(2) == [1, 3] - @test 𝒜(3) == [2, 4, 1] - @test 𝒜(4) == [1, 3] + @test 𝒜(1) == (2, 3, 4) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2, 4, 1) + @test 𝒜(4) == (1, 3) # 4 quadrangles in a grid elems = connect.([(1, 2, 5, 4), (2, 3, 6, 5), (4, 5, 8, 7), (5, 6, 9, 8)]) t = HalfEdgeTopology(elems) 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == [3, 2] - @test 𝒜(2) == [1, 4] - @test 𝒜(3) == [1, 4] - @test 𝒜(4) == [3, 2] + @test 𝒜(1) == (3, 2) + @test 𝒜(2) == (1, 4) + @test 𝒜(3) == (1, 4) + @test 𝒜(4) == (3, 2) # invalid relations elems = connect.([(1, 2, 3), (4, 3, 2)]) @@ -566,7 +566,7 @@ elems = connect.([(1, 2, 3), (4, 3, 2)]) t = SimpleTopology(elems) ∂ = Boundary{2,0}(t) - @test ∂(1) == [1, 2, 3] - @test ∂(2) == [4, 3, 2] + @test ∂(1) == (1, 2, 3) + @test ∂(2) == (4, 3, 2) end end From 19959870dfe8b7b2acb4139ec0e4815f4294a38c Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:39:00 -0300 Subject: [PATCH 277/423] Refactor `vizmesh!` (#1008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor 'vizmesh!' * Apply suggestions * Update 'asmakie' * Apply suggestions * Adjust comments --------- Co-authored-by: Júlio Hoffimann --- ext/MeshesMakieExt.jl | 1 + ext/geometryset.jl | 19 ------------------- ext/mesh.jl | 44 +++++++++++++++++++++++-------------------- ext/utils.jl | 19 +++++++++++++++++++ 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index dd6ea4630..3a881c754 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -15,6 +15,7 @@ using Colorfy using CoordRefSystems: Projected import TransformsBase as TB +import Makie.GeometryBasics as GB import Meshes: viz, viz! import Makie diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 113d695f5..c5f3006ef 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -128,22 +128,3 @@ function showfacets2D!(plot, geoms) viz!(plot, bset, color=segmentcolor, segmentsize=segmentsize) end end - -asmakie(geoms::AbstractVector{<:Geometry}) = asmakie.(geoms) - -asmakie(multis::AbstractVector{<:Multi}) = mapreduce(m -> asmakie.(parent(m)), vcat, multis) - -function asmakie(poly::Polygon) - rs = rings(poly) - outer = [asmakie(p) for p in vertices(first(rs))] - if hasholes(poly) - inners = map(i -> [asmakie(p) for p in vertices(rs[i])], 2:length(rs)) - Makie.Polygon(outer, inners) - else - Makie.Polygon(outer) - end -end - -asmakie(p::Point) = Makie.Point(ustrip.(Tuple(to(p)))) - -asmakie(v::Vec) = Makie.Vec(ustrip.(Tuple(v))) diff --git a/ext/mesh.jl b/ext/mesh.jl index 9c4a54b73..7dfa3cfa1 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -71,17 +71,20 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) elems = elements(topo) # coordinates of vertices - coords = map(p -> ustrip.(to(p)), verts) + coords = map(asmakie, verts) # fan triangulation (assume convexity) - tris4elem = map(elems) do elem + ntris = sum(e -> nvertices(pltype(e)) - 2, elems) + tris = Vector{GB.TriangleFace{Int}}(undef, ntris) + tind = 0 + for elem in elems I = indices(elem) - [[I[1], I[i], I[i + 1]] for i in 2:(length(I) - 1)] + for i in 2:(length(I) - 1) + tind += 1 + tris[tind] = GB.TriangleFace(I[1], I[i], I[i + 1]) + end end - # flatten vector of triangles - tris = [tri for tris in tris4elem for tri in tris] - # element vs. vertex coloring if $colorant isa AbstractVector ncolor = length($colorant) @@ -89,18 +92,18 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) # duplicate vertices and adjust # connectivities to avoid linear # interpolation of colors - nt = 0 + tind = 0 elem4tri = Dict{Int,Int}() - for e in 1:nelem - Δs = tris4elem[e] - for _ in 1:length(Δs) - nt += 1 - elem4tri[nt] = e + sizehint!(elem4tri, ntris) + for (eind, e) in enumerate(elems) + for _ in 1:(nvertices(pltype(e)) - 2) + tind += 1 + elem4tri[tind] = eind end end - nv = 3nt + nv = 3ntris tcoords = [coords[i] for tri in tris for i in tri] - tconnec = [collect(I) for I in Iterators.partition(1:nv, 3)] + tconnec = [GB.TriangleFace(i, i + 1, i + 2) for i in range(start=1, step=3, length=ntris)] tcolors = map(1:nv) do i t = ceil(Int, i / 3) e = elem4tri[t] @@ -126,22 +129,23 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) tcolors = $colorant end - # convert connectivities to matrix format - tmatrix = reduce(hcat, tconnec) |> transpose - # enable shading in 3D tshading = dim == 3 ? Makie.FastShading : Makie.NoShading - tcoords, tmatrix, tcolors, tshading + tcoords, tconnec, tcolors, tshading end # unpack observable of parameters tcoords = Makie.@lift $tparams[1] - tmatrix = Makie.@lift $tparams[2] + tconnec = Makie.@lift $tparams[2] tcolors = Makie.@lift $tparams[3] tshading = Makie.@lift $tparams[4] - Makie.mesh!(plot, tcoords, tmatrix, color=tcolors, shading=tshading) + # Makie's triangle mesh + mkemesh = Makie.@lift GB.Mesh($tcoords, $tconnec) + + # main visualization + Makie.mesh!(plot, mkemesh, color=tcolors, shading=tshading) if showsegments[] # retrieve coordinates parameters diff --git a/ext/utils.jl b/ext/utils.jl index 6ae4bc473..9921a2eb2 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -27,3 +27,22 @@ function vizmany!(plot, objs, color) viz!(plot, object, color=colors, alpha=alphas, colormap=colormap, pointsize=pointsize, segmentsize=segmentsize) end + +asmakie(geoms::AbstractVector{<:Geometry}) = asmakie.(geoms) + +asmakie(multis::AbstractVector{<:Multi}) = mapreduce(m -> asmakie.(parent(m)), vcat, multis) + +function asmakie(poly::Polygon) + rs = rings(poly) + outer = [asmakie(p) for p in vertices(first(rs))] + if hasholes(poly) + inners = map(i -> [asmakie(p) for p in vertices(rs[i])], 2:length(rs)) + Makie.Polygon(outer, inners) + else + Makie.Polygon(outer) + end +end + +asmakie(p::Point) = Makie.Point{embeddim(p),Float32}(ustrip.(Tuple(to(p)))) + +asmakie(v::Vec) = Makie.Vec{length(v),Float32}(ustrip.(Tuple(v))) From 3f16a999b1cb0a283dfe8155164d1000f2fda0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 16 Aug 2024 17:04:42 -0300 Subject: [PATCH 278/423] Adjust numtype in viz utils --- ext/MeshesMakieExt.jl | 2 ++ ext/utils.jl | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 3a881c754..59d60a4cd 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -12,6 +12,8 @@ using LinearAlgebra using CoordRefSystems using Colorfy +using Unitful: numtype +using Meshes: lentype using CoordRefSystems: Projected import TransformsBase as TB diff --git a/ext/utils.jl b/ext/utils.jl index 9921a2eb2..ff1236807 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -43,6 +43,6 @@ function asmakie(poly::Polygon) end end -asmakie(p::Point) = Makie.Point{embeddim(p),Float32}(ustrip.(Tuple(to(p)))) +asmakie(p::Point) = Makie.Point{embeddim(p),numtype(lentype(p))}(ustrip.(Tuple(to(p)))) -asmakie(v::Vec) = Makie.Vec{length(v),Float32}(ustrip.(Tuple(v))) +asmakie(v::Vec) = Makie.Vec{length(v),numtype(eltype(v))}(ustrip.(Tuple(v))) From 2f4b4eabb4ddad869e66446a245cc1ab0960e288 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Sat, 17 Aug 2024 08:08:18 -0300 Subject: [PATCH 279/423] Optimize `vizgrid!` of `TransformedGrid` (#1009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WIP] Optimize 'vizgrid!' of 'TransformedGrid' * Fix typo * Fix typo * Finish optimization --------- Co-authored-by: Júlio Hoffimann --- ext/grid/transformed.jl | 79 +++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 507abfa44..f858e3f67 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -4,44 +4,64 @@ function vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val, edim::Val) tgrid = plot[:object] - grid = Makie.@lift parent($tgrid) + color = plot[:color] + alpha = plot[:alpha] + colormap = plot[:colormap] + colorrange = plot[:colorrange] + showsegments = plot[:showsegments] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + # retrieve transformation trans = Makie.@lift Meshes.transform($tgrid) - if isoptimized(crs(grid[]), trans[]) - color = plot[:color] - alpha = plot[:alpha] - colormap = plot[:colormap] - showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] + + if isoptimized(trans[]) # visualize parent grid and transform visualization + grid = Makie.@lift parent($tgrid) viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) - else + elseif pdim == 2 # visualize quadrangle mesh with texture using uv coords + verts = Makie.@lift map(asmakie, vertices($tgrid)) + quads = Makie.@lift [GB.QuadFace(indices(e)) for e in elements(topology($tgrid))] + + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + + nverts = Makie.@lift length($verts) + nquads = Makie.@lift length($quads) + ncolor = Makie.@lift length($colorant) + + dims = Makie.@lift size($tgrid) + texture = if ncolor[] == 1 + Makie.@lift fill($colorant, $dims) + elseif ncolor[] == nquads[] + Makie.@lift reshape($colorant, $dims) + elseif ncolor[] == nverts[] + throw(ErrorException("cannot visualize transformed grid with colors on vertices")) + end + + uv = Makie.@lift [Makie.Vec2f(u, v) for u in range(0, 1, $dims[1] + 1) for v in range(0, 1, $dims[2] + 1)] + + mesh = Makie.@lift GB.Mesh(Makie.meta($verts, uv=$uv), $quads) + + Makie.mesh!(mesh, color=texture) + else # fallback to triangle mesh visualization vizmesh!(plot, M, pdim, edim) end end -isoptimized(::Type, ::TB.Identity) = true -isoptimized(CRS::Type, t::TB.SequentialTransform) = all(tᵢ -> isoptimized(CRS, tᵢ), t) - -isoptimized(::Type, ::GeometricTransform) = false - -isoptimized(::Type{<:Cartesian2D}, ::Proj{<:Projected}) = true -isoptimized(::Type{<:Projected}, ::Proj{<:Cartesian2D}) = true +# -------------- +# OPTIMIZATIONS +# -------------- -isoptimized(::Type, ::Rotate{<:Angle2d}) = true -isoptimized(::Type, ::Translate) = true -isoptimized(::Type, ::Scale) = true -function isoptimized(::Type, t::Affine{2}) +isoptimized(t) = false +isoptimized(::Rotate{<:Angle2d}) = true +isoptimized(::Translate) = true +isoptimized(::Scale) = true +function isoptimized(t::Affine{2}) A, _ = TB.parameters(t) isdiag(A) || isrotation(A) end - -makietransform!(plot, trans::Makie.Observable{<:TB.Identity}) = nothing - -makietransform!(plot, trans::Makie.Observable{<:Proj}) = nothing - -makietransform!(plot, trans::Makie.Observable{<:TB.SequentialTransform}) = - foreach(t -> makietransform!(plot, Makie.Observable(t)), trans[]) +isoptimized(::TB.Identity) = true +isoptimized(t::TB.SequentialTransform) = all(isoptimized, t) function makietransform!(plot, trans::Makie.Observable{<:Rotate{<:Angle2d}}) rot = first(TB.parameters(trans[])) @@ -71,3 +91,8 @@ function makietransform!(plot, trans::Makie.Observable{<:Affine{2}}) end Makie.translate!(plot, ustrip.(b)...) end + +makietransform!(plot, trans::Makie.Observable{<:TB.Identity}) = nothing + +makietransform!(plot, trans::Makie.Observable{<:TB.SequentialTransform}) = + foreach(t -> makietransform!(plot, Makie.Observable(t)), trans[]) From a0fda4739c8bbcb08e62234545b98cfed6fa8645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 17 Aug 2024 08:08:43 -0300 Subject: [PATCH 280/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b1212d6db..6d3765385 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.48.7" +version = "0.49.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From e7477b98d51ec91ac9cb99f9ed89a36e9234ca94 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:30:57 -0300 Subject: [PATCH 281/423] Fix viz of `TransformedGrid` (#1012) * Fix viz of 'TransformedGrid' * Add error message --- ext/grid/transformed.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index f858e3f67..cb785237c 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -19,7 +19,7 @@ function vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, p grid = Makie.@lift parent($tgrid) viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) - elseif pdim == 2 # visualize quadrangle mesh with texture using uv coords + elseif pdim == Val(2) # visualize quadrangle mesh with texture using uv coords verts = Makie.@lift map(asmakie, vertices($tgrid)) quads = Makie.@lift [GB.QuadFace(indices(e)) for e in elements(topology($tgrid))] @@ -35,14 +35,18 @@ function vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, p elseif ncolor[] == nquads[] Makie.@lift reshape($colorant, $dims) elseif ncolor[] == nverts[] - throw(ErrorException("cannot visualize transformed grid with colors on vertices")) + Makie.@lift reshape($colorant, $dims .+ 1) + else + throw(ArgumentError("invalid number of colors")) end - uv = Makie.@lift [Makie.Vec2f(u, v) for u in range(0, 1, $dims[1] + 1) for v in range(0, 1, $dims[2] + 1)] + uv = Makie.@lift [Makie.Vec2f(v, 1 - u) for v in range(0, 1, $dims[2] + 1) for u in range(0, 1, $dims[1] + 1)] mesh = Makie.@lift GB.Mesh(Makie.meta($verts, uv=$uv), $quads) - Makie.mesh!(mesh, color=texture) + shading = edim == Val(3) ? Makie.FastShading : Makie.NoShading + + Makie.mesh!(plot, mesh, color=texture, shading=shading) else # fallback to triangle mesh visualization vizmesh!(plot, M, pdim, edim) end From a54cd27eb74817e5c65da2ef90fb97f2d19c28bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 19 Aug 2024 18:31:21 -0300 Subject: [PATCH 282/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6d3765385..61ba61cfe 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.0" +version = "0.49.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 50eddebe12c0814e0420aebc33d70117e2d78dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 21 Aug 2024 09:12:47 -0300 Subject: [PATCH 283/423] Update exports (#1015) * Update exports * Update test/meshes.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Export TransformedGeometry --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- ext/grid/transformed.jl | 2 +- src/Meshes.jl | 8 ++++-- src/domains/meshes/transformedmesh.jl | 2 +- src/geometries/transformedgeom.jl | 2 +- test/boundingboxes.jl | 4 +-- test/coarsening.jl | 4 +-- test/discretization.jl | 2 +- test/meshes.jl | 35 +++++++++++++-------------- test/refinement.jl | 4 +-- test/subdomains.jl | 4 +-- test/transformedgeoms.jl | 18 +++++++------- 11 files changed, 44 insertions(+), 41 deletions(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index cb785237c..847a0c18e 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -2,7 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function vizgrid!(plot::Viz{<:Tuple{Meshes.TransformedGrid}}, M::Type{<:𝔼}, pdim::Val, edim::Val) +function vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Val, edim::Val) tgrid = plot[:object] color = plot[:color] alpha = plot[:alpha] diff --git a/src/Meshes.jl b/src/Meshes.jl index 2989165b7..af8212197 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -234,6 +234,9 @@ export MultiPolygon, MultiPolyhedron, + # transformed geometry + TransformedGeometry, + # connectivities Connectivity, paramdim, @@ -297,11 +300,12 @@ export # meshes Mesh, Grid, - SubGrid, + SimpleMesh, + TransformedMesh, CartesianGrid, RectilinearGrid, StructuredGrid, - SimpleMesh, + TransformedGrid, vertex, vertices, nvertices, diff --git a/src/domains/meshes/transformedmesh.jl b/src/domains/meshes/transformedmesh.jl index ecfd8dcd0..0c32800b2 100644 --- a/src/domains/meshes/transformedmesh.jl +++ b/src/domains/meshes/transformedmesh.jl @@ -5,7 +5,7 @@ """ TransformedMesh(mesh, transform) -Lazy representation of a geometric `transform` applied to a `mesh`. +Lazy representation of a coordinate `transform` applied to a `mesh`. """ struct TransformedMesh{M<:Manifold,C<:CRS,TP<:Topology,MS<:Mesh,TR<:Transform} <: Mesh{M,C,TP} mesh::MS diff --git a/src/geometries/transformedgeom.jl b/src/geometries/transformedgeom.jl index aa105cffd..b3d460c05 100644 --- a/src/geometries/transformedgeom.jl +++ b/src/geometries/transformedgeom.jl @@ -5,7 +5,7 @@ """ TransformedGeometry(geometry, transform) -Lazy representation of a geometric `transform` applied to a `geometry`. +Lazy representation of a coordinate `transform` applied to a `geometry`. """ struct TransformedGeometry{M<:Manifold,C<:CRS,G<:Geometry,T<:Transform} <: Geometry{M,C} geometry::G diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 2608354d3..a2573e3dc 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -101,13 +101,13 @@ @test @allocated(boundingbox(d)) < 50 g = cartgrid(10, 10) - d = Meshes.TransformedGrid(g, Rotate(T(π / 2))) + d = TransformedGrid(g, Rotate(T(π / 2))) @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 g = cartgrid(10, 10) rg = convert(RectilinearGrid, g) - d = Meshes.TransformedGrid(rg, Rotate(T(π / 2))) + d = TransformedGrid(rg, Rotate(T(π / 2))) @test boundingbox(d) ≈ Box(cart(-10, 0), cart(0, 10)) @test @allocated(boundingbox(d)) < 3000 diff --git a/test/coarsening.jl b/test/coarsening.jl index 1bde9fdc2..15d6e9fd5 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -10,7 +10,7 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid - tfgrid = Meshes.TransformedGrid(grid, Identity()) + tfgrid = TransformedGrid(grid, Identity()) @test coarsen(tfgrid, RegularCoarsening(2)) == coarsen(grid, RegularCoarsening(2)) grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) @@ -27,7 +27,7 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid - tfgrid = Meshes.TransformedGrid(grid, Identity()) + tfgrid = TransformedGrid(grid, Identity()) @test coarsen(tfgrid, RegularCoarsening(2, 4, 5)) == coarsen(grid, RegularCoarsening(2, 4, 5)) end end diff --git a/test/discretization.jl b/test/discretization.jl index 41b310ccb..28c802683 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -401,7 +401,7 @@ poly = PolyArea(cart.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) mesh = discretize(poly, RegularDiscretization(50)) - @test mesh isa SubGrid + @test mesh isa Meshes.SubGrid grid = parent(mesh) @test grid isa CartesianGrid @test eltype(mesh) <: Quadrangle diff --git a/test/meshes.jl b/test/meshes.jl index 75a275961..70b7e1beb 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -701,43 +701,42 @@ sgrid = convert(StructuredGrid, grid) mesh = convert(SimpleMesh, grid) trans = Identity() - tmesh = Meshes.TransformedMesh(mesh, trans) + tmesh = TransformedMesh(mesh, trans) @test crs(tmesh) <: Cartesian{NoDatum} @test Meshes.lentype(tmesh) == ℳ @test parent(tmesh) === mesh @test Meshes.transform(tmesh) === trans - @test Meshes.TransformedMesh(grid, trans) == grid - @test Meshes.TransformedMesh(rgrid, trans) == rgrid - @test Meshes.TransformedMesh(sgrid, trans) == sgrid - @test Meshes.TransformedMesh(mesh, trans) == mesh + @test TransformedMesh(grid, trans) == grid + @test TransformedMesh(rgrid, trans) == rgrid + @test TransformedMesh(sgrid, trans) == sgrid + @test TransformedMesh(mesh, trans) == mesh trans = Translate(T(10), T(10)) → Translate(T(-10), T(-10)) - @test Meshes.TransformedMesh(grid, trans) == grid - @test Meshes.TransformedMesh(rgrid, trans) == rgrid - @test Meshes.TransformedMesh(sgrid, trans) == sgrid - @test Meshes.TransformedMesh(mesh, trans) == mesh + @test TransformedMesh(grid, trans) == grid + @test TransformedMesh(rgrid, trans) == rgrid + @test TransformedMesh(sgrid, trans) == sgrid + @test TransformedMesh(mesh, trans) == mesh trans1 = Translate(T(10), T(10)) trans2 = Translate(T(-10), T(-10)) - @test Meshes.TransformedMesh(Meshes.TransformedMesh(grid, trans1), trans2) == - Meshes.TransformedMesh(grid, trans1 → trans2) + @test TransformedMesh(TransformedMesh(grid, trans1), trans2) == TransformedMesh(grid, trans1 → trans2) # transforms that change the Manifold and/or CRS points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) trans = Proj(Cartesian) - tmesh = Meshes.TransformedMesh(mesh, trans) + tmesh = TransformedMesh(mesh, trans) @test manifold(tmesh) === 🌐 @test crs(tmesh) <: Cartesian trans = Proj(Polar) - tgrid = Meshes.TransformedMesh(grid, trans) - @test tgrid isa Meshes.TransformedGrid + tgrid = TransformedMesh(grid, trans) + @test tgrid isa TransformedGrid @test manifold(tgrid) === 𝔼{2} @test crs(tgrid) <: Polar # grid interface trans = Identity() - tgrid = Meshes.TransformedMesh(grid, trans) - @test tgrid isa Meshes.TransformedGrid + tgrid = TransformedMesh(grid, trans) + @test tgrid isa TransformedGrid @test size(tgrid) == (10, 10) @test minimum(tgrid) == cart(0, 0) @test maximum(tgrid) == cart(10, 10) @@ -762,14 +761,14 @@ # optimization of centroid trans = Rotate(T(π / 4)) cgrid = cartgrid(10, 10) - tmesh = Meshes.TransformedMesh(cgrid, trans) + tmesh = TransformedMesh(cgrid, trans) centr = centroid(tmesh, 1) @test @allocated(centroid(tmesh, 1)) < 50 # optimization of == trans = Rotate(T(π / 4)) cgrid = cartgrid(1000, 1000) - tmesh = Meshes.TransformedMesh(cgrid, trans) + tmesh = TransformedMesh(cgrid, trans) @test tmesh == tmesh @test sprint(show, tgrid) == "10×10 TransformedGrid" diff --git a/test/refinement.jl b/test/refinement.jl index 43867e7a7..211b13f65 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -53,7 +53,7 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test refine(sgrid, RegularRefinement(2)) == tsgrid - tfgrid = Meshes.TransformedGrid(grid, Identity()) + tfgrid = TransformedGrid(grid, Identity()) @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) # 3D grids @@ -66,7 +66,7 @@ sgrid = convert(StructuredGrid, grid) tsgrid = convert(StructuredGrid, tgrid) @test refine(sgrid, RegularRefinement(2)) == tsgrid - tfgrid = Meshes.TransformedGrid(grid, Identity()) + tfgrid = TransformedGrid(grid, Identity()) @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) end diff --git a/test/subdomains.jl b/test/subdomains.jl index 5cb7fc5fc..c22ac5a57 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -40,7 +40,7 @@ # view of view stores the correct domain g = cartgrid(10, 10) v = view(view(g, 11:20), 1:3) - @test v isa SubGrid + @test v isa Meshes.SubGrid @test v[1] == g[11] @test v[2] == g[12] @test v[3] == g[13] @@ -58,7 +58,7 @@ # concatenation with same parent g = cartgrid(10, 10) vg = vcat(view(g, 50:70), view(g, 10:30)) - @test vg isa SubGrid + @test vg isa Meshes.SubGrid @test vg == view(g, [50:70; 10:30]) # concatenation with different parents g1 = cartgrid(10, 10) diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl index 94ffccf4b..4ccf01f4f 100644 --- a/test/transformedgeoms.jl +++ b/test/transformedgeoms.jl @@ -1,11 +1,11 @@ @testset "TransformedGeometry" begin b = Box(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) - tb = Meshes.TransformedGeometry(b, t) + tb = TransformedGeometry(b, t) @test parent(tb) == b @test Meshes.transform(tb) == t t2 = Scale(T(2), T(3)) - tb2 = Meshes.TransformedGeometry(tb, t2) + tb2 = TransformedGeometry(tb, t2) @test Meshes.transform(tb2) == (t → t2) @test paramdim(tb) == paramdim(b) @test tb == tb @@ -14,26 +14,26 @@ @test centroid(tb) == t(centroid(b)) @test discretize(tb) == t(discretize(b)) t3 = Scale(T(2), T(2)) - tb3 = Meshes.TransformedGeometry(b, t3) + tb3 = TransformedGeometry(b, t3) @test measure(tb3) == 4 * measure(b) equaltest(tb) isapproxtest(tb) b = Ball(latlon(0, 0), T(1)) t = Proj(Cartesian) - tb = Meshes.TransformedGeometry(b, t) + tb = TransformedGeometry(b, t) @test paramdim(tb) == paramdim(b) @test centroid(tb) == t(centroid(b)) s = Sphere(latlon(0, 0), T(1)) t = Proj(Cartesian) - ts = Meshes.TransformedGeometry(s, t) + ts = TransformedGeometry(s, t) @test paramdim(ts) == paramdim(s) @test centroid(ts) == t(centroid(s)) s = Segment(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) - ts = Meshes.TransformedGeometry(s, t) + ts = TransformedGeometry(s, t) @test vertex(ts, 1) == t(vertex(s, 1)) @test vertices(ts) == t.(vertices(s)) @test nvertices(ts) == nvertices(s) @@ -42,20 +42,20 @@ p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) t = Translate(T(1), T(2)) - tp = Meshes.TransformedGeometry(p, t) + tp = TransformedGeometry(p, t) @test vertex(tp, 1) == t(vertex(p, 1)) @test vertices(tp) == t.(vertices(p)) @test nvertices(tp) == nvertices(p) @test rings(tp) == t.(rings(p)) p2 = PolyArea(cart(0, 0), cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - tp2 = Meshes.TransformedGeometry(p2, t) + tp2 = TransformedGeometry(p2, t) @test unique(tp2) == tp equaltest(tp) isapproxtest(tp) b = Box(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) - tb = Meshes.TransformedGeometry(b, t) + tb = TransformedGeometry(b, t) @test sprint(show, tb) == "TransformedBox(geometry: Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m)), transform: Translate(offsets: (1.0 m, 2.0 m)))" @test sprint(show, MIME"text/plain"(), tb) == """ From fd7df3856a0dbc41f024fb352aecfbcdfa1378ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 21 Aug 2024 09:13:38 -0300 Subject: [PATCH 284/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 61ba61cfe..23fbb4a3e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.1" +version = "0.49.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 4f2eb992aa2e57db153a70f4d1787d93b621cacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 22 Aug 2024 14:13:23 -0300 Subject: [PATCH 285/423] Add pointmarker option to viz (#1016) --- ext/MeshesMakieExt.jl | 1 + ext/geometryset.jl | 14 ++++++++------ ext/subdomain.jl | 2 ++ src/viz.jl | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 59d60a4cd..2e29e4ace 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -32,6 +32,7 @@ Makie.@recipe(Viz, object) do scene segmentcolor=:gray30, segmentsize=1.5, showpoints=false, + pointmarker=:circle, pointcolor=:gray30, pointsize=4 ) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index c5f3006ef..df43b1585 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -41,13 +41,14 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms, colorant) end function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms::ObservableVector{<:Point}, colorant) + pointmarker = plot[:pointmarker] pointsize = plot[:pointsize] - # get coordinates + # get raw Cartesian coordinates of points coords = Makie.@lift map(p -> ustrip.(to(p)), $geoms) - # visualize points - Makie.scatter!(plot, coords, color=colorant, markersize=pointsize, overdraw=true) + # visualize points with given marker and size + Makie.scatter!(plot, coords, color=colorant, marker=pointmarker, markersize=pointsize, overdraw=true) end function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms, colorant) @@ -105,14 +106,15 @@ end function showfacets1D!(plot, geoms) showpoints = plot[:showpoints] + pointmarker = plot[:pointmarker] pointcolor = plot[:pointcolor] pointsize = plot[:pointsize] if showpoints[] # all boundaries are points or multipoints - bounds = Makie.@lift filter(!isnothing, boundary.($geoms)) - bset = Makie.@lift GeometrySet($bounds) - viz!(plot, bset, color=pointcolor, pointsize=pointsize) + points = Makie.@lift filter(!isnothing, boundary.($geoms)) + pset = Makie.@lift GeometrySet($points) + viz!(plot, pset, color=pointcolor, pointmarker=pointmarker, pointsize=pointsize) end end diff --git a/ext/subdomain.jl b/ext/subdomain.jl index d97735d0c..bb05d832e 100644 --- a/ext/subdomain.jl +++ b/ext/subdomain.jl @@ -24,6 +24,7 @@ function vizsubdom!(plot, ::Type{<:𝔼}, ::Val, ::Val) segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] showpoints = plot[:showpoints] + pointmarker = plot[:pointmarker] pointcolor = plot[:pointcolor] pointsize = plot[:pointsize] @@ -42,6 +43,7 @@ function vizsubdom!(plot, ::Type{<:𝔼}, ::Val, ::Val) segmentcolor, segmentsize, showpoints, + pointmarker, pointcolor, pointsize ) diff --git a/src/viz.jl b/src/viz.jl index bc7fc9fa5..ae308d898 100644 --- a/src/viz.jl +++ b/src/viz.jl @@ -17,6 +17,7 @@ Visualize Meshes.jl `object` with various `options`. * `segmentcolor` - color of segments * `segmentsize` - width of segments * `showpoints` - visualize points +* `pointmarker` - marker of points * `pointcolor` - color of points * `pointsize` - size of points From 3965c4a6156321e1390638cced4875f2c37322c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 22 Aug 2024 14:13:52 -0300 Subject: [PATCH 286/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 23fbb4a3e..457cac523 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.2" +version = "0.49.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 3bc098a18bc90558cb6412f0a4992272c3ad4c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 22 Aug 2024 16:07:02 -0300 Subject: [PATCH 287/423] Fix normal in TransformedGrid viz recipe (#1017) --- ext/MeshesMakieExt.jl | 1 - ext/grid/transformed.jl | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/MeshesMakieExt.jl b/ext/MeshesMakieExt.jl index 2e29e4ace..2a282739b 100644 --- a/ext/MeshesMakieExt.jl +++ b/ext/MeshesMakieExt.jl @@ -14,7 +14,6 @@ using Colorfy using Unitful: numtype using Meshes: lentype -using CoordRefSystems: Projected import TransformsBase as TB import Makie.GeometryBasics as GB diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 847a0c18e..fdea09552 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -20,8 +20,11 @@ function vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Va viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) elseif pdim == Val(2) # visualize quadrangle mesh with texture using uv coords + # decide whether or not to reverse connectivity list + rfunc = Makie.@lift _reverse(crs($tgrid)) + verts = Makie.@lift map(asmakie, vertices($tgrid)) - quads = Makie.@lift [GB.QuadFace(indices(e)) for e in elements(topology($tgrid))] + quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($tgrid))] colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -52,6 +55,9 @@ function vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Va end end +_reverse(::Type{<:CRS}) = identity +_reverse(::Type{<:LatLon}) = reverse + # -------------- # OPTIMIZATIONS # -------------- From 0b84faefcd4afa14ac28322d1d1977e77b2b4e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 22 Aug 2024 16:07:21 -0300 Subject: [PATCH 288/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 457cac523..bede55fe8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.3" +version = "0.49.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 4588a937776c9c19a82c3d0a9458937d342d7ad5 Mon Sep 17 00:00:00 2001 From: Kyle Beggs Date: Fri, 23 Aug 2024 13:00:41 -0400 Subject: [PATCH 289/423] Handle different CRS and length units in `sideof` (#1014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add length convert to sideof * use Proj * Update src/sideof.jl Co-authored-by: Júlio Hoffimann * fix test for sideof with different units * More adjustments * Update compat * Refactor withcrs * Revert changes --------- Co-authored-by: Kyle Beggs Co-authored-by: Júlio Hoffimann Co-authored-by: Elias Carvalho --- Project.toml | 2 +- src/sideof.jl | 5 +++-- test/sideof.jl | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index bede55fe8..d590e3c57 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.10" +CoordRefSystems = "0.11" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/src/sideof.jl b/src/sideof.jl index 29fad0a3d..66f39f9bd 100644 --- a/src/sideof.jl +++ b/src/sideof.jl @@ -58,10 +58,11 @@ Possible results are `IN`, `OUT` or `ON` the `ring`. """ function sideof(point::Point, ring::Ring) assertion(CoordRefSystems.ncoords(crs(point)) == 2, "points must have 2 coordinates") + point′ = point |> Proj(crs(ring)) if nvertices(ring) ≤ 1000 || Threads.nthreads() == 1 - _sideofserial(point, ring) + _sideofserial(point′, ring) else - _sideofthreads(point, ring) + _sideofthreads(point′, ring) end end diff --git a/test/sideof.jl b/test/sideof.jl index 7d99b9d3d..7e5933ee4 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -15,6 +15,13 @@ pts = [p1, p2, p3] @test sideof(pts, c) == [IN, OUT, ON] + c′ = c |> LengthUnit(Unitful.mm) + @test sideof(p1, c′) == IN + @test sideof(p2, c′) == OUT + @test sideof(p3, c′) == ON + pts = [p1, p2, p3] + @test sideof(pts, c′) == [IN, OUT, ON] + p1, p2, p3 = merc(0.5, 0.5), merc(1.5, 0.5), merc(1, 1) c = Ring([merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)]) @test sideof(p1, c) == IN From f687b767a4a7c99a2cf95629dbe3229963998508 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:27:16 -0300 Subject: [PATCH 290/423] Refactor `withcrs` (#1023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor 'withcrs' * Apply suggestions from code review Co-authored-by: Júlio Hoffimann --------- Co-authored-by: Júlio Hoffimann --- src/transforms/shadow.jl | 6 +++++- src/utils/crs.jl | 6 +++--- test/intersections.jl | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index c33e964ce..b7d5842a0 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -83,7 +83,11 @@ _sort(dims) = sort(SVector(dims)) _shadow(v::Vec, dims) = v[dims] -_shadow(p::Point, dims) = withcrs(p, to(p)[dims]) +function _shadow(p::Point, dims) + v = _shadow(to(p), dims) + c = Cartesian{datum(crs(p))}(v...) + Point(c) +end function _shadow(g::CartesianGrid, dims) sz = size(g)[dims] diff --git a/src/utils/crs.jl b/src/utils/crs.jl index bda5e466b..2ee66d369 100644 --- a/src/utils/crs.jl +++ b/src/utils/crs.jl @@ -9,9 +9,9 @@ Point at the end of the vector `v` with the same CRS of `g`. """ function withcrs(g::GeometryOrDomain, v::StaticVector) C = crs(g) - cart = Cartesian{datum(C)}(Tuple(v)) - ctor = CoordRefSystems.constructor(C) - Point(convert(ctor, cart)) + D = datum(C) + c = convert(C, Cartesian{D}(v...)) + Point(c) end """ diff --git a/test/intersections.jl b/test/intersections.jl index e268e9e62..54b93c0df 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -831,7 +831,7 @@ b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) @test intersection(b1, b2) |> type == Overlapping - @test unit(Meshes.lentype(b1 ∩ b2)) == u"m" + @test unit(Meshes.lentype(b1 ∩ b2)) == u"cm" @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) # type stability tests From 4ae22821063292f7185af3cee86361472e9d2def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 23 Aug 2024 14:27:46 -0300 Subject: [PATCH 291/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d590e3c57..679feb358 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.4" +version = "0.49.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 95c83c6a8d3368ae30efd559a778a2822cca666c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 23 Aug 2024 17:07:56 -0300 Subject: [PATCH 292/423] Refactor simplification methods (#1019) * Initial refactor * Add BinarySearchSimplification * More adjustments * Fix docstring * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * Formatting * Minor adjustments --- docs/src/algorithms/simplification.md | 127 +++++++++++++++----------- src/Meshes.jl | 6 +- src/simplification.jl | 28 +----- src/simplification/douglaspeucker.jl | 82 ++++------------- src/simplification/minmax.jl | 54 +++++++++++ src/simplification/selinger.jl | 35 ++++--- test/simplification.jl | 69 ++++++-------- 7 files changed, 193 insertions(+), 208 deletions(-) create mode 100644 src/simplification/minmax.jl diff --git a/docs/src/algorithms/simplification.md b/docs/src/algorithms/simplification.md index 8a9640bdb..49448d195 100644 --- a/docs/src/algorithms/simplification.md +++ b/docs/src/algorithms/simplification.md @@ -7,86 +7,103 @@ import CairoMakie as Mke # hide ```@docs simplify -decimate SimplificationMethod ``` -## Douglas-Peucker +## SelingerSimplification ```@docs -DouglasPeucker +SelingerSimplification ``` ```@example simplification -# polygonal area -polyarea = PolyArea([(0.22926679, 0.47329807), (0.23094065, 0.44913536), (0.2569517, 0.38217533), - (0.3072999, 0.272418), (0.34814754, 0.18421611), (0.37949452, 0.11756973), - (0.4013409, 0.07247882), (0.41368666, 0.048943404), (0.42597583, 0.031655528), - (0.4382084, 0.0206152), (0.45038435, 0.015822414), (0.4625037, 0.017277176), - (0.47175184, 0.02439156), (0.47812873, 0.03716557), (0.4816344, 0.055599205), - (0.48226887, 0.07969247), (0.48172843, 0.10446181), (0.4800131, 0.12990724), - (0.47712287, 0.15602873), (0.47305775, 0.18282633), (0.47093934, 0.20558843), - (0.47076762, 0.22431506), (0.47254258, 0.23900622), (0.47626427, 0.24966191), - (0.47768936, 0.25845313), (0.47681788, 0.26537988), (0.4736498, 0.27044216), - (0.46818516, 0.27363995), (0.4613889, 0.27232954), (0.45326096, 0.2665109), - (0.44380143, 0.256184), (0.43301025, 0.24134888), (0.4246466, 0.22978415), - (0.41871038, 0.22148979), (0.4152017, 0.21646582), (0.4141205, 0.21471222), - (0.41227448, 0.21589448), (0.40966362, 0.22001258), (0.40628797, 0.22706655), - (0.40214747, 0.23705636), (0.40200475, 0.24653101), (0.40585983, 0.25549048), - (0.41371268, 0.2639348), (0.4255633, 0.2718639), (0.4378565, 0.28495985), - (0.4505922, 0.30322257), (0.46377045, 0.32665208), (0.47739124, 0.35524836), - (0.5046394, 0.36442512), (0.5455148, 0.35418236), (0.60001767, 0.32452005), - (0.66814786, 0.27543822), (0.7186763, 0.24664374), (0.75160307, 0.23813659), - (0.76692814, 0.2499168), (0.7646515, 0.28198436), (0.7769703, 0.29925033), - (0.8038847, 0.3017147), (0.84539455, 0.28937748), (0.9015, 0.26223865), - (0.94408435, 0.24899776), (0.9731477, 0.24965483), (0.98869, 0.26420987), - (0.9907113, 0.29266283), (0.9849871, 0.31338844), (0.97151726, 0.32638666), - (0.950302, 0.3316575), (0.9213412, 0.32920095), (0.8798396, 0.34078467), - (0.8257972, 0.36640862), (0.7592141, 0.40607283), (0.6800901, 0.4597773), - (0.6450007, 0.49104902), (0.6539457, 0.49988794), (0.7069251, 0.48629412), - (0.803939, 0.45026752), (0.877913, 0.4226481), (0.9288472, 0.40343583), - (0.9567415, 0.39263073), (0.961596, 0.39023277), (0.9419039, 0.40523484), - (0.89766514, 0.43763688), (0.8288798, 0.48743892), (0.7355478, 0.55464095), - (0.6655121, 0.60063523), (0.6187727, 0.6254217), (0.5953296, 0.62900037), - (0.5951828, 0.6113712), (0.57516366, 0.60261106), (0.53527224, 0.6027198), - (0.4755085, 0.6116975), (0.3958725, 0.6295441), (0.33913234, 0.6398651), - (0.30528808, 0.6426605), (0.2943397, 0.6379303), (0.30628717, 0.6256744), - (0.32149008, 0.6093727), (0.33994842, 0.5890249), (0.36166218, 0.5646312), - (0.38663134, 0.5361916), (0.3919681, 0.520893), (0.3776725, 0.5187355), - (0.34374446, 0.52971905), (0.29018405, 0.5538437), (0.25439468, 0.5678829), - (0.2363764, 0.5718367), (0.23612918, 0.56570506), (0.25365302, 0.549488), - (0.2733971, 0.5246488), (0.29536137, 0.49118724), (0.3195459, 0.4491035), - (0.34595063, 0.39839754), (0.3647463, 0.3590396), (0.37593287, 0.33102974), - (0.37951034, 0.31436795), (0.37547874, 0.30905423), (0.36070493, 0.3204269), - (0.33518887, 0.348486), (0.29893062, 0.3932315), (0.25193012, 0.45466346)]) +poly = PolyArea([(0.22926679, 0.47329807), (0.23094065, 0.44913536), (0.2569517, 0.38217533), + (0.3072999, 0.272418), (0.34814754, 0.18421611), (0.37949452, 0.11756973), + (0.4013409, 0.07247882), (0.41368666, 0.048943404), (0.42597583, 0.031655528), + (0.4382084, 0.0206152), (0.45038435, 0.015822414), (0.4625037, 0.017277176), + (0.47175184, 0.02439156), (0.47812873, 0.03716557), (0.4816344, 0.055599205), + (0.48226887, 0.07969247), (0.48172843, 0.10446181), (0.4800131, 0.12990724), + (0.47712287, 0.15602873), (0.47305775, 0.18282633), (0.47093934, 0.20558843), + (0.47076762, 0.22431506), (0.47254258, 0.23900622), (0.47626427, 0.24966191), + (0.47768936, 0.25845313), (0.47681788, 0.26537988), (0.4736498, 0.27044216), + (0.46818516, 0.27363995), (0.4613889, 0.27232954), (0.45326096, 0.2665109), + (0.44380143, 0.256184), (0.43301025, 0.24134888), (0.4246466, 0.22978415), + (0.41871038, 0.22148979), (0.4152017, 0.21646582), (0.4141205, 0.21471222), + (0.41227448, 0.21589448), (0.40966362, 0.22001258), (0.40628797, 0.22706655), + (0.40214747, 0.23705636), (0.40200475, 0.24653101), (0.40585983, 0.25549048), + (0.41371268, 0.2639348), (0.4255633, 0.2718639), (0.4378565, 0.28495985), + (0.4505922, 0.30322257), (0.46377045, 0.32665208), (0.47739124, 0.35524836), + (0.5046394, 0.36442512), (0.5455148, 0.35418236), (0.60001767, 0.32452005), + (0.66814786, 0.27543822), (0.7186763, 0.24664374), (0.75160307, 0.23813659), + (0.76692814, 0.2499168), (0.7646515, 0.28198436), (0.7769703, 0.29925033), + (0.8038847, 0.3017147), (0.84539455, 0.28937748), (0.9015, 0.26223865), + (0.94408435, 0.24899776), (0.9731477, 0.24965483), (0.98869, 0.26420987), + (0.9907113, 0.29266283), (0.9849871, 0.31338844), (0.97151726, 0.32638666), + (0.950302, 0.3316575), (0.9213412, 0.32920095), (0.8798396, 0.34078467), + (0.8257972, 0.36640862), (0.7592141, 0.40607283), (0.6800901, 0.4597773), + (0.6450007, 0.49104902), (0.6539457, 0.49988794), (0.7069251, 0.48629412), + (0.803939, 0.45026752), (0.877913, 0.4226481), (0.9288472, 0.40343583), + (0.9567415, 0.39263073), (0.961596, 0.39023277), (0.9419039, 0.40523484), + (0.89766514, 0.43763688), (0.8288798, 0.48743892), (0.7355478, 0.55464095), + (0.6655121, 0.60063523), (0.6187727, 0.6254217), (0.5953296, 0.62900037), + (0.5951828, 0.6113712), (0.57516366, 0.60261106), (0.53527224, 0.6027198), + (0.4755085, 0.6116975), (0.3958725, 0.6295441), (0.33913234, 0.6398651), + (0.30528808, 0.6426605), (0.2943397, 0.6379303), (0.30628717, 0.6256744), + (0.32149008, 0.6093727), (0.33994842, 0.5890249), (0.36166218, 0.5646312), + (0.38663134, 0.5361916), (0.3919681, 0.520893), (0.3776725, 0.5187355), + (0.34374446, 0.52971905), (0.29018405, 0.5538437), (0.25439468, 0.5678829), + (0.2363764, 0.5718367), (0.23612918, 0.56570506), (0.25365302, 0.549488), + (0.2733971, 0.5246488), (0.29536137, 0.49118724), (0.3195459, 0.4491035), + (0.34595063, 0.39839754), (0.3647463, 0.3590396), (0.37593287, 0.33102974), + (0.37951034, 0.31436795), (0.37547874, 0.30905423), (0.36070493, 0.3204269), + (0.33518887, 0.348486), (0.29893062, 0.3932315), (0.25193012, 0.45466346)]) -simp1 = simplify(polyarea, DouglasPeucker(0.01)) -simp2 = simplify(polyarea, DouglasPeucker(0.05)) -simp3 = simplify(polyarea, DouglasPeucker(0.10)) +simp1 = simplify(poly, SelingerSimplification(0.01)) +simp2 = simplify(poly, SelingerSimplification(0.05)) +simp3 = simplify(poly, SelingerSimplification(0.10)) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], polyarea) +viz(fig[1,1], poly) viz(fig[1,2], simp1) viz(fig[2,1], simp2) viz(fig[2,2], simp3) fig ``` -## Selinger +## DouglasPeuckerSimplification ```@docs -Selinger +DouglasPeuckerSimplification ``` ```@example simplification -simp1 = simplify(polyarea, Selinger(0.01)) -simp2 = simplify(polyarea, Selinger(0.05)) -simp3 = simplify(polyarea, Selinger(0.10)) +simp1 = simplify(poly, DouglasPeuckerSimplification(0.01)) +simp2 = simplify(poly, DouglasPeuckerSimplification(0.05)) +simp3 = simplify(poly, DouglasPeuckerSimplification(0.10)) fig = Mke.Figure(size = (800, 800)) -viz(fig[1,1], polyarea) +viz(fig[1,1], poly) viz(fig[1,2], simp1) viz(fig[2,1], simp2) viz(fig[2,2], simp3) fig -``` \ No newline at end of file +``` + +## MinMaxSimplification + +```@docs +MinMaxSimplification +``` + +```@example simplification +simp1 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=20)) +simp2 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=10)) +simp3 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=5)) + +fig = Mke.Figure(size = (800, 800)) +viz(fig[1,1], poly) +viz(fig[1,2], simp1) +viz(fig[2,1], simp2) +viz(fig[2,2], simp3) +fig +``` diff --git a/src/Meshes.jl b/src/Meshes.jl index af8212197..a701a6a40 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -457,10 +457,10 @@ export # simplification SimplificationMethod, - DouglasPeucker, - Selinger, + SelingerSimplification, + DouglasPeuckerSimplification, + MinMaxSimplification, simplify, - decimate, # bounding boxes boundingbox, diff --git a/src/simplification.jl b/src/simplification.jl index 0f09b5ab3..148d2f0c5 100644 --- a/src/simplification.jl +++ b/src/simplification.jl @@ -12,15 +12,11 @@ abstract type SimplificationMethod end """ simplify(object, method) -Simplify `object` with given `method`. - -See also [`decimate`](@ref). +Simplify geometric `object` with given `method`. """ function simplify end -simplify(box::Box, method::SimplificationMethod) = _simplify(box, Val(embeddim(box)), method) - -_simplify(box::Box, ::Val{2}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method)) +simplify(box::Box{𝔼{2}}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method)) simplify(polygon::Polygon, method::SimplificationMethod) = PolyArea([simplify(ring, method) for ring in rings(polygon)]) @@ -32,22 +28,6 @@ simplify(domain::Domain, method::SimplificationMethod) = GeometrySet([simplify(e # IMPLEMENTATIONS # ---------------- -include("simplification/douglaspeucker.jl") include("simplification/selinger.jl") - -# ---------- -# UTILITIES -# ---------- - -""" - decimate(object, [ϵ]; min=3, max=typemax(Int), maxiter=10) - -Simplify `object` with an appropriate simplification method -and deviation tolerance `ϵ`. - -If the tolerance `ϵ` is not provided, perform binary search until -the number of vertices is between `min` and `max` or until the -number of iterations reaches a maximum `maxiter`. -""" -decimate(object, ϵ=nothing; min=3, max=typemax(Int), maxiter=10) = - simplify(object, DouglasPeucker(ϵ, min=min, max=max, maxiter=maxiter)) +include("simplification/douglaspeucker.jl") +include("simplification/minmax.jl") diff --git a/src/simplification/douglaspeucker.jl b/src/simplification/douglaspeucker.jl index ef744a8de..519915203 100644 --- a/src/simplification/douglaspeucker.jl +++ b/src/simplification/douglaspeucker.jl @@ -3,14 +3,12 @@ # ------------------------------------------------------------------ """ - DouglasPeucker([ϵ]; min=3, max=typemax(Int), maxiter=10) + DouglasPeuckerSimplification(τ) -Simplify geometries with Douglas-Peucker algorithm. The higher -is the tolerance `ϵ`, the more aggressive is the simplification. +Douglas-Peucker's simplification algorithm with tolerance `τ` in length units +(default to meter). -If the tolerance `ϵ` is not provided, perform binary search until -the number of vertices is between `min` and `max` or until the -number of iterations reaches a maximum `maxiter`. +The higher is the tolerance, the more aggressive is the simplification. ## References @@ -18,69 +16,21 @@ number of iterations reaches a maximum `maxiter`. the Number of Points Required to Represent a Digitized Line or its Caricature](https://www.sciencedirect.com/science/article/abs/pii/0167839691900198) """ -struct DouglasPeucker{T} <: SimplificationMethod - ϵ::T - min::Int - max::Int - maxiter::Int +struct DouglasPeuckerSimplification{ℒ<:Len} <: SimplificationMethod + τ::ℒ + DouglasPeuckerSimplification(τ::ℒ) where {ℒ<:Len} = new{float(ℒ)}(τ) end -DouglasPeucker(ϵ=nothing; min=3, max=typemax(Int), maxiter=10) = DouglasPeucker(_ϵ(ϵ), min, max, maxiter) +DouglasPeuckerSimplification(τ) = DouglasPeuckerSimplification(addunit(τ, u"m")) -_ϵ(ϵ::Nothing) = ϵ -_ϵ(ϵ::Number) = _ϵ(addunit(ϵ, u"m")) -_ϵ(ϵ::Len) = float(ϵ) - -function simplify(chain::Chain, method::DouglasPeucker) - v = if isnothing(method.ϵ) - # perform binary search with other parameters - βsimplify(vertices(chain), method.min, method.max, method.maxiter) - else - # perform Douglas-Peucker ϵ-simplification - ϵsimplify(vertices(chain), method.ϵ) - end |> collect - isclosed(chain) ? Ring(v) : Rope(v) -end - -# simplification by means of binary search -function βsimplify(v::AbstractVector{P}, min, max, maxiter) where {P<:Point} - i = 0 - u = v - n = length(u) - a = zero(lentype(P)) - b = initeps(u) - while !(min ≤ n ≤ max) && i < maxiter - # midpoint candidate - ϵ = (a + b) / 2 - - # evaluate at midpoint - u = ϵsimplify(v, ϵ) - n = length(u) - - # binary search - n < min && (b = ϵ) - n > max && (a = ϵ) - - i += 1 - end - - u -end - -# initial ϵ guess for a given chain -function initeps(v::AbstractVector{P}) where {P<:Point} - n = length(v) - ϵ = typemax(lentype(P)) - l = Line(first(v), last(v)) - d = [evaluate(Euclidean(), v[i], l) for i in 2:(n - 1)] - ϵ = quantile(d, 0.25) - 2ϵ +function simplify(chain::Chain, method::DouglasPeuckerSimplification) + verts = _douglaspeucker(vertices(chain), method.τ) |> collect + isclosed(chain) ? Ring(verts) : Rope(verts) end # simplify chain assuming it is open -function ϵsimplify(v::AbstractVector{P}, ϵ) where {P<:Point} - # find vertex with maximum distance - # to reference line +function _douglaspeucker(v::AbstractVector{P}, τ) where {P<:Point} + # find vertex with maximum distance to reference line l = Line(first(v), last(v)) imax, dmax = 0, zero(lentype(P)) for i in 2:(length(v) - 1) @@ -91,11 +41,11 @@ function ϵsimplify(v::AbstractVector{P}, ϵ) where {P<:Point} end end - if dmax < ϵ + if dmax < τ [first(v), last(v)] else - v₁ = ϵsimplify(v[begin:imax], ϵ) - v₂ = ϵsimplify(v[imax:end], ϵ) + v₁ = _douglaspeucker(v[begin:imax], τ) + v₂ = _douglaspeucker(v[imax:end], τ) [v₁[begin:(end - 1)]; v₂] end end diff --git a/src/simplification/minmax.jl b/src/simplification/minmax.jl new file mode 100644 index 000000000..72caf794c --- /dev/null +++ b/src/simplification/minmax.jl @@ -0,0 +1,54 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + MinMaxSimplification(method; min=3, max=typemax(Int), maxiter=10) + +Simplify geometries with binary search algorithm and a parent simplification `method`. + +The simplification is performed until the number of vertices is in the `[min, max]` +range or until a maximum number of iterations `maxiter` is reached. +""" +struct MinMaxSimplification{M} <: SimplificationMethod + method::M + min::Int + max::Int + maxiter::Int +end + +MinMaxSimplification(method; min=3, max=typemax(Int), maxiter=10) = MinMaxSimplification(method, min, max, maxiter) + +function simplify(c::Chain, m::MinMaxSimplification) + i = 0 + s = c + n = nvertices(c) + a, b = _initrange(c) + while !(m.min ≤ n ≤ m.max) && i < m.maxiter + # midpoint candidate + τ = (a + b) / 2 + + # evaluate at midpoint + s = simplify(c, m.method(τ)) + n = nvertices(s) + + # binary search + n < m.min && (b = τ) + n > m.max && (a = τ) + + i += 1 + end + + s +end + +# initial range for binary search +function _initrange(c) + v = vertices(c) + n = length(v) + l = Line(first(v), last(v)) + d = [evaluate(Euclidean(), v[i], l) for i in 2:(n - 1)] + z = zero(lentype(c)) + τ = quantile(d, 0.25) + (z, 2τ) +end diff --git a/src/simplification/selinger.jl b/src/simplification/selinger.jl index 3793ae04e..ed155b7a9 100644 --- a/src/simplification/selinger.jl +++ b/src/simplification/selinger.jl @@ -3,30 +3,31 @@ # ------------------------------------------------------------------ """ - Selinger(ϵ) + SelingerSimplification(τ) -Simplify geometries with Selinger's algorithm, which attempts to -minimize the number of vertices and the deviation of vertices -to the resulting segments based on deviation tolerance `ϵ`. +Selinger's simplification algorithm with tolerance `τ` in length units +(default to meter). + +The higher is the tolerance, the more aggressive is the simplification. ## References -* Selinger, P. 2003. [Potrace: A polygon-based tracing algorithm] +* SelingerSimplification, P. 2003. [Potrace: A polygon-based tracing algorithm] (https://potrace.sourceforge.net/potrace.pdf) """ -struct Selinger{ℒ<:Len} <: SimplificationMethod - ϵ::ℒ - Selinger(ϵ::ℒ) where {ℒ<:Len} = new{float(ℒ)}(ϵ) +struct SelingerSimplification{ℒ<:Len} <: SimplificationMethod + τ::ℒ + SelingerSimplification(τ::ℒ) where {ℒ<:Len} = new{float(ℒ)}(τ) end -Selinger(ϵ) = Selinger(addunit(ϵ, u"m")) +SelingerSimplification(τ) = SelingerSimplification(addunit(τ, u"m")) -function simplify(chain::Chain, method::Selinger) +function simplify(chain::Chain, method::SelingerSimplification) ℒ = lentype(chain) 𝒜 = typeof(zero(ℒ)^2) # retrieve parameters - ϵ = method.ϵ + τ = method.τ # vertices as circular vector v = vertices(chain) @@ -37,15 +38,12 @@ function simplify(chain::Chain, method::Selinger) P = Dict{Tuple{Int,Int},𝒜}() for i in 1:n, o in 1:(n - 2) j = i + o - i₊ = i + 1 - j₋ = j - 1 - jₙ = mod1(j, n) l = Line(p[i], p[j]) - δ = [evaluate(Euclidean(), p[k], l) for k in i₊:j₋] - if all(<(ϵ), δ) + δ = [evaluate(Euclidean(), p[k], l) for k in (i + 1):(j - 1)] + if all(<(τ), δ) dᵢⱼ = norm(p[j] - p[i]) - σᵢⱼ = o == 1 ? zero(ℒ) : sqrt(sum(abs2, δ) / length(δ)) - P[(i, jₙ)] = dᵢⱼ * σᵢⱼ + σᵢⱼ = o == 1 ? zero(ℒ) : norm(δ) + P[(i, mod1(j, n))] = dᵢⱼ * σᵢⱼ end end @@ -71,7 +69,6 @@ function simplify(chain::Chain, method::Selinger) end end - @assert first(bestpath) == last(bestpath) Ring(collect(v[bestpath[begin:(end - 1)]])) end diff --git a/test/simplification.jl b/test/simplification.jl index 0e8b3f9e7..3a71acd2c 100644 --- a/test/simplification.jl +++ b/test/simplification.jl @@ -1,60 +1,47 @@ @testset "Simplification" begin + @testset "Selinger" begin + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) + s1 = simplify(c, SelingerSimplification(T(0.1))) + s2 = simplify(c, SelingerSimplification(T(0.5))) + @test s1 == Ring(cart.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) + @test s2 == Ring(cart.([(1, 0), (2, 2), (0, 2), (0, 0)])) + end + @testset "DouglasPeucker" begin c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = simplify(c, DouglasPeucker(T(0.1))) - s2 = simplify(c, DouglasPeucker(T(0.5))) + s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) + s2 = simplify(c, DouglasPeuckerSimplification(T(0.5))) @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) p = PolyArea(Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) - s1 = simplify(p, DouglasPeucker(T(0.5))) + s1 = simplify(p, DouglasPeuckerSimplification(T(0.5))) @test s1 == PolyArea(Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)]))) m = Multi([p, p]) - s2 = simplify(m, DouglasPeucker(T(0.5))) + s2 = simplify(m, DouglasPeuckerSimplification(T(0.5))) @test s2 == Multi([s1, s1]) d = GeometrySet([p, p]) - s3 = simplify(d, DouglasPeucker(T(0.5))) + s3 = simplify(d, DouglasPeuckerSimplification(T(0.5))) @test s3 == GeometrySet([s1, s1]) - - # perform binary search for ϵ tolerance - c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = simplify(c, DouglasPeucker(T(0.1))) - s2 = simplify(c, DouglasPeucker(max=6)) - @test s1 == s2 - s1 = simplify(c, DouglasPeucker(T(0.5))) - s2 = simplify(c, DouglasPeucker(max=4)) - @test s1 == s2 end - @testset "Selinger" begin + @testset "MinMax" begin + # Selinger c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) - s1 = simplify(c, Selinger(0.1)) - s2 = simplify(c, Selinger(0.5)) - @test s1 == Ring(cart.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) - @test s2 == Ring(cart.([(1, 0), (2, 2), (0, 2), (0, 0)])) - end - - @testset "Utilities" begin - # decimate is a helper function to simplify - # geometries with an appropriate method - b = Box(cart(0, 0), cart(1, 1)) - s = decimate(b, 1.0) - @test s isa Polygon - @test nvertices(s) == 3 - @test boundary(s) == Ring(cart.([(0, 0), (1, 0), (0, 1)])) - - c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = decimate(c, T(0.1)) - s2 = decimate(c, T(0.5)) - @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) + s1 = simplify(c, SelingerSimplification(T(0.1))) + s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=6)) + @test nvertices(s2) ≤ nvertices(s1) + s1 = simplify(c, SelingerSimplification(T(0.5))) + s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=4)) + @test nvertices(s2) ≤ nvertices(s1) + # Douglas-Peucker c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = decimate(c, T(0.1)) - s2 = decimate(c, max=6) - @test s1 == s2 - s1 = decimate(c, T(0.5)) - s2 = decimate(c, max=4) - @test s1 == s2 + s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) + s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=6)) + @test s1 ≗ s2 + s1 = simplify(c, DouglasPeuckerSimplification(T(0.5))) + s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=4)) + @test s1 ≗ s2 end end From bc1ef9cb1c65c329673aef6d8bea390510d3ea21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 23 Aug 2024 17:08:16 -0300 Subject: [PATCH 293/423] Rename Within --> Crop (#1021) * Rename Within --> Crop * Update test/transforms.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/src/transforms.md | 6 +++--- src/Meshes.jl | 2 +- src/transforms.jl | 2 +- src/transforms/{within.jl => crop.jl} | 20 +++++++++--------- test/transforms.jl | 29 +++++++++++++-------------- 5 files changed, 29 insertions(+), 30 deletions(-) rename src/transforms/{within.jl => crop.jl} (72%) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 5d68062b6..caeb0c56e 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -176,15 +176,15 @@ viz(fig[1,2], disk) fig ``` -## Within +## Crop ```@docs -Within +Crop ``` ```@example transforms grid = CartesianGrid(10, 10) -subgrid = grid |> Within(x=(1.5, 6.5), y=(3.5, 8.5)) +subgrid = grid |> Crop(x=(1.5, 6.5), y=(3.5, 8.5)) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) diff --git a/src/Meshes.jl b/src/Meshes.jl index a701a6a40..6d04de839 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -534,7 +534,7 @@ export Proj, LengthUnit, Shadow, - Within, + Crop, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 6ea448e91..25e421103 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -96,7 +96,7 @@ include("transforms/stdcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") -include("transforms/within.jl") +include("transforms/crop.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/within.jl b/src/transforms/crop.jl similarity index 72% rename from src/transforms/within.jl rename to src/transforms/crop.jl index ba47ae7a3..9581790b9 100644 --- a/src/transforms/within.jl +++ b/src/transforms/crop.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - Within(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) + Crop(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) Retain the domain geometries that intersect with `x` limits [`xmax`,`xmax`], `y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units @@ -12,23 +12,23 @@ Retain the domain geometries that intersect with `x` limits [`xmax`,`xmax`], ## Examples ```julia -Within(x=(2, 4)) -Within(x=(1u"km", 3u"km")) -Within(y=(1.2, 1.8), z=(2.4, 3.0)) +Crop(x=(2, 4)) +Crop(x=(1u"km", 3u"km")) +Crop(y=(1.2, 1.8), z=(2.4, 3.0)) ``` """ -struct Within{X,Y,Z} <: GeometricTransform +struct Crop{X,Y,Z} <: GeometricTransform x::X y::Y z::Z end -Within(; x=nothing, y=nothing, z=nothing) = - Within(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) +Crop(; x=nothing, y=nothing, z=nothing) = + Crop(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) -parameters(t::Within) = (; x=t.x, y=t.y, z=t.z) +parameters(t::Crop) = (; x=t.x, y=t.y, z=t.z) -function preprocess(t::Within, d::Domain) +function preprocess(t::Crop, d::Domain) bbox = boundingbox(d) bbox₁ = _overlaps(1, t.x, bbox) bbox₂ = _overlaps(2, t.y, bbox₁) @@ -36,7 +36,7 @@ function preprocess(t::Within, d::Domain) indices(d, bbox₃) end -function apply(t::Within, d::Domain) +function apply(t::Crop, d::Domain) inds = preprocess(t, d) n = view(d, inds) n, nothing diff --git a/test/transforms.jl b/test/transforms.jl index dbc7148ba..6955caa44 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1638,21 +1638,20 @@ @test r == SimpleMesh(f.(vertices(d)), topology(d)) end - @testset "Within" begin - @test !isaffine(Within(x=(T(2), T(4)))) - @test !TB.isrevertible(Within(x=(T(2), T(4)))) - @test !TB.isinvertible(Within(x=(T(2), T(4)))) - @test TB.parameters(Within(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) - @test TB.parameters(Within(y=(T(2) * u"km", T(4) * u"km"))) == - (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) - @test TB.parameters(Within(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) - @test_throws ArgumentError Within(x=(T(2) * u"°", T(4) * u"°")) + @testset "Crop" begin + @test !isaffine(Crop(x=(T(2), T(4)))) + @test !TB.isrevertible(Crop(x=(T(2), T(4)))) + @test !TB.isinvertible(Crop(x=(T(2), T(4)))) + @test TB.parameters(Crop(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) + @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) + @test TB.parameters(Crop(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) + @test_throws ArgumentError Crop(x=(T(2) * u"°", T(4) * u"°")) # --------- # POINTSET # --------- - f = Within(x=(T(1.5), T(3.5))) + f = Crop(x=(T(1.5), T(3.5))) d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) r, c = TB.apply(f, d) @test r == PointSet([cart(2, 1), cart(3, 1)]) @@ -1661,7 +1660,7 @@ # GEOMETRYSET # ------------ - f = Within(x=(T(1.5), T(3.5))) + f = Crop(x=(T(1.5), T(3.5))) t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) t2 = t1 |> Translate(T(2), T(2)) t3 = t2 |> Translate(T(2), T(2)) @@ -1673,7 +1672,7 @@ # CARTESIANGRID # -------------- - f = Within(z=(T(1.5), T(4.5))) + f = Crop(z=(T(1.5), T(4.5))) d = cartgrid(10, 10, 10) r, c = TB.apply(f, d) @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) @@ -1682,7 +1681,7 @@ # RECTILINEARGRID # ---------------- - f = Within(y=(T(3.5), T(6.5))) + f = Crop(y=(T(3.5), T(6.5))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) @@ -1691,7 +1690,7 @@ # STRUCTUREDGRID # --------------- - f = Within(x=(T(5.5), T(8.5))) + f = Crop(x=(T(5.5), T(8.5))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) @@ -1700,7 +1699,7 @@ # SIMPLEMESH # ----------- - f = Within(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) + f = Crop(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) d = convert(SimpleMesh, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) From 254dbaaedbee83790aaca0c7549470b3ada39595 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Sat, 24 Aug 2024 10:56:19 -0300 Subject: [PATCH 294/423] Use TestItems.jl (#1024) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use TestItems.jl * Minor adjusts * More adjustments * More adjustments * More adjustments * More fixes * More fixes * More fixes * More fixes * Update test/primitives.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/sampling.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/transforms.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/transforms.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/traversing.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * More fixes * Adjust CI.yml * Adjust CY.yml * Adjust CI.yml * Adjust runtests.jl --------- Co-authored-by: Júlio Hoffimann Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/CI.yml | 7 +- test/Project.toml | 2 + test/boundingboxes.jl | 2 +- test/clamping.jl | 2 +- test/clipping.jl | 138 +- test/coarsening.jl | 58 +- test/complement.jl | 2 +- test/connectivities.jl | 2 +- test/crs.jl | 294 ++- test/discretization.jl | 1062 ++++++----- test/distances.jl | 2 +- test/domains.jl | 2 +- test/dummy.jl | 14 - test/hulls.jl | 312 ++- test/intersections.jl | 2363 ++++++++++++----------- test/matrices.jl | 152 +- test/merging.jl | 2 +- test/meshes.jl | 1642 ++++++++-------- test/multigeoms.jl | 2 +- test/neighborhoods.jl | 128 +- test/neighborsearch.jl | 192 +- test/orientation.jl | 2 +- test/partitioning.jl | 687 ++++--- test/pointification.jl | 2 +- test/polytopes.jl | 1834 +++++++++--------- test/predicates.jl | 1168 ++++++------ test/primitives.jl | 2507 ++++++++++++------------ test/rand.jl | 2 +- test/refinement.jl | 264 ++- test/runtests.jl | 158 +- test/sampling.jl | 825 ++++---- test/sets.jl | 178 +- test/sideof.jl | 2 +- test/simplification.jl | 82 +- test/sorting.jl | 32 +- test/subdomains.jl | 2 +- test/supportfun.jl | 2 +- test/tesselation.jl | 80 +- test/testutils.jl | 57 + test/tolerances.jl | 2 +- test/topologies.jl | 1030 +++++----- test/toporelations.jl | 1080 ++++++----- test/trajecs.jl | 58 +- test/transformedgeoms.jl | 2 +- test/transforms.jl | 3908 +++++++++++++++++++------------------- test/traversing.jl | 120 +- test/utils.jl | 2 +- test/vectors.jl | 2 +- test/viewing.jl | 2 +- test/winding.jl | 2 +- 50 files changed, 10183 insertions(+), 10289 deletions(-) delete mode 100644 test/dummy.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6902eb325..8e45b53e8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -16,7 +16,7 @@ env: JULIA_NUM_THREADS: 2 jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ matrix.float }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -30,6 +30,9 @@ jobs: - windows-latest arch: - x64 + float: + - Float32 + - Float64 steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 @@ -48,6 +51,8 @@ jobs: ${{ runner.os }}- - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 + env: + FLOAT_TYPE: ${{ matrix.float }} - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v4 with: diff --git a/test/Project.toml b/test/Project.toml index c32ce5155..96147db24 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -15,5 +15,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" +TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" TransformsBase = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index a2573e3dc..4d34cd371 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -1,4 +1,4 @@ -@testset "Bounding boxes" begin +@testitem "Bounding boxes" setup = [Setup] begin p = cart(0, 0) @test boundingbox(p) == Box(p, p) @test @allocated(boundingbox(p)) < 50 diff --git a/test/clamping.jl b/test/clamping.jl index c1d603b8c..7a725905c 100644 --- a/test/clamping.jl +++ b/test/clamping.jl @@ -1,4 +1,4 @@ -@testset "Clamping" begin +@testitem "Clamping" setup = [Setup] begin box = Box((zero(T), zero(T)), (one(T), one(T))) @test clamp(cart(0.5, 0.5), box) == cart(0.5, 0.5) @test clamp(cart(-1, 0.5), box) == cart(0, 0.5) diff --git a/test/clipping.jl b/test/clipping.jl index 53d727a38..c6bc5e0db 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -1,78 +1,76 @@ -@testset "Clipping" begin - @testset "SutherlandHodgman" begin - # triangle - poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) - @test issimple(clipped) - @test all(vertices(clipped) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) +@testitem "SutherlandHodgman" setup = [Setup] begin + # triangle + poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + clipped = clip(poly, other, SutherlandHodgman()) + @test issimple(clipped) + @test all(vertices(clipped) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) - # octagon - poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) - @test !issimple(clipped) - @test all( - vertices(clipped) .≈ - [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] - ) + # octagon + poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + clipped = clip(poly, other, SutherlandHodgman()) + @test !issimple(clipped) + @test all( + vertices(clipped) .≈ + [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] + ) - # inside - poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) - @test issimple(clipped) - @test all(vertices(clipped) .≈ vertices(poly)) + # inside + poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + clipped = clip(poly, other, SutherlandHodgman()) + @test issimple(clipped) + @test all(vertices(clipped) .≈ vertices(poly)) - # outside - poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) - @test isnothing(clipped) + # outside + poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + clipped = clip(poly, other, SutherlandHodgman()) + @test isnothing(clipped) - # surrounded - poly = Hexagon(cart(0, 2), cart(-2, 2), cart(-2, 0), cart(0, -2), cart(2, -2), cart(2, 0)) - other = Hexagon(cart(1, 0), cart(0, 1), cart(-1, 1), cart(-1, 0), cart(0, -1), cart(1, -1)) - clipped = clip(poly, other, SutherlandHodgman()) - @test issimple(clipped) - @test all(vertices(clipped) .≈ vertices(other)) + # surrounded + poly = Hexagon(cart(0, 2), cart(-2, 2), cart(-2, 0), cart(0, -2), cart(2, -2), cart(2, 0)) + other = Hexagon(cart(1, 0), cart(0, 1), cart(-1, 1), cart(-1, 0), cart(0, -1), cart(1, -1)) + clipped = clip(poly, other, SutherlandHodgman()) + @test issimple(clipped) + @test all(vertices(clipped) .≈ vertices(other)) - # PolyArea with box - outer = Ring(cart(8, 0), cart(4, 8), cart(2, 8), cart(-2, 0), cart(0, 0), cart(1, 2), cart(5, 2), cart(6, 0)) - inner = Ring(cart(4, 4), cart(2, 4), cart(3, 6)) - poly = PolyArea([outer, inner]) - other = Box(cart(0, 1), cart(3, 7)) - clipped = clip(poly, other, SutherlandHodgman()) - crings = rings(clipped) - @test !issimple(clipped) - @test all( - vertices(crings[1]) .≈ - [cart(1.5, 7.0), cart(0.0, 4.0), cart(0.0, 1.0), cart(0.5, 1.0), cart(1.0, 2.0), cart(3.0, 2.0), cart(3.0, 7.0)] - ) - @test all(vertices(crings[2]) .≈ [cart(3.0, 4.0), cart(2.0, 4.0), cart(3.0, 6.0)]) + # PolyArea with box + outer = Ring(cart(8, 0), cart(4, 8), cart(2, 8), cart(-2, 0), cart(0, 0), cart(1, 2), cart(5, 2), cart(6, 0)) + inner = Ring(cart(4, 4), cart(2, 4), cart(3, 6)) + poly = PolyArea([outer, inner]) + other = Box(cart(0, 1), cart(3, 7)) + clipped = clip(poly, other, SutherlandHodgman()) + crings = rings(clipped) + @test !issimple(clipped) + @test all( + vertices(crings[1]) .≈ + [cart(1.5, 7.0), cart(0.0, 4.0), cart(0.0, 1.0), cart(0.5, 1.0), cart(1.0, 2.0), cart(3.0, 2.0), cart(3.0, 7.0)] + ) + @test all(vertices(crings[2]) .≈ [cart(3.0, 4.0), cart(2.0, 4.0), cart(3.0, 6.0)]) - # PolyArea with outer ring outside and inner ring inside - outer = Ring(cart(8, 0), cart(2, 6), cart(-4, 0)) - inner = Ring(cart(1, 3), cart(3, 3), cart(3, 1), cart(1, 1)) - poly = PolyArea([outer, inner]) - other = Quadrangle(cart(4, 4), cart(0, 4), cart(0, 0), cart(4, 0)) - clipped = clip(poly, other, SutherlandHodgman()) - @test !issimple(clipped) - crings = rings(clipped) - @test all(vertices(crings[1]) .≈ vertices(other)) - @test all(vertices(crings[2]) .≈ vertices(inner)) + # PolyArea with outer ring outside and inner ring inside + outer = Ring(cart(8, 0), cart(2, 6), cart(-4, 0)) + inner = Ring(cart(1, 3), cart(3, 3), cart(3, 1), cart(1, 1)) + poly = PolyArea([outer, inner]) + other = Quadrangle(cart(4, 4), cart(0, 4), cart(0, 0), cart(4, 0)) + clipped = clip(poly, other, SutherlandHodgman()) + @test !issimple(clipped) + crings = rings(clipped) + @test all(vertices(crings[1]) .≈ vertices(other)) + @test all(vertices(crings[2]) .≈ vertices(inner)) - # PolyArea with one inner ring inside `other` and another inner ring outside `other` - outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) - inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) - inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) - poly = PolyArea([outer, inner₁, inner₂]) - other = PolyArea(Ring(cart(6, 1), cart(7, 2), cart(6, 5), cart(0, 2), cart(1, 1))) - clipped = clip(poly, other, SutherlandHodgman()) - crings = rings(clipped) - @test !issimple(clipped) - @test length(crings) == 2 - @test all(vertices(crings[1]) .≈ [cart(6, 4), cart(6, 5), cart(1, 2.5), cart(1, 1), cart(5, 2)]) - @test all(vertices(crings[2]) .≈ [cart(3.0, 3.0), cart(3.0, 3.5), cart(10 / 3, 11 / 3), cart(4.0, 3.0)]) - end + # PolyArea with one inner ring inside `other` and another inner ring outside `other` + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) + poly = PolyArea([outer, inner₁, inner₂]) + other = PolyArea(Ring(cart(6, 1), cart(7, 2), cart(6, 5), cart(0, 2), cart(1, 1))) + clipped = clip(poly, other, SutherlandHodgman()) + crings = rings(clipped) + @test !issimple(clipped) + @test length(crings) == 2 + @test all(vertices(crings[1]) .≈ [cart(6, 4), cart(6, 5), cart(1, 2.5), cart(1, 1), cart(5, 2)]) + @test all(vertices(crings[2]) .≈ [cart(3.0, 3.0), cart(3.0, 3.5), cart(10 / 3, 11 / 3), cart(4.0, 3.0)]) end diff --git a/test/coarsening.jl b/test/coarsening.jl index 15d6e9fd5..49dbe4af9 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -1,33 +1,31 @@ -@testset "Coarsening" begin - @testset "RegularCoarsening" begin - # 2D grids - grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 10)) - @test coarsen(grid, RegularCoarsening(2)) == tgrid - rgrid = convert(RectilinearGrid, grid) - trgrid = convert(RectilinearGrid, tgrid) - @test coarsen(rgrid, RegularCoarsening(2)) == trgrid - sgrid = convert(StructuredGrid, grid) - tsgrid = convert(StructuredGrid, tgrid) - @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid - tfgrid = TransformedGrid(grid, Identity()) - @test coarsen(tfgrid, RegularCoarsening(2)) == coarsen(grid, RegularCoarsening(2)) +@testitem "RegularCoarsening" setup = [Setup] begin + # 2D grids + grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 10)) + @test coarsen(grid, RegularCoarsening(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test coarsen(rgrid, RegularCoarsening(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test coarsen(sgrid, RegularCoarsening(2)) == tsgrid + tfgrid = TransformedGrid(grid, Identity()) + @test coarsen(tfgrid, RegularCoarsening(2)) == coarsen(grid, RegularCoarsening(2)) - grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) - tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 5)) - @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid + grid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(20, 20)) + tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 5)) + @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid - # 3D grids - grid = cartgrid(100, 100, 100) - tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(50, 25, 20)) - @test coarsen(grid, RegularCoarsening(2, 4, 5)) == tgrid - rgrid = convert(RectilinearGrid, grid) - trgrid = convert(RectilinearGrid, tgrid) - @test coarsen(rgrid, RegularCoarsening(2, 4, 5)) == trgrid - sgrid = convert(StructuredGrid, grid) - tsgrid = convert(StructuredGrid, tgrid) - @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid - tfgrid = TransformedGrid(grid, Identity()) - @test coarsen(tfgrid, RegularCoarsening(2, 4, 5)) == coarsen(grid, RegularCoarsening(2, 4, 5)) - end + # 3D grids + grid = cartgrid(100, 100, 100) + tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(50, 25, 20)) + @test coarsen(grid, RegularCoarsening(2, 4, 5)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test coarsen(rgrid, RegularCoarsening(2, 4, 5)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid + tfgrid = TransformedGrid(grid, Identity()) + @test coarsen(tfgrid, RegularCoarsening(2, 4, 5)) == coarsen(grid, RegularCoarsening(2, 4, 5)) end diff --git a/test/complement.jl b/test/complement.jl index 0d9bf0586..4d79fb693 100644 --- a/test/complement.jl +++ b/test/complement.jl @@ -1,4 +1,4 @@ -@testset "complement" begin +@testitem "Complement of geometries" setup = [Setup] begin τ = atol(T) t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) diff --git a/test/connectivities.jl b/test/connectivities.jl index bc967d447..a16195418 100644 --- a/test/connectivities.jl +++ b/test/connectivities.jl @@ -1,4 +1,4 @@ -@testset "Connectivities" begin +@testitem "Connectivities" setup = [Setup] begin # basic tests c = connect((1, 2, 3), Triangle) @test pltype(c) == Triangle diff --git a/test/crs.jl b/test/crs.jl index c3036c4a9..0eaba0281 100644 --- a/test/crs.jl +++ b/test/crs.jl @@ -1,150 +1,148 @@ -@testset "CRS" begin - @testset "Projected" begin - g = merc(1, 1) - @test crs(g) <: Mercator{WGS84Latest} - g = Ray(merc(0, 0), vector(1, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Line(merc(0, 0), merc(1, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = BezierCurve(merc(0, 0), merc(1, 1), merc(2, 0)) - @test crs(g) <: Mercator{WGS84Latest} - g = Box(merc(0, 0), merc(1, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Ball(merc(0, 0), T(1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Sphere(merc(0, 0), T(1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Segment(merc(0, 0), merc(1, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Rope(merc(0, 0), merc(1, 0), merc(0, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Ring(merc(0, 0), merc(1, 0), merc(0, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = PolyArea(merc(0, 0), merc(1, 0), merc(0, 1)) - @test crs(g) <: Mercator{WGS84Latest} - g = Multi([merc(0, 0), merc(1, 1)]) - @test crs(g) <: Mercator{WGS84Latest} - t1 = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) - t2 = Triangle(merc(1, 1), merc(2, 1), merc(1, 2)) - d = GeometrySet([t1, t2]) - @test crs(d) <: Mercator{WGS84Latest} - d = PointSet([merc(0, 0), merc(1, 1)]) - @test crs(d) <: Mercator{WGS84Latest} - d = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) - @test crs(d) <: Mercator{WGS84Latest} - p = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - @test crs(d) <: Mercator{WGS84Latest} - end +@testitem "Projected CRS" setup = [Setup] begin + g = merc(1, 1) + @test crs(g) <: Mercator{WGS84Latest} + g = Ray(merc(0, 0), vector(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Line(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = BezierCurve(merc(0, 0), merc(1, 1), merc(2, 0)) + @test crs(g) <: Mercator{WGS84Latest} + g = Box(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Ball(merc(0, 0), T(1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Sphere(merc(0, 0), T(1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Segment(merc(0, 0), merc(1, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Rope(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Ring(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = PolyArea(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(g) <: Mercator{WGS84Latest} + g = Multi([merc(0, 0), merc(1, 1)]) + @test crs(g) <: Mercator{WGS84Latest} + t1 = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + t2 = Triangle(merc(1, 1), merc(2, 1), merc(1, 2)) + d = GeometrySet([t1, t2]) + @test crs(d) <: Mercator{WGS84Latest} + d = PointSet([merc(0, 0), merc(1, 1)]) + @test crs(d) <: Mercator{WGS84Latest} + d = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) + @test crs(d) <: Mercator{WGS84Latest} + p = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + @test crs(d) <: Mercator{WGS84Latest} +end - @testset "Geographic" begin - g = latlon(1, 1) - @test crs(g) <: LatLon{WGS84Latest} - g = Ray(latlon(0, 0), vector(1, 1, 1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Line(latlon(0, 0), latlon(1, 1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = BezierCurve(latlon(0, 0), latlon(1, 1), latlon(0, 2)) - @test crs(g) <: LatLon{WGS84Latest} - g = Box(latlon(0, 180), latlon(45, 90)) - @test crs(g) <: LatLon{WGS84Latest} - g = Ball(latlon(0, 0), T(1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Sphere(latlon(0, 0), T(1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Ellipsoid((T(3), T(2), T(1)), latlon(0, 0)) - @test crs(g) <: LatLon{WGS84Latest} - p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) - g = Disk(p, T(2)) - @test crs(g) <: LatLon{WGS84Latest} - p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) - g = Circle(p, T(2)) - @test crs(g) <: LatLon{WGS84Latest} - b = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) - t = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - g = Cylinder(b, t, T(5)) - @test crs(g) <: LatLon{WGS84Latest} - b = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - t = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) - g = CylinderSurface(b, t, T(5)) - @test crs(g) <: LatLon{WGS84Latest} - g = ParaboloidSurface(latlon(0, 0), T(1), T(2)) - @test crs(g) <: LatLon{WGS84Latest} - p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - d = Disk(p, T(2)) - a = latlon(90, 0) - g = Cone(d, a) - @test crs(g) <: LatLon{WGS84Latest} - p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - d = Disk(p, T(2)) - a = latlon(90, 0) - g = ConeSurface(d, a) - @test crs(g) <: LatLon{WGS84Latest} - pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - db = Disk(pb, T(1)) - pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) - dt = Disk(pt, T(2)) - g = Frustum(db, dt) - @test crs(g) <: LatLon{WGS84Latest} - pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) - db = Disk(pb, T(1)) - pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) - dt = Disk(pt, T(2)) - g = FrustumSurface(db, dt) - @test crs(g) <: LatLon{WGS84Latest} - g = Torus(latlon(0, 0), vector(1, 0, 0), T(2), T(1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Segment(latlon(0, 0), latlon(1, 1)) - @test crs(g) <: LatLon{WGS84Latest} - g = Rope(latlon(0, 0), latlon(0, 1), latlon(1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Ring(latlon(0, 0), latlon(0, 1), latlon(1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Tetrahedron(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(90, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Hexahedron( - latlon(0, 45), - latlon(0, 135), - latlon(0, -135), - latlon(0, -45), - latlon(1, 45), - latlon(1, 135), - latlon(1, -135), - latlon(1, -45) - ) - @test crs(g) <: LatLon{WGS84Latest} - g = Pyramid(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(90, 0)) - @test crs(g) <: LatLon{WGS84Latest} - g = Wedge(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(1, 0), latlon(1, 90), latlon(1, -90)) - @test crs(g) <: LatLon{WGS84Latest} - g = Multi([latlon(0, 0), latlon(1, 1)]) - @test crs(g) <: LatLon{WGS84Latest} - t1 = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) - t2 = Triangle(latlon(1, 1), latlon(1, 2), latlon(2, 1)) - d = GeometrySet([t1, t2]) - @test crs(d) <: LatLon{WGS84Latest} - d = PointSet([latlon(0, 0), latlon(1, 1)]) - @test crs(d) <: LatLon{WGS84Latest} - d = CartesianGrid((10, 10, 10), latlon(0, 0), (T(1), T(1), T(1))) - @test crs(d) <: LatLon{WGS84Latest} - p = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - @test crs(d) <: LatLon{WGS84Latest} - d = CylindricalTrajectory([latlon(0, 0), latlon(1, 1), latlon(0, 2)]) - @test crs(d) <: LatLon{WGS84Latest} - end +@testitem "Geographic CRS" setup = [Setup] begin + g = latlon(1, 1) + @test crs(g) <: LatLon{WGS84Latest} + g = Ray(latlon(0, 0), vector(1, 1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Line(latlon(0, 0), latlon(1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = BezierCurve(latlon(0, 0), latlon(1, 1), latlon(0, 2)) + @test crs(g) <: LatLon{WGS84Latest} + g = Box(latlon(0, 180), latlon(45, 90)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ball(latlon(0, 0), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Sphere(latlon(0, 0), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ellipsoid((T(3), T(2), T(1)), latlon(0, 0)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Disk(p, T(2)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(0, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Circle(p, T(2)) + @test crs(g) <: LatLon{WGS84Latest} + b = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + t = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = Cylinder(b, t, T(5)) + @test crs(g) <: LatLon{WGS84Latest} + b = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + t = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + g = CylinderSurface(b, t, T(5)) + @test crs(g) <: LatLon{WGS84Latest} + g = ParaboloidSurface(latlon(0, 0), T(1), T(2)) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + d = Disk(p, T(2)) + a = latlon(90, 0) + g = Cone(d, a) + @test crs(g) <: LatLon{WGS84Latest} + p = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + d = Disk(p, T(2)) + a = latlon(90, 0) + g = ConeSurface(d, a) + @test crs(g) <: LatLon{WGS84Latest} + pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + db = Disk(pb, T(1)) + pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + dt = Disk(pt, T(2)) + g = Frustum(db, dt) + @test crs(g) <: LatLon{WGS84Latest} + pb = Plane(latlon(-90, 0), vector(1, 0, 0), vector(0, 1, 0)) + db = Disk(pb, T(1)) + pt = Plane(latlon(90, 0), vector(1, 0, 0), vector(0, 1, 0)) + dt = Disk(pt, T(2)) + g = FrustumSurface(db, dt) + @test crs(g) <: LatLon{WGS84Latest} + g = Torus(latlon(0, 0), vector(1, 0, 0), T(2), T(1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Segment(latlon(0, 0), latlon(1, 1)) + @test crs(g) <: LatLon{WGS84Latest} + g = Rope(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Ring(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Tetrahedron(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(90, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Hexahedron( + latlon(0, 45), + latlon(0, 135), + latlon(0, -135), + latlon(0, -45), + latlon(1, 45), + latlon(1, 135), + latlon(1, -135), + latlon(1, -45) + ) + @test crs(g) <: LatLon{WGS84Latest} + g = Pyramid(latlon(0, 45), latlon(0, 135), latlon(0, -135), latlon(0, -45), latlon(90, 0)) + @test crs(g) <: LatLon{WGS84Latest} + g = Wedge(latlon(0, 0), latlon(0, 90), latlon(0, -90), latlon(1, 0), latlon(1, 90), latlon(1, -90)) + @test crs(g) <: LatLon{WGS84Latest} + g = Multi([latlon(0, 0), latlon(1, 1)]) + @test crs(g) <: LatLon{WGS84Latest} + t1 = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + t2 = Triangle(latlon(1, 1), latlon(1, 2), latlon(2, 1)) + d = GeometrySet([t1, t2]) + @test crs(d) <: LatLon{WGS84Latest} + d = PointSet([latlon(0, 0), latlon(1, 1)]) + @test crs(d) <: LatLon{WGS84Latest} + d = CartesianGrid((10, 10, 10), latlon(0, 0), (T(1), T(1), T(1))) + @test crs(d) <: LatLon{WGS84Latest} + p = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + @test crs(d) <: LatLon{WGS84Latest} + d = CylindricalTrajectory([latlon(0, 0), latlon(1, 1), latlon(0, 2)]) + @test crs(d) <: LatLon{WGS84Latest} end diff --git a/test/discretization.jl b/test/discretization.jl index 28c802683..ebd728bac 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -1,564 +1,562 @@ -@testset "Discretization" begin - @testset "FanTriangulation" begin - pts = cart.([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)]) - tris = [Triangle(pts[1], pts[i], pts[i + 1]) for i in 2:(length(pts) - 1)] - hex = Hexagon(pts...) - mesh = discretize(hex, FanTriangulation()) - @test nvertices(mesh) == 6 - @test nelements(mesh) == 4 - @test eltype(mesh) <: Triangle - @test vertices(mesh) == pts - @test collect(elements(mesh)) == tris - end +@testitem "FanTriangulation" setup = [Setup] begin + pts = cart.([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.75, 1.5), (0.25, 1.5), (0.0, 1.0)]) + tris = [Triangle(pts[1], pts[i], pts[i + 1]) for i in 2:(length(pts) - 1)] + hex = Hexagon(pts...) + mesh = discretize(hex, FanTriangulation()) + @test nvertices(mesh) == 6 + @test nelements(mesh) == 4 + @test eltype(mesh) <: Triangle + @test vertices(mesh) == pts + @test collect(elements(mesh)) == tris +end - @testset "DehnTriangulation" begin - octa = Octagon( - cart(0.2, 0.2), - cart(0.5, -0.5), - cart(0.8, 0.2), - cart(1.5, 0.5), - cart(0.8, 0.8), - cart(0.5, 1.5), - cart(0.2, 0.8), - cart(-0.5, 0.5) - ) - mesh = discretize(octa, DehnTriangulation()) - @test nvertices(mesh) == 8 - @test nelements(mesh) == 6 +@testitem "DehnTriangulation" setup = [Setup] begin + octa = Octagon( + cart(0.2, 0.2), + cart(0.5, -0.5), + cart(0.8, 0.2), + cart(1.5, 0.5), + cart(0.8, 0.8), + cart(0.5, 1.5), + cart(0.2, 0.8), + cart(-0.5, 0.5) + ) + mesh = discretize(octa, DehnTriangulation()) + @test nvertices(mesh) == 8 + @test nelements(mesh) == 6 + @test eltype(mesh) <: Triangle + + # type stability tests + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @inferred discretize(poly, DehnTriangulation()) + + octa = Octagon( + cart(0.2, 0.2, 0.0), + cart(0.5, -0.5, 0.0), + cart(0.8, 0.2, 0.0), + cart(1.5, 0.5, 0.0), + cart(0.8, 0.8, 0.0), + cart(0.5, 1.5, 0.0), + cart(0.2, 0.8, 0.0), + cart(-0.5, 0.5, 0.0) + ) + mesh = discretize(octa, DehnTriangulation()) + @test nvertices(mesh) == 8 + @test nelements(mesh) == 6 + @test eltype(mesh) <: Triangle +end + +@testitem "HeldTriangulation" setup = [Setup] begin + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) + @test Meshes.earsccw(𝒫) == [2, 4, 5] + + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)])) + @test Meshes.earsccw(𝒫) == [2, 4] + + 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (1, 2)])) + @test Meshes.earsccw(𝒫) == [2, 4] + + 𝒫 = Ring(cart.([(0, 0), (1, 1), (1, 2)])) + @test Meshes.earsccw(𝒫) == [] + + 𝒫 = Ring( + cart.([ + (0.443339268495331, 0.283757618605357), + (0.497822414616971, 0.398142813114205), + (0.770343126156527, 0.201815462842808), + (0.761236456732531, 0.330085709922366), + (0.985658085510286, 0.221530395507904), + (0.877899962498139, 0.325516131702896), + (0.561404274882782, 0.540334008885703), + (0.949459768187313, 0.396227653478068), + (0.594962560615951, 0.584927547374551), + (0.324208409133154, 0.607290684450708), + (0.424085089823892, 0.493532112641353), + (0.209843417261654, 0.590030658255966), + (0.27993878548962, 0.525162463476181), + (0.385557753911967, 0.322338556632868) + ]) + ) + @test Meshes.earsccw(𝒫) == [1, 3, 5, 6, 8, 10, 12, 14] + + points = cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) + connec = connect.([(4, 5, 6), (3, 4, 6), (3, 6, 1), (1, 2, 3)], Triangle) + target = SimpleMesh(points, connec) + poly = PolyArea(points) + mesh = discretize(poly, HeldTriangulation(shuffle=false)) + @test mesh == target + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 + + # https://github.com/JuliaGeometry/Meshes.jl/issues/675 + poly = PolyArea( + cart.([ + (1.1794224993e7, 1.7289506814e7), + (1.1794045018e7, 1.7289446822e7), + (1.1793985026e7, 1.7289486817e7), + (1.1793965029e7, 1.7289586803e7), + (1.1794105009e7, 1.7289766778e7), + (1.1794184998e7, 1.7289866764e7), + (1.179424499e7, 1.728996675e7), + (1.179424499e7, 1.7290106731e7), + (1.1794344976e7, 1.7290246711e7), + (1.1794364973e7, 1.7290386692e7), + (1.1794504954e7, 1.7290406689e7), + (1.1794724923e7, 1.729018672e7), + (1.1794624937e7, 1.7289946753e7), + (1.1794624937e7, 1.7289806772e7), + (1.1794564946e7, 1.7289706786e7), + (1.1794424965e7, 1.7289626797e7) + ]) + ) + rng = StableRNG(123) + mesh = discretize(poly, HeldTriangulation(rng)) + @test nvertices(mesh) == 16 + @test nelements(mesh) == 14 + + # https://github.com/JuliaGeometry/Meshes.jl/issues/738 + poly = PolyArea( + cart.([ + (-0.5, 0.3296139), + (-0.19128194, -0.5), + (-0.37872985, 0.29592824), + (0.21377224, -0.0076110554), + (-0.20127837, 0.24671146) + ]) + ) + rng = StableRNG(123) + mesh = discretize(poly, HeldTriangulation(rng)) + @test nvertices(mesh) == 5 + @test nelements(mesh) == 3 +end + +@testitem "DelaunayTriangulation" setup = [Setup] begin + rng = StableRNG(123) + poly = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 2), cart(0, 1)) + mesh = discretize(poly, DelaunayTriangulation(rng)) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 +end + +@testitem "Misc triangulations" setup = [Setup] begin + rng = StableRNG(123) + for method in [DehnTriangulation(), HeldTriangulation(rng), DelaunayTriangulation(rng)] + triangle = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + mesh = discretize(triangle, method) + @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(0, 1)] + @test nelements(mesh) == 1 + @test mesh[1] ≗ triangle + + quadrangle = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + mesh = discretize(quadrangle, method) + elms = collect(elements(mesh)) + @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)] + @test eltype(elms) <: Triangle + @test length(elms) == 2 + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + t = Triangle(cart(1, 0), cart(2, 1), cart(1, 1)) + m = Multi([q, t]) + mesh = discretize(m, method) + elms = collect(elements(mesh)) + @test vertices(mesh) == [pointify(q); pointify(t)] + @test vertices(elms[1]) ⊆ vertices(q) + @test vertices(elms[2]) ⊆ vertices(q) + @test vertices(elms[3]) ⊆ vertices(t) + @test eltype(elms) <: Triangle + @test length(elms) == 3 + + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) + bpoly = poly |> Bridge(T(0.01)) + mesh = discretizewithin(boundary(bpoly), method) + @test nvertices(mesh) == 16 + @test nelements(mesh) == 14 + @test all(t -> area(t) > zero(ℳ)^2, mesh) + + # 3D chains + chain = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)])) + mesh = discretizewithin(chain, method) + @test vertices(mesh) == vertices(chain) @test eltype(mesh) <: Triangle + @test nelements(mesh) == 2 - # type stability tests - poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @inferred discretize(poly, DehnTriangulation()) - - octa = Octagon( - cart(0.2, 0.2, 0.0), - cart(0.5, -0.5, 0.0), - cart(0.8, 0.2, 0.0), - cart(1.5, 0.5, 0.0), - cart(0.8, 0.8, 0.0), - cart(0.5, 1.5, 0.0), - cart(0.2, 0.8, 0.0), - cart(-0.5, 0.5, 0.0) - ) - mesh = discretize(octa, DehnTriangulation()) - @test nvertices(mesh) == 8 - @test nelements(mesh) == 6 + # latlon coordinates + poly = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) + mesh = discretize(poly, method) + @test vertices(mesh) == vertices(poly) @test eltype(mesh) <: Triangle + @test nelements(mesh) == 2 + + # preserves order of vertices + poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + mesh = simplexify(poly) + @test pointify(mesh) == pointify(poly) end +end - @testset "HeldTriangulation" begin - 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) - @test Meshes.earsccw(𝒫) == [2, 4, 5] +@testitem "Difficult triangulations" setup = [Setup] begin + rng = StableRNG(123) + for method in [DehnTriangulation(), HeldTriangulation(rng)] + poly = readpoly(T, joinpath(datadir, "taubin.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (1, 2)])) - @test Meshes.earsccw(𝒫) == [2, 4] + poly = readpoly(T, joinpath(datadir, "poly1.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - 𝒫 = Ring(cart.([(0, 0), (1, 0), (1, 1), (1, 2)])) - @test Meshes.earsccw(𝒫) == [2, 4] + poly = readpoly(T, joinpath(datadir, "poly2.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - 𝒫 = Ring(cart.([(0, 0), (1, 1), (1, 2)])) - @test Meshes.earsccw(𝒫) == [] + poly = readpoly(T, joinpath(datadir, "poly3.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - 𝒫 = Ring( - cart.([ - (0.443339268495331, 0.283757618605357), - (0.497822414616971, 0.398142813114205), - (0.770343126156527, 0.201815462842808), - (0.761236456732531, 0.330085709922366), - (0.985658085510286, 0.221530395507904), - (0.877899962498139, 0.325516131702896), - (0.561404274882782, 0.540334008885703), - (0.949459768187313, 0.396227653478068), - (0.594962560615951, 0.584927547374551), - (0.324208409133154, 0.607290684450708), - (0.424085089823892, 0.493532112641353), - (0.209843417261654, 0.590030658255966), - (0.27993878548962, 0.525162463476181), - (0.385557753911967, 0.322338556632868) - ]) - ) - @test Meshes.earsccw(𝒫) == [1, 3, 5, 6, 8, 10, 12, 14] - - points = cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)]) - connec = connect.([(4, 5, 6), (3, 4, 6), (3, 6, 1), (1, 2, 3)], Triangle) - target = SimpleMesh(points, connec) - poly = PolyArea(points) - mesh = discretize(poly, HeldTriangulation(shuffle=false)) - @test mesh == target + poly = readpoly(T, joinpath(datadir, "poly4.line")) + mesh = discretize(poly, method) @test Set(vertices(poly)) == Set(vertices(mesh)) @test nelements(mesh) == length(vertices(mesh)) - 2 - # https://github.com/JuliaGeometry/Meshes.jl/issues/675 - poly = PolyArea( - cart.([ - (1.1794224993e7, 1.7289506814e7), - (1.1794045018e7, 1.7289446822e7), - (1.1793985026e7, 1.7289486817e7), - (1.1793965029e7, 1.7289586803e7), - (1.1794105009e7, 1.7289766778e7), - (1.1794184998e7, 1.7289866764e7), - (1.179424499e7, 1.728996675e7), - (1.179424499e7, 1.7290106731e7), - (1.1794344976e7, 1.7290246711e7), - (1.1794364973e7, 1.7290386692e7), - (1.1794504954e7, 1.7290406689e7), - (1.1794724923e7, 1.729018672e7), - (1.1794624937e7, 1.7289946753e7), - (1.1794624937e7, 1.7289806772e7), - (1.1794564946e7, 1.7289706786e7), - (1.1794424965e7, 1.7289626797e7) - ]) - ) - rng = StableRNG(123) - mesh = discretize(poly, HeldTriangulation(rng)) - @test nvertices(mesh) == 16 - @test nelements(mesh) == 14 + poly = readpoly(T, joinpath(datadir, "poly5.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - # https://github.com/JuliaGeometry/Meshes.jl/issues/738 - poly = PolyArea( - cart.([ - (-0.5, 0.3296139), - (-0.19128194, -0.5), - (-0.37872985, 0.29592824), - (0.21377224, -0.0076110554), - (-0.20127837, 0.24671146) - ]) - ) - rng = StableRNG(123) - mesh = discretize(poly, HeldTriangulation(rng)) - @test nvertices(mesh) == 5 - @test nelements(mesh) == 3 - end + poly = readpoly(T, joinpath(datadir, "smooth1.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - @testset "DelaunayTriangulation" begin - rng = StableRNG(123) - poly = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 2), cart(0, 1)) - mesh = discretize(poly, DelaunayTriangulation(rng)) + poly = readpoly(T, joinpath(datadir, "smooth2.line")) + mesh = discretize(poly, method) @test Set(vertices(poly)) == Set(vertices(mesh)) @test nelements(mesh) == length(vertices(mesh)) - 2 - end - @testset "Miscellaneous triangulations" begin - rng = StableRNG(123) - for method in [DehnTriangulation(), HeldTriangulation(rng), DelaunayTriangulation(rng)] - triangle = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - mesh = discretize(triangle, method) - @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(0, 1)] - @test nelements(mesh) == 1 - @test mesh[1] ≗ triangle - - quadrangle = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - mesh = discretize(quadrangle, method) - elms = collect(elements(mesh)) - @test vertices(mesh) == [cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)] - @test eltype(elms) <: Triangle - @test length(elms) == 2 - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - t = Triangle(cart(1, 0), cart(2, 1), cart(1, 1)) - m = Multi([q, t]) - mesh = discretize(m, method) - elms = collect(elements(mesh)) - @test vertices(mesh) == [pointify(q); pointify(t)] - @test vertices(elms[1]) ⊆ vertices(q) - @test vertices(elms[2]) ⊆ vertices(q) - @test vertices(elms[3]) ⊆ vertices(t) - @test eltype(elms) <: Triangle - @test length(elms) == 3 - - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) - bpoly = poly |> Bridge(T(0.01)) - mesh = discretizewithin(boundary(bpoly), method) - @test nvertices(mesh) == 16 - @test nelements(mesh) == 14 - @test all(t -> area(t) > zero(ℳ)^2, mesh) - - # 3D chains - chain = Ring(cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 1)])) - mesh = discretizewithin(chain, method) - @test vertices(mesh) == vertices(chain) - @test eltype(mesh) <: Triangle - @test nelements(mesh) == 2 - - # latlon coordinates - poly = PolyArea(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) - mesh = discretize(poly, method) - @test vertices(mesh) == vertices(poly) - @test eltype(mesh) <: Triangle - @test nelements(mesh) == 2 - - # preserves order of vertices - poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - mesh = simplexify(poly) - @test pointify(mesh) == pointify(poly) - end - end + poly = readpoly(T, joinpath(datadir, "smooth3.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - @testset "Difficult triangulations" begin - rng = StableRNG(123) - for method in [DehnTriangulation(), HeldTriangulation(rng)] - poly = readpoly(T, joinpath(datadir, "taubin.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "poly1.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "poly2.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "poly3.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "poly4.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "poly5.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "smooth1.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "smooth2.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "smooth3.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "smooth4.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - poly = readpoly(T, joinpath(datadir, "smooth5.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - - # https://github.com/JuliaGeometry/Meshes.jl/issues/738 - poly = readpoly(T, joinpath(datadir, "hole1.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == 32 - - poly = readpoly(T, joinpath(datadir, "hole2.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == 30 - - poly = readpoly(T, joinpath(datadir, "hole3.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == 32 - - poly = readpoly(T, joinpath(datadir, "hole4.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == 30 - - poly = readpoly(T, joinpath(datadir, "hole5.line")) - mesh = discretize(poly, method) - @test Set(vertices(poly)) == Set(vertices(mesh)) - @test nelements(mesh) == 32 - end - - if T == Float64 - poly = PolyArea( - cart.([ - (-48.03012478813999, -18.323912004531923), - (-48.030125176275845, -18.323904748608573), - (-48.03017873307118, -18.323925747019675), - (-48.03017945243984, -18.32393728592407), - (-48.030185785831904, -18.32394021501982), - (-48.03017951837907, -18.323938343610457), - (-48.030124261780436, -18.32392184444903), - (-48.0301218833633, -18.323910661117687) - ]) - ) - mesh = discretize(poly) - @test nvertices(mesh) == 8 - @test nelements(mesh) == 6 - end - - # degenerate triangle - poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) - mesh = discretize(poly) - @test nvertices(mesh) == 3 - @test nelements(mesh) == 1 - @test vertices(mesh) == [cart(0, 0), cart(0, 0), cart(0, 0)] - @test mesh[1] == Triangle(cart(0, 0), cart(0, 0), cart(0, 0)) - end + poly = readpoly(T, joinpath(datadir, "smooth4.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - @testset "ManualDiscretization" begin - box = Box(cart(0, 0, 0), cart(1, 1, 1)) - hexa = Hexahedron(pointify(box)...) - bmesh = discretize(box, ManualDiscretization()) - hmesh = discretize(hexa, ManualDiscretization()) - @test bmesh == hmesh - @test nvertices(bmesh) == 8 - @test nelements(bmesh) == 5 - end + poly = readpoly(T, joinpath(datadir, "smooth5.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 - @testset "RegularDiscretization" begin - bezier = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) - mesh = discretize(bezier, RegularDiscretization(10)) - @test nvertices(mesh) == 11 - @test nelements(mesh) == 10 - @test eltype(mesh) <: Segment - @test nvertices.(mesh) ⊆ [2] - - box = Box(cart(0, 0), cart(2, 2)) - mesh = discretize(box, RegularDiscretization(10)) - @test mesh isa CartesianGrid - @test nvertices(mesh) == 121 - @test nelements(mesh) == 100 - @test eltype(mesh) <: Quadrangle - @test nvertices.(mesh) ⊆ [4] - - sphere = Sphere(cart(0, 0), T(1)) - mesh = discretize(sphere, RegularDiscretization(10)) - @test nvertices(mesh) == 10 - @test nelements(mesh) == 10 - @test eltype(mesh) <: Segment - @test nvertices.(mesh) ⊆ [2] - - sphere = Sphere(cart(0, 0, 0), T(1)) - mesh = discretize(sphere, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - ellips = Ellipsoid((T(3), T(2), T(1))) - mesh = discretize(ellips, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - ball = Ball(cart(0, 0), T(1)) - mesh = discretize(ball, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 1 - @test nelements(mesh) == 10 * 10 + 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - disk = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)) - mesh = discretize(disk, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 + 1 - @test nelements(mesh) == 10 * 10 + 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - cyl = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) - mesh = discretize(cyl, RegularDiscretization(10)) - @test nvertices(mesh) == 11 * 10 * 11 + 11 - @test nelements(mesh) == 11 * 10 * 10 - @test eltype(mesh) <: Polyhedron - @test nvertices.(mesh) ⊆ [6, 8] - - cylsurf = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) - mesh = discretize(cylsurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * 11 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - consurf = ConeSurface(Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)), cart(0, 0, 1)) - mesh = discretize(consurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * 11 + 2 - @test nelements(mesh) == 10 * 10 + 2 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - parsurf = rand(ParaboloidSurface) - mesh = discretize(parsurf, RegularDiscretization(10)) - @test nvertices(mesh) == 10 * (10 + 1) - @test nelements(mesh) == 10 * 10 - @test eltype(mesh) <: Ngon - @test nvertices.(mesh) ⊆ [3, 4] - - poly = PolyArea(cart.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) - mesh = discretize(poly, RegularDiscretization(50)) - @test mesh isa Meshes.SubGrid - grid = parent(mesh) - @test grid isa CartesianGrid - @test eltype(mesh) <: Quadrangle - @test all(intersects(poly), mesh) - end + # https://github.com/JuliaGeometry/Meshes.jl/issues/738 + poly = readpoly(T, joinpath(datadir, "hole1.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == 32 + + poly = readpoly(T, joinpath(datadir, "hole2.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == 30 + + poly = readpoly(T, joinpath(datadir, "hole3.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == 32 - @testset "Discretize" begin - ball = Ball(cart(0, 0), T(1)) - mesh = discretize(ball) - @test !(eltype(mesh) <: Triangle) - @test !(eltype(mesh) <: Quadrangle) - @test nelements(mesh) == 2550 - - sphere = Sphere(cart(0, 0, 0), T(1)) - mesh = discretize(sphere) - @test !(eltype(mesh) <: Triangle) - @test !(eltype(mesh) <: Quadrangle) - @test nelements(mesh) == 2600 - - cyl = Cylinder(T(1)) - mesh = discretize(cyl) - @test !(eltype(mesh) <: Wedge) - @test !(eltype(mesh) <: Hexahedron) - @test nelements(mesh) == 300 - - cylsurf = CylinderSurface(T(1)) - mesh = discretize(cylsurf) - @test !(eltype(mesh) <: Triangle) - @test !(eltype(mesh) <: Quadrangle) - @test nelements(mesh) == 200 - - grid = CartesianGrid(10) - @test discretize(grid) == grid - - mesh = SimpleMesh(randpoint2(3), connect.([(1, 2, 3)])) - @test discretize(mesh) == mesh + poly = readpoly(T, joinpath(datadir, "hole4.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == 30 + + poly = readpoly(T, joinpath(datadir, "hole5.line")) + mesh = discretize(poly, method) + @test Set(vertices(poly)) == Set(vertices(mesh)) + @test nelements(mesh) == 32 end - @testset "Simplexify" begin - # simplexify is a helper function that calls an - # appropriate discretization method depending on - # the geometry type that is given to it - box = Box(cart(0), cart(1)) - msh = simplexify(box) - @test eltype(msh) <: Segment - @test topology(msh) == GridTopology(1) - @test nvertices(msh) == 2 - @test nelements(msh) == 1 - @test msh[1] == Segment(cart(0), cart(1)) - - seg = Segment(cart(0), cart(1)) - msh = simplexify(seg) - @test eltype(msh) <: Segment - @test topology(msh) == GridTopology(1) - @test nvertices(msh) == 2 - @test nelements(msh) == 1 - @test msh[1] == Segment(cart(0), cart(1)) - - chn = Rope(cart.([(0, 0), (1, 0), (1, 1)])) - msh = simplexify(chn) - @test eltype(msh) <: Segment - @test nvertices(msh) == 3 - @test nelements(msh) == 2 - @test msh[1] == Segment(cart(0, 0), cart(1, 0)) - @test msh[2] == Segment(cart(1, 0), cart(1, 1)) - chn = Ring(cart.([(0, 0), (1, 0), (1, 1)])) - msh = simplexify(chn) - @test eltype(msh) <: Segment - @test nvertices(msh) == 3 - @test nelements(msh) == 3 - @test msh[1] == Segment(cart(0, 0), cart(1, 0)) - @test msh[2] == Segment(cart(1, 0), cart(1, 1)) - @test msh[3] == Segment(cart(1, 1), cart(0, 0)) - - sph = Sphere(cart(0, 0), T(1)) - msh = simplexify(sph) - @test eltype(msh) <: Segment - @test nvertices(msh) == nelements(msh) - - bez = BezierCurve(cart.([(0, 0), (1, 0), (1, 1)])) - msh = simplexify(bez) - @test eltype(msh) <: Segment - @test nvertices(msh) == nelements(msh) + 1 - - box = Box(cart(0, 0), cart(1, 1)) - ngon = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - poly = readpoly(T, joinpath(datadir, "taubin.line")) - for geom in [box, ngon, poly] - bound = boundary(geom) - mesh = simplexify(geom) - @test Set(vertices(bound)) == Set(vertices(mesh)) - @test nelements(mesh) == length(vertices(mesh)) - 2 - end - - # triangulation of multi geometries - box1 = Box(cart(0, 0), cart(1, 1)) - box2 = Box(cart(1, 1), cart(2, 2)) - multi = Multi([box1, box2]) - mesh = simplexify(multi) + if T == Float64 + poly = PolyArea( + cart.([ + (-48.03012478813999, -18.323912004531923), + (-48.030125176275845, -18.323904748608573), + (-48.03017873307118, -18.323925747019675), + (-48.03017945243984, -18.32393728592407), + (-48.030185785831904, -18.32394021501982), + (-48.03017951837907, -18.323938343610457), + (-48.030124261780436, -18.32392184444903), + (-48.0301218833633, -18.323910661117687) + ]) + ) + mesh = discretize(poly) @test nvertices(mesh) == 8 - @test nelements(mesh) == 4 + @test nelements(mesh) == 6 + end - # triangulation of spheres - sphere = Sphere(cart(0, 0, 0), T(1)) - mesh = simplexify(sphere) - @test eltype(mesh) <: Triangle - xs = to.(vertices(mesh)) - @test all(x -> norm(x) ≈ oneunit(ℳ), xs) + # degenerate triangle + poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) + mesh = discretize(poly) + @test nvertices(mesh) == 3 + @test nelements(mesh) == 1 + @test vertices(mesh) == [cart(0, 0), cart(0, 0), cart(0, 0)] + @test mesh[1] == Triangle(cart(0, 0), cart(0, 0), cart(0, 0)) +end - # triangulation of cylinder surfaces - cylsurf = CylinderSurface(T(1)) - mesh = simplexify(cylsurf) - @test eltype(mesh) <: Triangle - xs = to.(vertices(mesh)) - @test all(x -> -oneunit(ℳ) ≤ x[1] ≤ oneunit(ℳ), xs) - @test all(x -> -oneunit(ℳ) ≤ x[2] ≤ oneunit(ℳ), xs) - @test all(x -> zero(ℳ) ≤ x[3] ≤ oneunit(ℳ), xs) - - # triangulation of balls - ball = Ball(cart(0, 0), T(1)) - mesh = simplexify(ball) - @test eltype(mesh) <: Triangle - xs = to.(vertices(mesh)) - @test all(x -> norm(x) ≤ oneunit(ℳ) + eps(T) * u"m", xs) +@testitem "ManualDiscretization" setup = [Setup] begin + box = Box(cart(0, 0, 0), cart(1, 1, 1)) + hexa = Hexahedron(pointify(box)...) + bmesh = discretize(box, ManualDiscretization()) + hmesh = discretize(hexa, ManualDiscretization()) + @test bmesh == hmesh + @test nvertices(bmesh) == 8 + @test nelements(bmesh) == 5 +end + +@testitem "RegularDiscretization" setup = [Setup] begin + bezier = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) + mesh = discretize(bezier, RegularDiscretization(10)) + @test nvertices(mesh) == 11 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + box = Box(cart(0, 0), cart(2, 2)) + mesh = discretize(box, RegularDiscretization(10)) + @test mesh isa CartesianGrid + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + + sphere = Sphere(cart(0, 0), T(1)) + mesh = discretize(sphere, RegularDiscretization(10)) + @test nvertices(mesh) == 10 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + sphere = Sphere(cart(0, 0, 0), T(1)) + mesh = discretize(sphere, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + ellips = Ellipsoid((T(3), T(2), T(1))) + mesh = discretize(ellips, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + ball = Ball(cart(0, 0), T(1)) + mesh = discretize(ball, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 1 + @test nelements(mesh) == 10 * 10 + 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + disk = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)) + mesh = discretize(disk, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 + 1 + @test nelements(mesh) == 10 * 10 + 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + cyl = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) + mesh = discretize(cyl, RegularDiscretization(10)) + @test nvertices(mesh) == 11 * 10 * 11 + 11 + @test nelements(mesh) == 11 * 10 * 10 + @test eltype(mesh) <: Polyhedron + @test nvertices.(mesh) ⊆ [6, 8] + + cylsurf = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) + mesh = discretize(cylsurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * 11 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + consurf = ConeSurface(Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(1)), cart(0, 0, 1)) + mesh = discretize(consurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * 11 + 2 + @test nelements(mesh) == 10 * 10 + 2 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + parsurf = rand(ParaboloidSurface) + mesh = discretize(parsurf, RegularDiscretization(10)) + @test nvertices(mesh) == 10 * (10 + 1) + @test nelements(mesh) == 10 * 10 + @test eltype(mesh) <: Ngon + @test nvertices.(mesh) ⊆ [3, 4] + + poly = PolyArea(cart.([(0, 0), (0, 1), (1, 2), (2, 1), (2, 0)])) + mesh = discretize(poly, RegularDiscretization(50)) + @test mesh isa Meshes.SubGrid + grid = parent(mesh) + @test grid isa CartesianGrid + @test eltype(mesh) <: Quadrangle + @test all(intersects(poly), mesh) +end - # triangulation of meshes +@testitem "Discretize" setup = [Setup] begin + ball = Ball(cart(0, 0), T(1)) + mesh = discretize(ball) + @test !(eltype(mesh) <: Triangle) + @test !(eltype(mesh) <: Quadrangle) + @test nelements(mesh) == 2550 + + sphere = Sphere(cart(0, 0, 0), T(1)) + mesh = discretize(sphere) + @test !(eltype(mesh) <: Triangle) + @test !(eltype(mesh) <: Quadrangle) + @test nelements(mesh) == 2600 + + cyl = Cylinder(T(1)) + mesh = discretize(cyl) + @test !(eltype(mesh) <: Wedge) + @test !(eltype(mesh) <: Hexahedron) + @test nelements(mesh) == 300 + + cylsurf = CylinderSurface(T(1)) + mesh = discretize(cylsurf) + @test !(eltype(mesh) <: Triangle) + @test !(eltype(mesh) <: Quadrangle) + @test nelements(mesh) == 200 + + grid = CartesianGrid(10) + @test discretize(grid) == grid + + mesh = SimpleMesh(randpoint2(3), connect.([(1, 2, 3)])) + @test discretize(mesh) == mesh +end + +@testitem "Simplexify" setup = [Setup] begin + # simplexify is a helper function that calls an + # appropriate discretization method depending on + # the geometry type that is given to it + box = Box(cart(0), cart(1)) + msh = simplexify(box) + @test eltype(msh) <: Segment + @test topology(msh) == GridTopology(1) + @test nvertices(msh) == 2 + @test nelements(msh) == 1 + @test msh[1] == Segment(cart(0), cart(1)) + + seg = Segment(cart(0), cart(1)) + msh = simplexify(seg) + @test eltype(msh) <: Segment + @test topology(msh) == GridTopology(1) + @test nvertices(msh) == 2 + @test nelements(msh) == 1 + @test msh[1] == Segment(cart(0), cart(1)) + + chn = Rope(cart.([(0, 0), (1, 0), (1, 1)])) + msh = simplexify(chn) + @test eltype(msh) <: Segment + @test nvertices(msh) == 3 + @test nelements(msh) == 2 + @test msh[1] == Segment(cart(0, 0), cart(1, 0)) + @test msh[2] == Segment(cart(1, 0), cart(1, 1)) + chn = Ring(cart.([(0, 0), (1, 0), (1, 1)])) + msh = simplexify(chn) + @test eltype(msh) <: Segment + @test nvertices(msh) == 3 + @test nelements(msh) == 3 + @test msh[1] == Segment(cart(0, 0), cart(1, 0)) + @test msh[2] == Segment(cart(1, 0), cart(1, 1)) + @test msh[3] == Segment(cart(1, 1), cart(0, 0)) + + sph = Sphere(cart(0, 0), T(1)) + msh = simplexify(sph) + @test eltype(msh) <: Segment + @test nvertices(msh) == nelements(msh) + + bez = BezierCurve(cart.([(0, 0), (1, 0), (1, 1)])) + msh = simplexify(bez) + @test eltype(msh) <: Segment + @test nvertices(msh) == nelements(msh) + 1 + + box = Box(cart(0, 0), cart(1, 1)) + ngon = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + poly = readpoly(T, joinpath(datadir, "taubin.line")) + for geom in [box, ngon, poly] + bound = boundary(geom) + mesh = simplexify(geom) + @test Set(vertices(bound)) == Set(vertices(mesh)) + @test nelements(mesh) == length(vertices(mesh)) - 2 + end + + # triangulation of multi geometries + box1 = Box(cart(0, 0), cart(1, 1)) + box2 = Box(cart(1, 1), cart(2, 2)) + multi = Multi([box1, box2]) + mesh = simplexify(multi) + @test nvertices(mesh) == 8 + @test nelements(mesh) == 4 + + # triangulation of spheres + sphere = Sphere(cart(0, 0, 0), T(1)) + mesh = simplexify(sphere) + @test eltype(mesh) <: Triangle + xs = to.(vertices(mesh)) + @test all(x -> norm(x) ≈ oneunit(ℳ), xs) + + # triangulation of cylinder surfaces + cylsurf = CylinderSurface(T(1)) + mesh = simplexify(cylsurf) + @test eltype(mesh) <: Triangle + xs = to.(vertices(mesh)) + @test all(x -> -oneunit(ℳ) ≤ x[1] ≤ oneunit(ℳ), xs) + @test all(x -> -oneunit(ℳ) ≤ x[2] ≤ oneunit(ℳ), xs) + @test all(x -> zero(ℳ) ≤ x[3] ≤ oneunit(ℳ), xs) + + # triangulation of balls + ball = Ball(cart(0, 0), T(1)) + mesh = simplexify(ball) + @test eltype(mesh) <: Triangle + xs = to.(vertices(mesh)) + @test all(x -> norm(x) ≤ oneunit(ℳ) + eps(T) * u"m", xs) + + # triangulation of meshes + grid = cartgrid(3, 3) + mesh = simplexify(grid) + gpts = vertices(grid) + mpts = vertices(mesh) + @test nvertices(mesh) == 16 + @test nelements(mesh) == 18 + @test collect(mpts) == collect(gpts) + @test eltype(mesh) <: Triangle + @test measure(mesh) == measure(grid) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/499 + quad = Quadrangle(cart(0, 1, -1), cart(0, 1, 1), cart(0, -1, 1), cart(0, -1, -1)) + mesh = simplexify(quad) + @test vertices(mesh) == pointify(quad) + + if visualtests grid = cartgrid(3, 3) mesh = simplexify(grid) - gpts = vertices(grid) - mpts = vertices(mesh) - @test nvertices(mesh) == 16 - @test nelements(mesh) == 18 - @test collect(mpts) == collect(gpts) - @test eltype(mesh) <: Triangle - @test measure(mesh) == measure(grid) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/499 - quad = Quadrangle(cart(0, 1, -1), cart(0, 1, 1), cart(0, -1, 1), cart(0, -1, -1)) - mesh = simplexify(quad) - @test vertices(mesh) == pointify(quad) - - if visualtests - grid = cartgrid(3, 3) - mesh = simplexify(grid) - fig = Mke.Figure(size=(600, 300)) - viz(fig[1, 1], grid, showsegments=true) - viz(fig[1, 2], mesh, showsegments=true) - @test_reference "data/triangulate-$T.png" fig - end - - # tetrahedralization - box = Box(cart(0, 0, 0), cart(1, 1, 1)) - hex = Hexahedron(pointify(box)...) - bmesh = simplexify(box) - hmesh = simplexify(hex) - @test bmesh == hmesh - @test nvertices(bmesh) == 8 - @test nelements(bmesh) == 5 + fig = Mke.Figure(size=(600, 300)) + viz(fig[1, 1], grid, showsegments=true) + viz(fig[1, 2], mesh, showsegments=true) + @test_reference "data/triangulate-$T.png" fig end + + # tetrahedralization + box = Box(cart(0, 0, 0), cart(1, 1, 1)) + hex = Hexahedron(pointify(box)...) + bmesh = simplexify(box) + hmesh = simplexify(hex) + @test bmesh == hmesh + @test nvertices(bmesh) == 8 + @test nelements(bmesh) == 5 end diff --git a/test/distances.jl b/test/distances.jl index 45ac83762..ff107856a 100644 --- a/test/distances.jl +++ b/test/distances.jl @@ -1,4 +1,4 @@ -@testset "Distances" begin +@testitem "Distances" setup = [Setup] begin p = cart(0, 1) l = Line(cart(0, 0), cart(1, 0)) @test evaluate(Euclidean(), p, l) == T(1) * u"m" diff --git a/test/domains.jl b/test/domains.jl index c2bab01aa..019b26eda 100644 --- a/test/domains.jl +++ b/test/domains.jl @@ -1,4 +1,4 @@ -@testset "Domain" begin +@testitem "Domain" setup = [Setup] begin # basic properties dom = DummyDomain(cart(0, 0)) @test embeddim(dom) == 2 diff --git a/test/dummy.jl b/test/dummy.jl deleted file mode 100644 index e6400a806..000000000 --- a/test/dummy.jl +++ /dev/null @@ -1,14 +0,0 @@ -# dummy type implementing the Domain trait -struct DummyDomain{M<:Meshes.Manifold,C<:CRS} <: Domain{M,C} - origin::Point{M,C} -end - -function Meshes.element(domain::DummyDomain, ind::Int) - ℒ = Meshes.lentype(domain) - T = Unitful.numtype(ℒ) - c = domain.origin + Vec(ntuple(i -> T(ind) * unit(ℒ), embeddim(domain))) - r = oneunit(ℒ) - Ball(c, r) -end - -Meshes.nelements(d::DummyDomain) = 3 diff --git a/test/hulls.jl b/test/hulls.jl index ce3c2b194..968c499e8 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -1,167 +1,165 @@ -@testset "Hulls" begin - @testset "Basic" begin - for method in [GrahamScan(), JarvisMarch()] - # basic test - pts = randpoint2(100) - chul = hull(pts, method) - @test all(pts .∈ Ref(chul)) - - # duplicated points - pts = [randpoint2(100); randpoint2(100)] - chul = hull(pts, method) - @test all(pts .∈ Ref(chul)) - - # corner cases - pts = cart.([(0, 0)]) - chul = hull(pts, method) - @test chul == cart(0, 0) - pts = cart.([(0, 1), (1, 0)]) - chul = hull(pts, method) - @test chul == Segment(cart(0, 1), cart(1, 0)) - pts = cart.([(1, 0), (0, 0), (0, 1)]) - chul = hull(pts, method) - @test vertices(chul) == cart.([(0, 0), (1, 0), (0, 1)]) - - # original point set is already in hull - pts = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) - chul = hull(pts, method) - verts = vertices(chul) - @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) - - # random points in interior do not affect result - p1 = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) - p2 = cart.([0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10]) - pts = [p1; p2] - chul = hull(pts, method) - verts = vertices(chul) - @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) - - pts = - cart.([ - (0, 5), - (1, 5), - (1, 4), - (2, 4), - (2, 3), - (3, 3), - (4, 3), - (5, 3), - (5, 4), - (6, 4), - (6, 5), - (7, 5), - (7, 6), - (7, 7), - (6, 7), - (6, 8), - (5, 8), - (5, 9), - (4, 9), - (3, 9), - (2, 9), - (2, 8), - (1, 8), - (1, 7), - (0, 7), - (0, 6) - ]) - chul = hull(pts, method) - @test nvertices(chul) < length(pts) - - poly = readpoly(T, joinpath(datadir, "hull.line")) - pts = vertices(poly) - chul = hull(pts, method) - @test nvertices(chul) < length(pts) - - if method == GrahamScan() - # simplifying rectangular hull / triangular - points = [cart(i - 1, j - 1) for i in 1:11 for j in 1:11] - chull = hull(points, method) - @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] - for _ in 1:100 # test presence of interior points doesn't affect the result - push!(points, cart(10 * rand(), 10 * rand())) - end - chull = hull(points, method) - @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] - - points = [cart(-1, 0), cart(0, 0), cart(1, 0), cart(0, 2)] - chull = hull(points, method) - @test vertices(chull) == [cart(-1, 0), cart(1, 0), cart(0, 2)] - - # degenerate cases - points = [cart(0, 0), cart(1, 0), cart(2, 0)] - chull = hull(points, method) - @test vertices(chull) == (cart(0, 0), cart(2, 0)) - - points = [cart(0, 0), cart(1, 0), cart(2, 0), cart(10, 0), cart(100, 0)] - chull = hull(points, method) - @test vertices(chull) == (cart(0, 0), cart(100, 0)) - - # partially collinear - points = [ - cart(2, 0), - cart(4, 0), - cart(6, 0), - cart(10, 0), - cart(12, 1), - cart(14, 3), - cart(14, 6), - cart(14, 9), - cart(13, 10), - cart(11, 11), - cart(8, 12), - cart(3, 11), - cart(0, 8), - cart(0, 7), - cart(0, 6), - cart(0, 5), - cart(0, 4), - cart(0, 3), - cart(0, 2), - cart(1, 0) - ] - chull = hull(points, method) - truth = [ - cart(0, 2), - cart(1, 0), - cart(10, 0), - cart(12, 1), - cart(14, 3), - cart(14, 9), - cart(13, 10), - cart(11, 11), - cart(8, 12), - cart(3, 11), - cart(0, 8) - ] - @test vertices(chull) == truth - push!(points, cart(4, 8), cart(2, 6), cart(6, 2), cart(10, 8), cart(8, 8), cart(10, 6)) - chull = hull(points, method) - @test vertices(chull) == truth +@testitem "Hulls" setup = [Setup] begin + for method in [GrahamScan(), JarvisMarch()] + # basic test + pts = randpoint2(100) + chul = hull(pts, method) + @test all(pts .∈ Ref(chul)) + + # duplicated points + pts = [randpoint2(100); randpoint2(100)] + chul = hull(pts, method) + @test all(pts .∈ Ref(chul)) + + # corner cases + pts = cart.([(0, 0)]) + chul = hull(pts, method) + @test chul == cart(0, 0) + pts = cart.([(0, 1), (1, 0)]) + chul = hull(pts, method) + @test chul == Segment(cart(0, 1), cart(1, 0)) + pts = cart.([(1, 0), (0, 0), (0, 1)]) + chul = hull(pts, method) + @test vertices(chul) == cart.([(0, 0), (1, 0), (0, 1)]) + + # original point set is already in hull + pts = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) + chul = hull(pts, method) + verts = vertices(chul) + @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) + + # random points in interior do not affect result + p1 = cart.([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, -1)]) + p2 = cart.([0.5 .* (rand(), rand()) .+ 0.5 for _ in 1:10]) + pts = [p1; p2] + chul = hull(pts, method) + verts = vertices(chul) + @test verts == cart.([(0, 0), (0.5, -1), (1, 0), (1, 1), (0, 1)]) + + pts = + cart.([ + (0, 5), + (1, 5), + (1, 4), + (2, 4), + (2, 3), + (3, 3), + (4, 3), + (5, 3), + (5, 4), + (6, 4), + (6, 5), + (7, 5), + (7, 6), + (7, 7), + (6, 7), + (6, 8), + (5, 8), + (5, 9), + (4, 9), + (3, 9), + (2, 9), + (2, 8), + (1, 8), + (1, 7), + (0, 7), + (0, 6) + ]) + chul = hull(pts, method) + @test nvertices(chul) < length(pts) + + poly = readpoly(T, joinpath(datadir, "hull.line")) + pts = vertices(poly) + chul = hull(pts, method) + @test nvertices(chul) < length(pts) + + if method == GrahamScan() + # simplifying rectangular hull / triangular + points = [cart(i - 1, j - 1) for i in 1:11 for j in 1:11] + chull = hull(points, method) + @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] + for _ in 1:100 # test presence of interior points doesn't affect the result + push!(points, cart(10 * rand(), 10 * rand())) end + chull = hull(points, method) + @test vertices(chull) == [cart(0, 0), cart(10, 0), cart(10, 10), cart(0, 10)] + + points = [cart(-1, 0), cart(0, 0), cart(1, 0), cart(0, 2)] + chull = hull(points, method) + @test vertices(chull) == [cart(-1, 0), cart(1, 0), cart(0, 2)] + + # degenerate cases + points = [cart(0, 0), cart(1, 0), cart(2, 0)] + chull = hull(points, method) + @test vertices(chull) == (cart(0, 0), cart(2, 0)) + + points = [cart(0, 0), cart(1, 0), cart(2, 0), cart(10, 0), cart(100, 0)] + chull = hull(points, method) + @test vertices(chull) == (cart(0, 0), cart(100, 0)) + + # partially collinear + points = [ + cart(2, 0), + cart(4, 0), + cart(6, 0), + cart(10, 0), + cart(12, 1), + cart(14, 3), + cart(14, 6), + cart(14, 9), + cart(13, 10), + cart(11, 11), + cart(8, 12), + cart(3, 11), + cart(0, 8), + cart(0, 7), + cart(0, 6), + cart(0, 5), + cart(0, 4), + cart(0, 3), + cart(0, 2), + cart(1, 0) + ] + chull = hull(points, method) + truth = [ + cart(0, 2), + cart(1, 0), + cart(10, 0), + cart(12, 1), + cart(14, 3), + cart(14, 9), + cart(13, 10), + cart(11, 11), + cart(8, 12), + cart(3, 11), + cart(0, 8) + ] + @test vertices(chull) == truth + push!(points, cart(4, 8), cart(2, 6), cart(6, 2), cart(10, 8), cart(8, 8), cart(10, 6)) + chull = hull(points, method) + @test vertices(chull) == truth end end +end - @testset "convexhull" begin - @test convexhull(cart(0, 0)) == cart(0, 0) +@testitem "Convex hulls" setup = [Setup] begin + @test convexhull(cart(0, 0)) == cart(0, 0) - @test convexhull(Box(cart(0, 0), cart(1, 1))) == Box(cart(0, 0), cart(1, 1)) + @test convexhull(Box(cart(0, 0), cart(1, 1))) == Box(cart(0, 0), cart(1, 1)) - @test convexhull(Ball(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) - @test convexhull(Ball(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) + @test convexhull(Ball(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) + @test convexhull(Ball(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) - @test convexhull(Sphere(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) - @test convexhull(Sphere(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) + @test convexhull(Sphere(cart(0, 0), T(1))) == Ball(cart(0, 0), T(1)) + @test convexhull(Sphere(cart(1, 1), T(1))) == Ball(cart(1, 1), T(1)) - b1 = Box(cart(0, 0), cart(1, 1)) - b2 = Box(cart(-1, -1), cart(0.5, 0.5)) - @test convexhull(Multi([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) - @test convexhull(GeometrySet([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(-1, -1), cart(0.5, 0.5)) + @test convexhull(Multi([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) + @test convexhull(GeometrySet([b1, b2])) == PolyArea(cart.([(-1, -1), (0.5, -1), (1, 0), (1, 1), (0, 1), (-1, 0.5)])) - b1 = Ball(cart(0, 0), T(1)) - b2 = Box(cart(-1, -1), cart(0, 0)) - h = convexhull(Multi([b1, b2])) - @test cart(-0.8, -0.8) ∈ h - @test cart(0.2, 0.2) ∈ h - end + b1 = Ball(cart(0, 0), T(1)) + b2 = Box(cart(-1, -1), cart(0, 0)) + h = convexhull(Multi([b1, b2])) + @test cart(-0.8, -0.8) ∈ h + @test cart(0.2, 0.2) ∈ h end diff --git a/test/intersections.jl b/test/intersections.jl index 54b93c0df..edcc1c43f 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1,1202 +1,1189 @@ -@testset "Intersections" begin - # helper function for type stability tests - function someornone(g1, g2) - intersection(g1, g2) do I - if type(I) == NotIntersecting - "None" - else - "Some" - end - end - end +@testitem "Point intersection" setup = [Setup] begin + p = cart(0, 0) + q = cart(-1, -1) + b = Box(cart(0, 0), cart(1, 1)) + @test p ∩ p == p + @test q ∩ q == q + @test p ∩ b == b ∩ p == p + @test isnothing(p ∩ q) + @test isnothing(q ∩ b) +end - @testset "Points" begin - p = cart(0, 0) - q = cart(-1, -1) - b = Box(cart(0, 0), cart(1, 1)) - @test p ∩ p == p - @test q ∩ q == q - @test p ∩ b == b ∩ p == p - @test isnothing(p ∩ q) - @test isnothing(q ∩ b) - end +@testitem "Segment intersection" setup = [Setup] begin + # segments in 2D + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0.5, 0.0), cart(2, 0)) + @test s1 ∩ s2 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) + @test s2 ∩ s1 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) + + s1 = Segment(cart(0, 0), cart(1, -1)) + s2 = Segment(cart(0.5, -0.5), cart(1.5, -1.5)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0.5, -0.5), cart(1, -1)) + + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(0, 1)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(-1, 0)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(0, 0), cart(0, 1)) + s2 = Segment(cart(0, 0), cart(0, -1)) + @test s1 ∩ s2 ≈ cart(0, 0) + @test s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(1, 1), cart(1, 2)) + s2 = Segment(cart(1, 1), cart(1, 0)) + @test s1 ∩ s2 ≈ cart(1, 1) + @test s2 ∩ s1 ≈ cart(1, 1) + + s1 = Segment(cart(1, 1), cart(2, 1)) + s2 = Segment(cart(1, 0), cart(3, 0)) + @test s1 ∩ s2 === nothing + @test s2 ∩ s1 === nothing + + s1 = Segment(cart(0.181429364026879, 0.546811355144474), cart(0.38282226144778, 0.107781953228536)) + s2 = Segment(cart(0.412498700935005, 0.212081819871479), cart(0.395936725690311, 0.252041094122474)) + @test s1 ∩ s2 === nothing + @test s2 ∩ s1 === nothing + + s1 = Segment(cart(1, 2), cart(1, 0)) + s2 = Segment(cart(1, 0), cart(1, 1)) + @test s1 ∩ s2 ≈ Segment(cart(1, 1), cart(1, 0)) + @test s2 ∩ s1 ≈ Segment(cart(1, 0), cart(1, 1)) + + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(-2, 0), cart(-1, 0)) + s3 = Segment(cart(-1, 0), cart(-2, 0)) + @test s1 ∩ s2 === s2 ∩ s1 === nothing + @test s1 ∩ s3 === s3 ∩ s1 === nothing + + s1 = Segment(cart(-1, 0), cart(0, 0)) + s2 = Segment(cart(0, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0, 0) + + s1 = Segment(cart(-1, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) + + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) + + s1 = Segment(cart(0, 0), cart(3, 0)) + s2 = Segment(cart(1, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 + + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(1, 0), cart(2, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 + + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(1, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(1, 0), cart(2, 0)) + + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(2, 0), cart(3, 0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2, 0) + + s1 = Segment(cart(0, 0), cart(2, 0)) + s2 = Segment(cart(3, 0), cart(4, 0)) + @test s1 ∩ s2 === s2 ∩ s1 === nothing + + s1 = Segment(cart(2, 1), cart(1, 2)) + s2 = Segment(cart(1, 0), cart(1, 1)) + @test s1 ∩ s2 === s2 ∩ s1 === nothing + + s1 = Segment(cart(1.5, 1.5), cart(3.0, 1.5)) + s2 = Segment(cart(3.0, 1.0), cart(2.0, 2.0)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2.5, 1.5) + + s1 = Segment(cart(0.94495744, 0.53224397), cart(0.94798386, 0.5344541)) + s2 = Segment(cart(0.94798386, 0.5344541), cart(0.9472896, 0.5340202)) + @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0.94798386, 0.5344541) + + s₁ = Segment(cart(0, 0), cart(3, 4)) + s₂ = Segment(cart(1, 2), cart(3, -2)) + s₃ = Segment(cart(2, 0), cart(-2, 0)) + s₄ = Segment(cart(0, 0), cart(1, 2)) + s₅ = Segment(cart(1, 2), cart(3, 4)) + s₆ = Segment(cart(-1, -4 / 3), cart(0, 0)) + s₇ = Segment(cart(1, 2), cart(0, 4)) + s₈ = Segment(cart(4, 16 / 3), cart(3, 4)) + + s₉ = Segment(cart(-1, 5), cart(1, 4)) + s₁₀ = Segment(cart(1, 4), cart(-1, 5)) + s₁₁ = Segment(cart(-2, 5.5), cart(-0.8, 4.9)) + s₁₂ = Segment(cart(-0.8, 4.9), cart(-2, 5.5)) + s₁₃ = Segment(cart(-0.5, 4.75), cart(0.2, 4.4)) + s₁₄ = Segment(cart(0.2, 4.4), cart(-0.5, 4.75)) + s₁₅ = Segment(cart(0.5, 4.25), cart(1, 4)) + s₁₆ = Segment(cart(1, 4), cart(0.5, 4.25)) + s₁₇ = Segment(cart(2, 3.5), cart(1.5, 3.75)) + s₁₈ = Segment(cart(1.5, 3.75), cart(2, 3.5)) + + @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ cart(1.2, 1.6) # CASE 1: Crossing Segments + @test intersection(s₁, s₂) |> type == Crossing + @test intersection(s₂, s₁) |> type == Crossing + + @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ cart(0, 0) # CASE 2: EdgeTouching (s₁(0)) + @test intersection(s₁, s₃) |> type == EdgeTouching + @test intersection(s₃, s₁) |> type == EdgeTouching + + @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ cart(2, 0) # CASE 2: EdgeTouching (s₃(1)) + @test intersection(s₂, s₃) |> type == EdgeTouching + @test intersection(s₃, s₂) |> type == EdgeTouching + + @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) + @test intersection(s₁, s₄) |> type == CornerTouching + @test intersection(s₄, s₁) |> type == CornerTouching + + @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) + @test intersection(s₂, s₄) |> type == CornerTouching + @test intersection(s₄, s₂) |> type == CornerTouching + + @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) + @test intersection(s₂, s₄) |> type == CornerTouching + @test intersection(s₄, s₂) |> type == CornerTouching + + @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear + @test intersection(s₁, s₆) |> type == CornerTouching + @test intersection(s₆, s₁) |> type == CornerTouching + + @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear + @test intersection(s₂, s₇) |> type == CornerTouching + @test intersection(s₇, s₂) |> type == CornerTouching + + @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear + @test intersection(s₁, s₈) |> type == CornerTouching + @test intersection(s₈, s₁) |> type == CornerTouching + + @test s₉ ∩ s₉ ≈ s₉ # CASE 4: Overlapping (same segment) + @test intersection(s₉, s₉) |> type == Overlapping + + @test s₉ ∩ s₁₀ ≈ s₉ # CASE 4: Overlapping (same segment, flipped points) + @test s₁₀ ∩ s₉ ≈ s₁₀ + @test intersection(s₉, s₁₀) |> type == Overlapping + @test intersection(s₁₀, s₉) |> type == Overlapping + + @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) + @test intersection(s₉, s₁₁) |> type == Overlapping + @test intersection(s₁₁, s₉) |> type == Overlapping + + @test s₉ ∩ s₁₂ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) + @test s₁₂ ∩ s₉ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # flipped Points in Segment + @test intersection(s₉, s₁₂) |> type == Overlapping + @test intersection(s₁₂, s₉) |> type == Overlapping + + @test s₁₀ ∩ s₁₁ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) + @test s₁₁ ∩ s₁₀ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # flipped Points in Segment + @test intersection(s₁₀, s₁₁) |> type == Overlapping + @test intersection(s₁₁, s₁₀) |> type == Overlapping + + @test s₉ ∩ s₁₃ ≈ s₁₃ ∩ s₉ ≈ s₁₃ # CASE 4: Overlapping (same alignment) + @test intersection(s₉, s₁₃) |> type == Overlapping + @test intersection(s₁₃, s₉) |> type == Overlapping + + @test s₁₄ ∩ s₉ ≈ s₁₄ # CASE 4: Overlapping (opposite alignment) + @test s₉ ∩ s₁₄ ≈ s₁₃ # flipped Points in Segment + @test intersection(s₉, s₁₄) |> type == Overlapping + @test intersection(s₁₄, s₉) |> type == Overlapping + + @test s₉ ∩ s₁₅ ≈ s₁₅ ∩ s₉ ≈ s₁₅ # CASE 4: Overlapping (same alignment, corner case) + @test intersection(s₉, s₁₅) |> type == Overlapping + @test intersection(s₁₅, s₉) |> type == Overlapping + + @test s₁₅ ∩ s₁₀ ≈ s₁₅ # CASE 4: Overlapping (same alignment, corner case) + @test s₁₀ ∩ s₁₅ ≈ s₁₆ # flipped Points in Segment + @test intersection(s₁₀, s₁₅) |> type == Overlapping + @test intersection(s₁₅, s₁₀) |> type == Overlapping + + @test s₁₆ ∩ s₉ ≈ s₁₆ # CASE 4: Overlapping (opposite alignment, corner case) + @test s₉ ∩ s₁₆ ≈ s₁₅ # flipped Points in Segment + @test intersection(s₉, s₁₆) |> type == Overlapping + @test intersection(s₁₆, s₉) |> type == Overlapping + + @test s₁₀ ∩ s₁₆ ≈ s₁₆ ∩ s₁₀ ≈ s₁₆ # CASE 4: Overlapping (same alignment, corner case) + @test intersection(s₁₀, s₁₆) |> type == Overlapping + @test intersection(s₁₆, s₁₀) |> type == Overlapping + + @test s₉ ∩ s₁₇ === s₁₇ ∩ s₉ === nothing # CASE 5: NotIntersecting (collinear, same alignment) + @test intersection(s₉, s₁₇) |> type == NotIntersecting + @test intersection(s₁₇, s₉) |> type == NotIntersecting + + @test s₁₀ ∩ s₁₇ === s₁₇ ∩ s₁₀ === nothing # CASE 5: NotIntersecting (collinear, opposite alignment) + @test intersection(s₁₀, s₁₇) |> type == NotIntersecting + @test intersection(s₁₇, s₁₀) |> type == NotIntersecting + + @test s₉ ∩ s₁₈ === s₁₈ ∩ s₉ === nothing # CASE 5: NotIntersecting (collinear, opposite alignment) + @test intersection(s₉, s₁₈) |> type == NotIntersecting + @test intersection(s₁₈, s₉) |> type == NotIntersecting + + @test s₁ ∩ s₉ === s₉ ∩ s₁ === nothing # CASE 5: NotIntersecting, one λ in range + @test intersection(s₉, s₁) |> type == NotIntersecting + @test intersection(s₁, s₉) |> type == NotIntersecting + + @test s₁ ∩ s₁₀ === s₁₀ ∩ s₁ === nothing # CASE 5: NotIntersecting, one λ in range + @test intersection(s₁₀, s₁) |> type == NotIntersecting + @test intersection(s₁, s₁₀) |> type == NotIntersecting + + @test s₃ ∩ s₉ === s₉ ∩ s₃ === nothing # CASE 5: NotIntersecting + @test intersection(s₉, s₁) |> type == NotIntersecting + @test intersection(s₁, s₉) |> type == NotIntersecting + + @test s₃ ∩ s₁₀ === s₁₀ ∩ s₃ === nothing # CASE 5: NotIntersecting + @test intersection(s₁₀, s₃) |> type == NotIntersecting + @test intersection(s₃, s₁₀) |> type == NotIntersecting + + # segments in 3D + s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) + s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) + s3 = Segment(cart(0.5, 0.0, 0.0), cart(1.5, 0.0, 0.0)) + s4 = Segment(cart(0.0, 1.0, 0.0), cart(0.0, -2.0, 0.0)) + s5 = Segment(cart(-1.0, 1.0, 0.0), cart(2.0, -2.0, 0.0)) + s6 = Segment(cart(0.0, 0.0, 0.0), cart(0.0, 1.0, 0.0)) + s7 = Segment(cart(-1.0, 1.0, 0.0), cart(-1.0, -1.0, 0.0)) + s8 = Segment(cart(-1.0, 1.0, 1.0), cart(-1.0, -1.0, 1.0)) + s9 = Segment(cart(0.5, 1.0, 1.0), cart(0.5, -1.0, 1.0)) + s10 = Segment(cart(0.0, 1.0, 0.0), cart(1.0, 1.0, 0.0)) + s11 = Segment(cart(1.5, 0.0, 0.0), cart(2.5, 0.0, 0.0)) + s12 = Segment(cart(1.0, 0.0, 0.0), cart(2.0, 0.0, 0.0)) + + @test intersection(s1, s2) |> type == Crossing + @test s1 ∩ s2 ≈ cart(0.5, 0.0, 0.0) + @test intersection(s1, s3) |> type == Overlapping + @test s1 ∩ s3 ≈ Segment(cart(0.5, 0.0, 0.0), cart(1.0, 0.0, 0.0)) + @test intersection(s1, s4) |> type == EdgeTouching + @test s1 ∩ s4 ≈ cart(0.0, 0.0, 0.0) + @test intersection(s1, s5) |> type == EdgeTouching + @test s1 ∩ s5 ≈ cart(0.0, 0.0, 0.0) + @test intersection(s1, s6) |> type == CornerTouching + @test s1 ∩ s6 ≈ cart(0.0, 0.0, 0.0) + @test intersection(s1, s7) |> type == NotIntersecting + @test isnothing(s1 ∩ s7) + @test intersection(s1, s8) |> type == NotIntersecting + @test isnothing(s1 ∩ s8) + @test intersection(s1, s9) |> type == NotIntersecting + @test isnothing(s1 ∩ s9) + @test intersection(s1, s10) |> type == NotIntersecting + @test isnothing(s1 ∩ s10) + @test intersection(s1, s11) |> type == NotIntersecting + @test isnothing(s1 ∩ s11) + @test intersection(s1, s12) |> type == CornerTouching + @test s1 ∩ s12 ≈ cart(1.0, 0.0, 0.0) + + # precision test + s1 = Segment(cart(2.0, 2.0), cart(3.0, 1.0)) + s2 = Segment(cart(2.12505, 1.87503), cart(50000.0, 30000.0)) + s3 = Segment(cart(2.125005, 1.875003), cart(50000.0, 30000.0)) + s4 = Segment(cart(2.125005, 1.875003), cart(50002.125005, 30001.875003)) + @test s1 ∩ s2 === s2 ∩ s1 === nothing + @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) + @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) + + # type stability tests + s1 = Segment(cart(0, 0), cart(1, 0)) + s2 = Segment(cart(0.5, 0.0), cart(2, 0)) + @inferred someornone(s1, s2) + + s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) + s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) + @inferred someornone(s1, s2) + + # rays and segments in 2D + r₁ = Ray(cart(1, 0), vector(2, 1)) + s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing + s₂ = Segment(cart(0, 2), cart(1, 0.5)) # NotIntersecting + s₃ = Segment(cart(0, 2), cart(0.5, -0.5)) # NotIntersecting + s₄ = Segment(cart(0.5, 1), cart(1.5, -1)) # EdgeTouching + s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # EdgeTouching + s₆ = Segment(cart(1, 0), cart(1, -1)) # CornerTouching + s₇ = Segment(cart(0.5, -1), cart(1, 0)) # CornerTouching + + @test intersection(r₁, s₁) |> type == Crossing #CASE 1 + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(1.25, 0.125) + @test intersection(r₁, s₂) |> type == NotIntersecting # CASE 5 + @test r₁ ∩ s₂ === s₂ ∩ r₁ === nothing + @test intersection(r₁, s₃) |> type == NotIntersecting # CASE 5 + @test r₁ ∩ s₃ === s₃ ∩ r₁ === nothing + @test intersection(r₁, s₄) |> type == EdgeTouching # CASE 2 + @test r₁ ∩ s₄ ≈ s₄ ∩ r₁ ≈ r₁(0) + @test intersection(r₁, s₅) |> type == EdgeTouching # CASE 2 + @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ cart(1.5, 0.25) + @test intersection(r₁, s₆) |> type == CornerTouching # CASE 3 + @test r₁ ∩ s₆ ≈ s₆ ∩ r₁ ≈ r₁(0) + @test intersection(r₁, s₇) |> type == CornerTouching # CASE 3 + @test r₁ ∩ s₇ ≈ s₇ ∩ r₁ ≈ r₁(0) + + r₂ = Ray(cart(3, 2), vector(1, 1)) + s₈ = Segment(cart(4, 3), cart(5, 4)) # Overlapping + s₉ = Segment(cart(2.5, 1.5), cart(3.3, 2.3)) # Overlapping s(1) + s₁₀ = Segment(cart(3.6, 2.6), cart(2.6, 1.6)) # Overlapping s(0) + s₁₁ = Segment(cart(2.2, 1.2), cart(3, 2)) # CornerTouching, colinear, s(1) + s₁₂ = Segment(cart(3, 2), cart(2.4, 1.4)) # CornerTouching, colinear, s(0) + s₁₃ = Segment(cart(3, 2), cart(3.1, 2.1)) # Overlapping s(0) = r(0) + s₁₄ = Segment(cart(3.2, 2.2), cart(3, 2)) # Overlapping s(1) = r(0) + s₁₅ = Segment(cart(2, 1), cart(1.6, 0.6)) # No Intersection, colinear + s₁₆ = Segment(cart(3, 1), cart(4, 2)) # No Intersection, parallel + @test intersection(r₂, s₈) |> type == Overlapping # CASE 4 + @test r₂ ∩ s₈ === s₈ ∩ r₂ === s₈ + @test intersection(r₂, s₉) |> type == Overlapping # CASE 4 + @test r₂ ∩ s₉ == s₉ ∩ r₂ == Segment(r₂(0), s₉(1)) + @test intersection(r₂, s₁₀) |> type == Overlapping # CASE 4 + @test r₂ ∩ s₁₀ == s₁₀ ∩ r₂ == Segment(r₂(0), s₁₀(0)) + @test intersection(r₂, s₁₁) |> type == CornerTouching # CASE 3 + @test r₂ ∩ s₁₁ ≈ s₁₁ ∩ r₂ ≈ r₂(0) + @test intersection(r₂, s₁₂) |> type == CornerTouching # CASE 3 + @test r₂ ∩ s₁₂ ≈ s₁₂ ∩ r₂ ≈ r₂(0) + @test intersection(r₂, s₁₃) |> type == Overlapping # CASE 4 + @test r₂ ∩ s₁₃ === s₁₃ ∩ r₂ === s₁₃ + @test intersection(r₂, s₁₄) |> type == Overlapping # CASE 4 + @test r₂ ∩ s₁₄ === s₁₄ ∩ r₂ === s₁₄ + @test intersection(r₂, s₁₅) |> type == NotIntersecting # CASE 5 + @test r₂ ∩ s₁₅ === s₁₅ ∩ r₂ === nothing + @test intersection(r₂, s₁₆) |> type == NotIntersecting # CASE 5 + @test r₂ ∩ s₁₆ === s₁₆ ∩ r₂ === nothing + + # type stability tests + r₁ = Ray(cart(0, 0), vector(1, 0)) + s₁ = Segment(cart(-1, -1), cart(-1, 1)) + @inferred someornone(r₁, s₁) + + # 3D test + r₁ = Ray(cart(1, 2, 3), vector(1, 2, 3)) + s₁ = Segment(cart(1, 3, 5), cart(3, 5, 7)) + @test intersection(r₁, s₁) |> type === Crossing # CASE 1 + @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(2, 4, 6) + + s₂ = Segment(cart(0, 1, 2), cart(2, 3, 4)) + @test intersection(r₁, s₂) |> type === EdgeTouching # CASE 2 + @test r₁ ∩ s₂ == s₂ ∩ r₁ == r₁(0) + + s₃ = Segment(cart(0.23, 1, 2.3), cart(1, 2, 3)) + @test intersection(r₁, s₃) |> type === CornerTouching # CASE 3 + @test r₁ ∩ s₃ == s₃ ∩ r₁ == r₁(0) + + s₄ = Segment(cart(0, 0, 0), cart(2, 4, 6)) + @test intersection(r₁, s₄) |> type === Overlapping # CASE 4 + @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(cart(1, 2, 3), cart(2, 4, 6)) + + s₅ = Segment(cart(0, 0, 0), cart(0.5, 1, 1.5)) + @test intersection(r₁, s₅) |> type === NotIntersecting # CASE 5 + @test r₁ ∩ s₅ === s₅ ∩ r₁ === nothing + + l₁ = Line(cart(1, 0), cart(3, 1)) + s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing + s₂ = Segment(cart(0.5, 1), cart(0, 0)) # NotIntersecting + s₃ = Segment(cart(0, 2), cart(-2, 1)) # NotIntersecting + s₄ = Segment(cart(0.5, -1), cart(1, 0)) # Touching + s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # Touching + s₆ = Segment(cart(-3, -2), cart(4, 1.5)) # Overlapping + + @test intersection(l₁, s₁) |> type == Crossing #CASE 1 + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125) + @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 + @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing + @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 + @test l₁ ∩ s₃ === s₃ ∩ l₁ === nothing + @test intersection(l₁, s₄) |> type == Touching # CASE 2 + @test l₁ ∩ s₄ ≈ s₄ ∩ l₁ ≈ s₄(1) + @test intersection(l₁, s₅) |> type == Touching # CASE 2 + @test l₁ ∩ s₅ ≈ s₅ ∩ l₁ ≈ s₅(0) + @test intersection(l₁, s₆) |> type == Overlapping # CASE 3 + @test l₁ ∩ s₆ ≈ s₆ ∩ l₁ ≈ s₆ + + # type stability tests + @inferred someornone(l₁, s₁) + @inferred someornone(l₁, s₂) + + # 3d tests + l₁ = Line(cart(1, 0, 1), cart(3, 1, 1)) + s₁ = Segment(cart(0, 2, 1), cart(2, -1, 1)) # Crossing + s₂ = Segment(cart(0.5, 1, 1), cart(0, 0, 1)) # NotIntersecting + s₃ = Segment(cart(0, 2, 1), cart(-2, 1, 1)) # NotIntersecting + s₄ = Segment(cart(0.5, -1, 1), cart(1, 0, 1)) # Touching + s₅ = Segment(cart(1.5, 0.25, 1), cart(1.5, 2, 1)) # Touching + s₆ = Segment(cart(-3, -2, 1), cart(4, 1.5, 1)) # Overlapping + s₇ = Segment(cart(0, 2, 1), cart(2, -1, 1.1)) # NotIntersecting + + @test intersection(l₁, s₁) |> type == Crossing #CASE 1 + @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125, 1) + @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 + @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing + @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 + @test l₁ ∩ s₃ === s₃ ∩ l₁ === nothing + @test intersection(l₁, s₄) |> type == Touching # CASE 2 + @test l₁ ∩ s₄ ≈ s₄ ∩ l₁ ≈ s₄(1) + @test intersection(l₁, s₅) |> type == Touching # CASE 2 + @test l₁ ∩ s₅ ≈ s₅ ∩ l₁ ≈ s₅(0) + @test intersection(l₁, s₆) |> type == Overlapping # CASE 3 + @test l₁ ∩ s₆ ≈ s₆ ∩ l₁ ≈ s₆ + @test intersection(l₁, s₇) |> type == NotIntersecting # CASE 4 + @test l₁ ∩ s₇ === s₇ ∩ l₁ === nothing + + # degenerate segments + A = cart(0.0, 0.0) + B = cart(0.5, 0.0) + C = cart(1.0, 0.0) + s₀ = Segment(A, C) + s₁ = Segment(A, A) + s₂ = Segment(B, B) + s₃ = Segment(C, C) + @test s₀ ∩ s₁ ≈ s₁ ∩ s₀ ≈ A + @test s₀ ∩ s₂ ≈ s₂ ∩ s₀ ≈ B + @test s₀ ∩ s₃ ≈ s₃ ∩ s₀ ≈ C + @test intersection(s₀, s₁) |> type == CornerTouching + @test intersection(s₀, s₂) |> type == EdgeTouching + @test intersection(s₀, s₃) |> type == CornerTouching + @test s₁ ∩ s₂ === s₂ ∩ s₁ === nothing + @test s₁ ∩ s₃ === s₃ ∩ s₁ === nothing + @test s₂ ∩ s₃ === s₃ ∩ s₂ === nothing + @test intersection(s₁, s₂) |> type == NotIntersecting + @test intersection(s₁, s₃) |> type == NotIntersecting + @test intersection(s₂, s₃) |> type == NotIntersecting + @test s₁ ∩ s₁ ≈ A + @test s₂ ∩ s₂ ≈ B + @test s₃ ∩ s₃ ≈ C + @test intersection(s₁, s₁) |> type == CornerTouching + @test intersection(s₂, s₂) |> type == CornerTouching + @test intersection(s₃, s₃) |> type == CornerTouching + + # utils + @test Meshes._sort4vals(2.5, 1.4, 1.1, 2.0) == (1.4, 2.0) + @test Meshes._sort4vals(2.0, 1.1, 1.4, 2.5) == (1.4, 2.0) + @test Meshes._sort4vals(2.0, 2.5, 1.1, 1.4) == (1.4, 2.0) +end - @testset "Segments" begin - # segments in 2D - s1 = Segment(cart(0, 0), cart(1, 0)) - s2 = Segment(cart(0.5, 0.0), cart(2, 0)) - @test s1 ∩ s2 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) - @test s2 ∩ s1 ≈ Segment(cart(0.5, 0.0), cart(1, 0)) - - s1 = Segment(cart(0, 0), cart(1, -1)) - s2 = Segment(cart(0.5, -0.5), cart(1.5, -1.5)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0.5, -0.5), cart(1, -1)) - - s1 = Segment(cart(0, 0), cart(1, 0)) - s2 = Segment(cart(0, 0), cart(0, 1)) - @test s1 ∩ s2 ≈ cart(0, 0) - @test s2 ∩ s1 ≈ cart(0, 0) - - s1 = Segment(cart(0, 0), cart(1, 0)) - s2 = Segment(cart(0, 0), cart(-1, 0)) - @test s1 ∩ s2 ≈ cart(0, 0) - @test s2 ∩ s1 ≈ cart(0, 0) - - s1 = Segment(cart(0, 0), cart(0, 1)) - s2 = Segment(cart(0, 0), cart(0, -1)) - @test s1 ∩ s2 ≈ cart(0, 0) - @test s2 ∩ s1 ≈ cart(0, 0) - - s1 = Segment(cart(1, 1), cart(1, 2)) - s2 = Segment(cart(1, 1), cart(1, 0)) - @test s1 ∩ s2 ≈ cart(1, 1) - @test s2 ∩ s1 ≈ cart(1, 1) - - s1 = Segment(cart(1, 1), cart(2, 1)) - s2 = Segment(cart(1, 0), cart(3, 0)) - @test s1 ∩ s2 === nothing - @test s2 ∩ s1 === nothing - - s1 = Segment(cart(0.181429364026879, 0.546811355144474), cart(0.38282226144778, 0.107781953228536)) - s2 = Segment(cart(0.412498700935005, 0.212081819871479), cart(0.395936725690311, 0.252041094122474)) - @test s1 ∩ s2 === nothing - @test s2 ∩ s1 === nothing - - s1 = Segment(cart(1, 2), cart(1, 0)) - s2 = Segment(cart(1, 0), cart(1, 1)) - @test s1 ∩ s2 ≈ Segment(cart(1, 1), cart(1, 0)) - @test s2 ∩ s1 ≈ Segment(cart(1, 0), cart(1, 1)) - - s1 = Segment(cart(0, 0), cart(2, 0)) - s2 = Segment(cart(-2, 0), cart(-1, 0)) - s3 = Segment(cart(-1, 0), cart(-2, 0)) - @test s1 ∩ s2 === s2 ∩ s1 === nothing - @test s1 ∩ s3 === s3 ∩ s1 === nothing - - s1 = Segment(cart(-1, 0), cart(0, 0)) - s2 = Segment(cart(0, 0), cart(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0, 0) - - s1 = Segment(cart(-1, 0), cart(1, 0)) - s2 = Segment(cart(0, 0), cart(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) - - s1 = Segment(cart(0, 0), cart(1, 0)) - s2 = Segment(cart(0, 0), cart(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(0, 0), cart(1, 0)) - - s1 = Segment(cart(0, 0), cart(3, 0)) - s2 = Segment(cart(1, 0), cart(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - - s1 = Segment(cart(0, 0), cart(2, 0)) - s2 = Segment(cart(1, 0), cart(2, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ s2 - - s1 = Segment(cart(0, 0), cart(2, 0)) - s2 = Segment(cart(1, 0), cart(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ Segment(cart(1, 0), cart(2, 0)) - - s1 = Segment(cart(0, 0), cart(2, 0)) - s2 = Segment(cart(2, 0), cart(3, 0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2, 0) - - s1 = Segment(cart(0, 0), cart(2, 0)) - s2 = Segment(cart(3, 0), cart(4, 0)) - @test s1 ∩ s2 === s2 ∩ s1 === nothing - - s1 = Segment(cart(2, 1), cart(1, 2)) - s2 = Segment(cart(1, 0), cart(1, 1)) - @test s1 ∩ s2 === s2 ∩ s1 === nothing - - s1 = Segment(cart(1.5, 1.5), cart(3.0, 1.5)) - s2 = Segment(cart(3.0, 1.0), cart(2.0, 2.0)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(2.5, 1.5) - - s1 = Segment(cart(0.94495744, 0.53224397), cart(0.94798386, 0.5344541)) - s2 = Segment(cart(0.94798386, 0.5344541), cart(0.9472896, 0.5340202)) - @test s1 ∩ s2 ≈ s2 ∩ s1 ≈ cart(0.94798386, 0.5344541) - - s₁ = Segment(cart(0, 0), cart(3, 4)) - s₂ = Segment(cart(1, 2), cart(3, -2)) - s₃ = Segment(cart(2, 0), cart(-2, 0)) - s₄ = Segment(cart(0, 0), cart(1, 2)) - s₅ = Segment(cart(1, 2), cart(3, 4)) - s₆ = Segment(cart(-1, -4 / 3), cart(0, 0)) - s₇ = Segment(cart(1, 2), cart(0, 4)) - s₈ = Segment(cart(4, 16 / 3), cart(3, 4)) - - s₉ = Segment(cart(-1, 5), cart(1, 4)) - s₁₀ = Segment(cart(1, 4), cart(-1, 5)) - s₁₁ = Segment(cart(-2, 5.5), cart(-0.8, 4.9)) - s₁₂ = Segment(cart(-0.8, 4.9), cart(-2, 5.5)) - s₁₃ = Segment(cart(-0.5, 4.75), cart(0.2, 4.4)) - s₁₄ = Segment(cart(0.2, 4.4), cart(-0.5, 4.75)) - s₁₅ = Segment(cart(0.5, 4.25), cart(1, 4)) - s₁₆ = Segment(cart(1, 4), cart(0.5, 4.25)) - s₁₇ = Segment(cart(2, 3.5), cart(1.5, 3.75)) - s₁₈ = Segment(cart(1.5, 3.75), cart(2, 3.5)) - - @test s₁ ∩ s₂ ≈ s₂ ∩ s₁ ≈ cart(1.2, 1.6) # CASE 1: Crossing Segments - @test intersection(s₁, s₂) |> type == Crossing - @test intersection(s₂, s₁) |> type == Crossing - - @test s₁ ∩ s₃ ≈ s₃ ∩ s₁ ≈ cart(0, 0) # CASE 2: EdgeTouching (s₁(0)) - @test intersection(s₁, s₃) |> type == EdgeTouching - @test intersection(s₃, s₁) |> type == EdgeTouching - - @test s₂ ∩ s₃ ≈ s₃ ∩ s₂ ≈ cart(2, 0) # CASE 2: EdgeTouching (s₃(1)) - @test intersection(s₂, s₃) |> type == EdgeTouching - @test intersection(s₃, s₂) |> type == EdgeTouching - - @test s₁ ∩ s₄ ≈ s₄ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₄(0)) - @test intersection(s₁, s₄) |> type == CornerTouching - @test intersection(s₄, s₁) |> type == CornerTouching - - @test s₂ ∩ s₄ ≈ s₄ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₄(1)) - @test intersection(s₂, s₄) |> type == CornerTouching - @test intersection(s₄, s₂) |> type == CornerTouching - - @test s₁ ∩ s₅ ≈ s₅ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₅(1)) - @test intersection(s₂, s₄) |> type == CornerTouching - @test intersection(s₄, s₂) |> type == CornerTouching - - @test s₁ ∩ s₆ ≈ s₆ ∩ s₁ ≈ cart(0, 0) # CASE 3: CornerTouching (s₁(0), s₆(1)), collinear - @test intersection(s₁, s₆) |> type == CornerTouching - @test intersection(s₆, s₁) |> type == CornerTouching - - @test s₂ ∩ s₇ ≈ s₇ ∩ s₂ ≈ cart(1, 2) # CASE 3: CornerTouching (s₂(0), s₇(0)), collinear - @test intersection(s₂, s₇) |> type == CornerTouching - @test intersection(s₇, s₂) |> type == CornerTouching - - @test s₁ ∩ s₈ ≈ s₈ ∩ s₁ ≈ cart(3, 4) # CASE 3: CornerTouching (s₁(1), s₈(1)), collinear - @test intersection(s₁, s₈) |> type == CornerTouching - @test intersection(s₈, s₁) |> type == CornerTouching - - @test s₉ ∩ s₉ ≈ s₉ # CASE 4: Overlapping (same segment) - @test intersection(s₉, s₉) |> type == Overlapping - - @test s₉ ∩ s₁₀ ≈ s₉ # CASE 4: Overlapping (same segment, flipped points) - @test s₁₀ ∩ s₉ ≈ s₁₀ - @test intersection(s₉, s₁₀) |> type == Overlapping - @test intersection(s₁₀, s₉) |> type == Overlapping - - @test s₉ ∩ s₁₁ ≈ s₁₁ ∩ s₉ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (same alignment) - @test intersection(s₉, s₁₁) |> type == Overlapping - @test intersection(s₁₁, s₉) |> type == Overlapping - - @test s₉ ∩ s₁₂ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # CASE 4: Overlapping (opposite alignment, λ = 0 involved) - @test s₁₂ ∩ s₉ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # flipped Points in Segment - @test intersection(s₉, s₁₂) |> type == Overlapping - @test intersection(s₁₂, s₉) |> type == Overlapping - - @test s₁₀ ∩ s₁₁ ≈ Segment(cart(-0.8, 4.9), cart(-1, 5)) # CASE 4: Overlapping (opposite alignment, λ = 1 involved) - @test s₁₁ ∩ s₁₀ ≈ Segment(cart(-1, 5), cart(-0.8, 4.9)) # flipped Points in Segment - @test intersection(s₁₀, s₁₁) |> type == Overlapping - @test intersection(s₁₁, s₁₀) |> type == Overlapping - - @test s₉ ∩ s₁₃ ≈ s₁₃ ∩ s₉ ≈ s₁₃ # CASE 4: Overlapping (same alignment) - @test intersection(s₉, s₁₃) |> type == Overlapping - @test intersection(s₁₃, s₉) |> type == Overlapping - - @test s₁₄ ∩ s₉ ≈ s₁₄ # CASE 4: Overlapping (opposite alignment) - @test s₉ ∩ s₁₄ ≈ s₁₃ # flipped Points in Segment - @test intersection(s₉, s₁₄) |> type == Overlapping - @test intersection(s₁₄, s₉) |> type == Overlapping - - @test s₉ ∩ s₁₅ ≈ s₁₅ ∩ s₉ ≈ s₁₅ # CASE 4: Overlapping (same alignment, corner case) - @test intersection(s₉, s₁₅) |> type == Overlapping - @test intersection(s₁₅, s₉) |> type == Overlapping - - @test s₁₅ ∩ s₁₀ ≈ s₁₅ # CASE 4: Overlapping (same alignment, corner case) - @test s₁₀ ∩ s₁₅ ≈ s₁₆ # flipped Points in Segment - @test intersection(s₁₀, s₁₅) |> type == Overlapping - @test intersection(s₁₅, s₁₀) |> type == Overlapping - - @test s₁₆ ∩ s₉ ≈ s₁₆ # CASE 4: Overlapping (opposite alignment, corner case) - @test s₉ ∩ s₁₆ ≈ s₁₅ # flipped Points in Segment - @test intersection(s₉, s₁₆) |> type == Overlapping - @test intersection(s₁₆, s₉) |> type == Overlapping - - @test s₁₀ ∩ s₁₆ ≈ s₁₆ ∩ s₁₀ ≈ s₁₆ # CASE 4: Overlapping (same alignment, corner case) - @test intersection(s₁₀, s₁₆) |> type == Overlapping - @test intersection(s₁₆, s₁₀) |> type == Overlapping - - @test s₉ ∩ s₁₇ === s₁₇ ∩ s₉ === nothing # CASE 5: NotIntersecting (collinear, same alignment) - @test intersection(s₉, s₁₇) |> type == NotIntersecting - @test intersection(s₁₇, s₉) |> type == NotIntersecting - - @test s₁₀ ∩ s₁₇ === s₁₇ ∩ s₁₀ === nothing # CASE 5: NotIntersecting (collinear, opposite alignment) - @test intersection(s₁₀, s₁₇) |> type == NotIntersecting - @test intersection(s₁₇, s₁₀) |> type == NotIntersecting - - @test s₉ ∩ s₁₈ === s₁₈ ∩ s₉ === nothing # CASE 5: NotIntersecting (collinear, opposite alignment) - @test intersection(s₉, s₁₈) |> type == NotIntersecting - @test intersection(s₁₈, s₉) |> type == NotIntersecting - - @test s₁ ∩ s₉ === s₉ ∩ s₁ === nothing # CASE 5: NotIntersecting, one λ in range - @test intersection(s₉, s₁) |> type == NotIntersecting - @test intersection(s₁, s₉) |> type == NotIntersecting - - @test s₁ ∩ s₁₀ === s₁₀ ∩ s₁ === nothing # CASE 5: NotIntersecting, one λ in range - @test intersection(s₁₀, s₁) |> type == NotIntersecting - @test intersection(s₁, s₁₀) |> type == NotIntersecting - - @test s₃ ∩ s₉ === s₉ ∩ s₃ === nothing # CASE 5: NotIntersecting - @test intersection(s₉, s₁) |> type == NotIntersecting - @test intersection(s₁, s₉) |> type == NotIntersecting - - @test s₃ ∩ s₁₀ === s₁₀ ∩ s₃ === nothing # CASE 5: NotIntersecting - @test intersection(s₁₀, s₃) |> type == NotIntersecting - @test intersection(s₃, s₁₀) |> type == NotIntersecting - - # segments in 3D - s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) - s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) - s3 = Segment(cart(0.5, 0.0, 0.0), cart(1.5, 0.0, 0.0)) - s4 = Segment(cart(0.0, 1.0, 0.0), cart(0.0, -2.0, 0.0)) - s5 = Segment(cart(-1.0, 1.0, 0.0), cart(2.0, -2.0, 0.0)) - s6 = Segment(cart(0.0, 0.0, 0.0), cart(0.0, 1.0, 0.0)) - s7 = Segment(cart(-1.0, 1.0, 0.0), cart(-1.0, -1.0, 0.0)) - s8 = Segment(cart(-1.0, 1.0, 1.0), cart(-1.0, -1.0, 1.0)) - s9 = Segment(cart(0.5, 1.0, 1.0), cart(0.5, -1.0, 1.0)) - s10 = Segment(cart(0.0, 1.0, 0.0), cart(1.0, 1.0, 0.0)) - s11 = Segment(cart(1.5, 0.0, 0.0), cart(2.5, 0.0, 0.0)) - s12 = Segment(cart(1.0, 0.0, 0.0), cart(2.0, 0.0, 0.0)) - - @test intersection(s1, s2) |> type == Crossing - @test s1 ∩ s2 ≈ cart(0.5, 0.0, 0.0) - @test intersection(s1, s3) |> type == Overlapping - @test s1 ∩ s3 ≈ Segment(cart(0.5, 0.0, 0.0), cart(1.0, 0.0, 0.0)) - @test intersection(s1, s4) |> type == EdgeTouching - @test s1 ∩ s4 ≈ cart(0.0, 0.0, 0.0) - @test intersection(s1, s5) |> type == EdgeTouching - @test s1 ∩ s5 ≈ cart(0.0, 0.0, 0.0) - @test intersection(s1, s6) |> type == CornerTouching - @test s1 ∩ s6 ≈ cart(0.0, 0.0, 0.0) - @test intersection(s1, s7) |> type == NotIntersecting - @test isnothing(s1 ∩ s7) - @test intersection(s1, s8) |> type == NotIntersecting - @test isnothing(s1 ∩ s8) - @test intersection(s1, s9) |> type == NotIntersecting - @test isnothing(s1 ∩ s9) - @test intersection(s1, s10) |> type == NotIntersecting - @test isnothing(s1 ∩ s10) - @test intersection(s1, s11) |> type == NotIntersecting - @test isnothing(s1 ∩ s11) - @test intersection(s1, s12) |> type == CornerTouching - @test s1 ∩ s12 ≈ cart(1.0, 0.0, 0.0) - - # precision test - s1 = Segment(cart(2.0, 2.0), cart(3.0, 1.0)) - s2 = Segment(cart(2.12505, 1.87503), cart(50000.0, 30000.0)) - s3 = Segment(cart(2.125005, 1.875003), cart(50000.0, 30000.0)) - s4 = Segment(cart(2.125005, 1.875003), cart(50002.125005, 30001.875003)) - @test s1 ∩ s2 === s2 ∩ s1 === nothing - @test s1 ∩ s3 === s3 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) - @test s1 ∩ s4 === s4 ∩ s1 === ((T == Float32) ? cart(2.125005, 1.875003) : nothing) - - # type stability tests - s1 = Segment(cart(0, 0), cart(1, 0)) - s2 = Segment(cart(0.5, 0.0), cart(2, 0)) - @inferred someornone(s1, s2) - - s1 = Segment(cart(0.0, 0.0, 0.0), cart(1.0, 0.0, 0.0)) - s2 = Segment(cart(0.5, 1.0, 0.0), cart(0.5, -1.0, 0.0)) - @inferred someornone(s1, s2) - - # rays and segments in 2D - r₁ = Ray(cart(1, 0), vector(2, 1)) - s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing - s₂ = Segment(cart(0, 2), cart(1, 0.5)) # NotIntersecting - s₃ = Segment(cart(0, 2), cart(0.5, -0.5)) # NotIntersecting - s₄ = Segment(cart(0.5, 1), cart(1.5, -1)) # EdgeTouching - s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # EdgeTouching - s₆ = Segment(cart(1, 0), cart(1, -1)) # CornerTouching - s₇ = Segment(cart(0.5, -1), cart(1, 0)) # CornerTouching - - @test intersection(r₁, s₁) |> type == Crossing #CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(1.25, 0.125) - @test intersection(r₁, s₂) |> type == NotIntersecting # CASE 5 - @test r₁ ∩ s₂ === s₂ ∩ r₁ === nothing - @test intersection(r₁, s₃) |> type == NotIntersecting # CASE 5 - @test r₁ ∩ s₃ === s₃ ∩ r₁ === nothing - @test intersection(r₁, s₄) |> type == EdgeTouching # CASE 2 - @test r₁ ∩ s₄ ≈ s₄ ∩ r₁ ≈ r₁(0) - @test intersection(r₁, s₅) |> type == EdgeTouching # CASE 2 - @test r₁ ∩ s₅ ≈ s₅ ∩ r₁ ≈ cart(1.5, 0.25) - @test intersection(r₁, s₆) |> type == CornerTouching # CASE 3 - @test r₁ ∩ s₆ ≈ s₆ ∩ r₁ ≈ r₁(0) - @test intersection(r₁, s₇) |> type == CornerTouching # CASE 3 - @test r₁ ∩ s₇ ≈ s₇ ∩ r₁ ≈ r₁(0) - - r₂ = Ray(cart(3, 2), vector(1, 1)) - s₈ = Segment(cart(4, 3), cart(5, 4)) # Overlapping - s₉ = Segment(cart(2.5, 1.5), cart(3.3, 2.3)) # Overlapping s(1) - s₁₀ = Segment(cart(3.6, 2.6), cart(2.6, 1.6)) # Overlapping s(0) - s₁₁ = Segment(cart(2.2, 1.2), cart(3, 2)) # CornerTouching, colinear, s(1) - s₁₂ = Segment(cart(3, 2), cart(2.4, 1.4)) # CornerTouching, colinear, s(0) - s₁₃ = Segment(cart(3, 2), cart(3.1, 2.1)) # Overlapping s(0) = r(0) - s₁₄ = Segment(cart(3.2, 2.2), cart(3, 2)) # Overlapping s(1) = r(0) - s₁₅ = Segment(cart(2, 1), cart(1.6, 0.6)) # No Intersection, colinear - s₁₆ = Segment(cart(3, 1), cart(4, 2)) # No Intersection, parallel - @test intersection(r₂, s₈) |> type == Overlapping # CASE 4 - @test r₂ ∩ s₈ === s₈ ∩ r₂ === s₈ - @test intersection(r₂, s₉) |> type == Overlapping # CASE 4 - @test r₂ ∩ s₉ == s₉ ∩ r₂ == Segment(r₂(0), s₉(1)) - @test intersection(r₂, s₁₀) |> type == Overlapping # CASE 4 - @test r₂ ∩ s₁₀ == s₁₀ ∩ r₂ == Segment(r₂(0), s₁₀(0)) - @test intersection(r₂, s₁₁) |> type == CornerTouching # CASE 3 - @test r₂ ∩ s₁₁ ≈ s₁₁ ∩ r₂ ≈ r₂(0) - @test intersection(r₂, s₁₂) |> type == CornerTouching # CASE 3 - @test r₂ ∩ s₁₂ ≈ s₁₂ ∩ r₂ ≈ r₂(0) - @test intersection(r₂, s₁₃) |> type == Overlapping # CASE 4 - @test r₂ ∩ s₁₃ === s₁₃ ∩ r₂ === s₁₃ - @test intersection(r₂, s₁₄) |> type == Overlapping # CASE 4 - @test r₂ ∩ s₁₄ === s₁₄ ∩ r₂ === s₁₄ - @test intersection(r₂, s₁₅) |> type == NotIntersecting # CASE 5 - @test r₂ ∩ s₁₅ === s₁₅ ∩ r₂ === nothing - @test intersection(r₂, s₁₆) |> type == NotIntersecting # CASE 5 - @test r₂ ∩ s₁₆ === s₁₆ ∩ r₂ === nothing - - # type stability tests - r₁ = Ray(cart(0, 0), vector(1, 0)) - s₁ = Segment(cart(-1, -1), cart(-1, 1)) - @inferred someornone(r₁, s₁) - - # 3D test - r₁ = Ray(cart(1, 2, 3), vector(1, 2, 3)) - s₁ = Segment(cart(1, 3, 5), cart(3, 5, 7)) - @test intersection(r₁, s₁) |> type === Crossing # CASE 1 - @test r₁ ∩ s₁ ≈ s₁ ∩ r₁ ≈ cart(2, 4, 6) - - s₂ = Segment(cart(0, 1, 2), cart(2, 3, 4)) - @test intersection(r₁, s₂) |> type === EdgeTouching # CASE 2 - @test r₁ ∩ s₂ == s₂ ∩ r₁ == r₁(0) - - s₃ = Segment(cart(0.23, 1, 2.3), cart(1, 2, 3)) - @test intersection(r₁, s₃) |> type === CornerTouching # CASE 3 - @test r₁ ∩ s₃ == s₃ ∩ r₁ == r₁(0) - - s₄ = Segment(cart(0, 0, 0), cart(2, 4, 6)) - @test intersection(r₁, s₄) |> type === Overlapping # CASE 4 - @test r₁ ∩ s₄ == s₄ ∩ r₁ == Segment(cart(1, 2, 3), cart(2, 4, 6)) - - s₅ = Segment(cart(0, 0, 0), cart(0.5, 1, 1.5)) - @test intersection(r₁, s₅) |> type === NotIntersecting # CASE 5 - @test r₁ ∩ s₅ === s₅ ∩ r₁ === nothing - - l₁ = Line(cart(1, 0), cart(3, 1)) - s₁ = Segment(cart(0, 2), cart(2, -1)) # Crossing - s₂ = Segment(cart(0.5, 1), cart(0, 0)) # NotIntersecting - s₃ = Segment(cart(0, 2), cart(-2, 1)) # NotIntersecting - s₄ = Segment(cart(0.5, -1), cart(1, 0)) # Touching - s₅ = Segment(cart(1.5, 0.25), cart(1.5, 2)) # Touching - s₆ = Segment(cart(-3, -2), cart(4, 1.5)) # Overlapping - - @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125) - @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 - @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing - @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 - @test l₁ ∩ s₃ === s₃ ∩ l₁ === nothing - @test intersection(l₁, s₄) |> type == Touching # CASE 2 - @test l₁ ∩ s₄ ≈ s₄ ∩ l₁ ≈ s₄(1) - @test intersection(l₁, s₅) |> type == Touching # CASE 2 - @test l₁ ∩ s₅ ≈ s₅ ∩ l₁ ≈ s₅(0) - @test intersection(l₁, s₆) |> type == Overlapping # CASE 3 - @test l₁ ∩ s₆ ≈ s₆ ∩ l₁ ≈ s₆ - - # type stability tests - @inferred someornone(l₁, s₁) - @inferred someornone(l₁, s₂) - - # 3d tests - l₁ = Line(cart(1, 0, 1), cart(3, 1, 1)) - s₁ = Segment(cart(0, 2, 1), cart(2, -1, 1)) # Crossing - s₂ = Segment(cart(0.5, 1, 1), cart(0, 0, 1)) # NotIntersecting - s₃ = Segment(cart(0, 2, 1), cart(-2, 1, 1)) # NotIntersecting - s₄ = Segment(cart(0.5, -1, 1), cart(1, 0, 1)) # Touching - s₅ = Segment(cart(1.5, 0.25, 1), cart(1.5, 2, 1)) # Touching - s₆ = Segment(cart(-3, -2, 1), cart(4, 1.5, 1)) # Overlapping - s₇ = Segment(cart(0, 2, 1), cart(2, -1, 1.1)) # NotIntersecting - - @test intersection(l₁, s₁) |> type == Crossing #CASE 1 - @test l₁ ∩ s₁ ≈ s₁ ∩ l₁ ≈ cart(1.25, 0.125, 1) - @test intersection(l₁, s₂) |> type == NotIntersecting # CASE 4 - @test l₁ ∩ s₂ === s₂ ∩ l₁ === nothing - @test intersection(l₁, s₃) |> type == NotIntersecting # CASE 4 - @test l₁ ∩ s₃ === s₃ ∩ l₁ === nothing - @test intersection(l₁, s₄) |> type == Touching # CASE 2 - @test l₁ ∩ s₄ ≈ s₄ ∩ l₁ ≈ s₄(1) - @test intersection(l₁, s₅) |> type == Touching # CASE 2 - @test l₁ ∩ s₅ ≈ s₅ ∩ l₁ ≈ s₅(0) - @test intersection(l₁, s₆) |> type == Overlapping # CASE 3 - @test l₁ ∩ s₆ ≈ s₆ ∩ l₁ ≈ s₆ - @test intersection(l₁, s₇) |> type == NotIntersecting # CASE 4 - @test l₁ ∩ s₇ === s₇ ∩ l₁ === nothing - - # degenerate segments - A = cart(0.0, 0.0) - B = cart(0.5, 0.0) - C = cart(1.0, 0.0) - s₀ = Segment(A, C) - s₁ = Segment(A, A) - s₂ = Segment(B, B) - s₃ = Segment(C, C) - @test s₀ ∩ s₁ ≈ s₁ ∩ s₀ ≈ A - @test s₀ ∩ s₂ ≈ s₂ ∩ s₀ ≈ B - @test s₀ ∩ s₃ ≈ s₃ ∩ s₀ ≈ C - @test intersection(s₀, s₁) |> type == CornerTouching - @test intersection(s₀, s₂) |> type == EdgeTouching - @test intersection(s₀, s₃) |> type == CornerTouching - @test s₁ ∩ s₂ === s₂ ∩ s₁ === nothing - @test s₁ ∩ s₃ === s₃ ∩ s₁ === nothing - @test s₂ ∩ s₃ === s₃ ∩ s₂ === nothing - @test intersection(s₁, s₂) |> type == NotIntersecting - @test intersection(s₁, s₃) |> type == NotIntersecting - @test intersection(s₂, s₃) |> type == NotIntersecting - @test s₁ ∩ s₁ ≈ A - @test s₂ ∩ s₂ ≈ B - @test s₃ ∩ s₃ ≈ C - @test intersection(s₁, s₁) |> type == CornerTouching - @test intersection(s₂, s₂) |> type == CornerTouching - @test intersection(s₃, s₃) |> type == CornerTouching - - # utils - @test Meshes._sort4vals(2.5, 1.4, 1.1, 2.0) == (1.4, 2.0) - @test Meshes._sort4vals(2.0, 1.1, 1.4, 2.5) == (1.4, 2.0) - @test Meshes._sort4vals(2.0, 2.5, 1.1, 1.4) == (1.4, 2.0) - end +@testitem "Ray intersection" setup = [Setup] begin + # rays in 2D + r₁ = Ray(cart(1, 0), vector(2, 1)) + r₂ = Ray(cart(0, 2), vector(2, -3)) + r₃ = Ray(cart(0.5, 1), vector(1, -2)) + r₄ = Ray(cart(0, 2), vector(1, -3)) + r₅ = Ray(cart(4, 1.5), vector(4, 2)) + r₆ = Ray(cart(2, 0.5), vector(-0.5, -0.25)) + r₇ = Ray(cart(4, 0), vector(0, 1)) + @test intersection(r₁, r₂) |> type == Crossing #CASE 1 + @test r₁ ∩ r₂ ≈ cart(1.25, 0.125) + @test r₁ ∩ r₇ ≈ cart(4, 1.5) + @test intersection(r₁, r₃) |> type == EdgeTouching #CASE 2 + @test r₁ ∩ r₃ ≈ r₁(0) # origin of first ray + @test r₅ ∩ r₇ ≈ r₅(0) + @test intersection(r₃, r₁) |> type == EdgeTouching + @test r₃ ∩ r₁ ≈ r₁(0) # origin of second ray + @test r₇ ∩ r₅ ≈ r₅(0) + @test intersection(r₂, r₄) |> type == CornerTouching #CASE 3 + @test r₂ ∩ r₄ ≈ r₂(0) ≈ r₄(0) + @test intersection(r₅, r₁) |> type == PosOverlapping #CASE 4 + @test r₅ ∩ r₁ == r₅ # first ray + @test intersection(r₁, r₅) |> type == PosOverlapping #CASE 4 + @test r₁ ∩ r₅ == r₅ # second ray + @test intersection(r₁, r₆) |> type == NegOverlapping #CASE 5 + @test r₁ ∩ r₆ == Segment(r₁(0), r₆(0)) + @test intersection(r₁, r₄) |> type == NotIntersecting #CASE 6 + @test r₁ ∩ r₄ === r₄ ∩ r₁ === nothing + + # lines and rays in 2D + l₁ = Line(cart(0, 0), cart(4, 5)) + r₁ = Ray(cart(3, 4), vector(1, -2)) # crossing ray + r₂ = Ray(cart(1, 1.25), vector(1, 0.3)) # touching ray + r₃ = Ray(cart(-1, -1.25), vector(-1, -1.25)) # overlapping ray + r₄ = Ray(cart(1, 3), vector(1, 1.25)) # parallel ray + r₅ = Ray(cart(1, 1), vector(1, -1)) # no Intersection + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846) # CASE 1 + @test intersection(l₁, r₁) |> type === Crossing + + @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 + @test intersection(l₁, r₂) |> type === Touching + + @test l₁ ∩ r₃ == r₃ ∩ l₁ == r₃ # CASE 3 + @test intersection(l₁, r₃) |> type === Overlapping + + @test l₁ ∩ r₄ == r₄ ∩ l₁ === nothing # CASE 4 parallel + @test intersection(l₁, r₄) |> type === NotIntersecting + + @test l₁ ∩ r₅ == r₅ ∩ l₁ === nothing # CASE 4 no intersection + @test intersection(l₁, r₅) |> type === NotIntersecting + + # type stability tests + @inferred someornone(l₁, r₁) + @inferred someornone(l₁, r₅) + + # 3D tests + # lines and rays in 3D + l₁ = Line(cart(0, 0, 0.1), cart(4, 5, 0.1)) + r₁ = Ray(cart(3, 4, 0.1), vector(1, -2, 0)) # crossing ray + r₂ = Ray(cart(1, 1.25, 0.1), vector(1, 0.3, 0)) # touching ray + r₃ = Ray(cart(-1, -1.25, 0.1), vector(-1, -1.25, 0)) # overlapping ray + r₄ = Ray(cart(1, 3, 0.1), vector(1, 1.25, 0)) # parallel ray + r₅ = Ray(cart(1, 1, 0.1), vector(1, -1, 0)) # no Intersection + r₆ = Ray(cart(3, 4, 0), vector(1, -2, 1)) # crossing ray + + @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 + @test intersection(l₁, r₁) |> type === Crossing + + @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 + @test intersection(l₁, r₂) |> type === Touching + + @test l₁ ∩ r₃ == r₃ ∩ l₁ == r₃ # CASE 3 + @test intersection(l₁, r₃) |> type === Overlapping + + @test l₁ ∩ r₄ == r₄ ∩ l₁ === nothing # CASE 4 parallel + @test intersection(l₁, r₄) |> type === NotIntersecting + + @test l₁ ∩ r₅ == r₅ ∩ l₁ === nothing # CASE 4 no intersection + @test intersection(l₁, r₅) |> type === NotIntersecting + + @test l₁ ∩ r₆ == r₆ ∩ l₁ === nothing # CASE 4 no intersection + @test intersection(l₁, r₆) |> type === NotIntersecting +end - @testset "Rays" begin - # rays in 2D - r₁ = Ray(cart(1, 0), vector(2, 1)) - r₂ = Ray(cart(0, 2), vector(2, -3)) - r₃ = Ray(cart(0.5, 1), vector(1, -2)) - r₄ = Ray(cart(0, 2), vector(1, -3)) - r₅ = Ray(cart(4, 1.5), vector(4, 2)) - r₆ = Ray(cart(2, 0.5), vector(-0.5, -0.25)) - r₇ = Ray(cart(4, 0), vector(0, 1)) - @test intersection(r₁, r₂) |> type == Crossing #CASE 1 - @test r₁ ∩ r₂ ≈ cart(1.25, 0.125) - @test r₁ ∩ r₇ ≈ cart(4, 1.5) - @test intersection(r₁, r₃) |> type == EdgeTouching #CASE 2 - @test r₁ ∩ r₃ ≈ r₁(0) # origin of first ray - @test r₅ ∩ r₇ ≈ r₅(0) - @test intersection(r₃, r₁) |> type == EdgeTouching - @test r₃ ∩ r₁ ≈ r₁(0) # origin of second ray - @test r₇ ∩ r₅ ≈ r₅(0) - @test intersection(r₂, r₄) |> type == CornerTouching #CASE 3 - @test r₂ ∩ r₄ ≈ r₂(0) ≈ r₄(0) - @test intersection(r₅, r₁) |> type == PosOverlapping #CASE 4 - @test r₅ ∩ r₁ == r₅ # first ray - @test intersection(r₁, r₅) |> type == PosOverlapping #CASE 4 - @test r₁ ∩ r₅ == r₅ # second ray - @test intersection(r₁, r₆) |> type == NegOverlapping #CASE 5 - @test r₁ ∩ r₆ == Segment(r₁(0), r₆(0)) - @test intersection(r₁, r₄) |> type == NotIntersecting #CASE 6 - @test r₁ ∩ r₄ === r₄ ∩ r₁ === nothing - - # lines and rays in 2D - l₁ = Line(cart(0, 0), cart(4, 5)) - r₁ = Ray(cart(3, 4), vector(1, -2)) # crossing ray - r₂ = Ray(cart(1, 1.25), vector(1, 0.3)) # touching ray - r₃ = Ray(cart(-1, -1.25), vector(-1, -1.25)) # overlapping ray - r₄ = Ray(cart(1, 3), vector(1, 1.25)) # parallel ray - r₅ = Ray(cart(1, 1), vector(1, -1)) # no Intersection - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846) # CASE 1 - @test intersection(l₁, r₁) |> type === Crossing - - @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 - @test intersection(l₁, r₂) |> type === Touching - - @test l₁ ∩ r₃ == r₃ ∩ l₁ == r₃ # CASE 3 - @test intersection(l₁, r₃) |> type === Overlapping - - @test l₁ ∩ r₄ == r₄ ∩ l₁ === nothing # CASE 4 parallel - @test intersection(l₁, r₄) |> type === NotIntersecting - - @test l₁ ∩ r₅ == r₅ ∩ l₁ === nothing # CASE 4 no intersection - @test intersection(l₁, r₅) |> type === NotIntersecting - - # type stability tests - @inferred someornone(l₁, r₁) - @inferred someornone(l₁, r₅) - - # 3D tests - # lines and rays in 3D - l₁ = Line(cart(0, 0, 0.1), cart(4, 5, 0.1)) - r₁ = Ray(cart(3, 4, 0.1), vector(1, -2, 0)) # crossing ray - r₂ = Ray(cart(1, 1.25, 0.1), vector(1, 0.3, 0)) # touching ray - r₃ = Ray(cart(-1, -1.25, 0.1), vector(-1, -1.25, 0)) # overlapping ray - r₄ = Ray(cart(1, 3, 0.1), vector(1, 1.25, 0)) # parallel ray - r₅ = Ray(cart(1, 1, 0.1), vector(1, -1, 0)) # no Intersection - r₆ = Ray(cart(3, 4, 0), vector(1, -2, 1)) # crossing ray - - @test l₁ ∩ r₁ ≈ r₁ ∩ l₁ ≈ cart(3.0769230769230766, 3.846153846153846, 0.1) # CASE 1 - @test intersection(l₁, r₁) |> type === Crossing - - @test l₁ ∩ r₂ == r₂ ∩ l₁ == r₂(0) # CASE 2 - @test intersection(l₁, r₂) |> type === Touching - - @test l₁ ∩ r₃ == r₃ ∩ l₁ == r₃ # CASE 3 - @test intersection(l₁, r₃) |> type === Overlapping - - @test l₁ ∩ r₄ == r₄ ∩ l₁ === nothing # CASE 4 parallel - @test intersection(l₁, r₄) |> type === NotIntersecting - - @test l₁ ∩ r₅ == r₅ ∩ l₁ === nothing # CASE 4 no intersection - @test intersection(l₁, r₅) |> type === NotIntersecting - - @test l₁ ∩ r₆ == r₆ ∩ l₁ === nothing # CASE 4 no intersection - @test intersection(l₁, r₆) |> type === NotIntersecting +@testitem "Line intersection" setup = [Setup] begin + # lines in 2D + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(-1, -1), cart(-1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(-1, 0) + + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(0, 1), cart(1, 1)) + @test l1 ∩ l2 === l2 ∩ l1 === nothing + + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(1, 0), cart(2, 0)) + @test l1 == l2 + @test l1 ∩ l2 == l2 ∩ l1 == l1 + + # rounding errors + for k in 1:1000 + δ = k * atol(T) + lo = Line(cart(3.0, 1.0), cart(2.0, 2.0)) + lδ = Line(cart(1.5, 1.5 + δ), cart(3.0, 1.5 + δ)) + p = cart(2.5 - δ, 1.5 + δ) + @test lo ∩ lδ ≈ lδ ∩ lo ≈ p end - @testset "Lines" begin - # lines in 2D - l1 = Line(cart(0, 0), cart(1, 0)) - l2 = Line(cart(-1, -1), cart(-1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(-1, 0) - - l1 = Line(cart(0, 0), cart(1, 0)) - l2 = Line(cart(0, 1), cart(1, 1)) - @test l1 ∩ l2 === l2 ∩ l1 === nothing - - l1 = Line(cart(0, 0), cart(1, 0)) - l2 = Line(cart(1, 0), cart(2, 0)) - @test l1 == l2 - @test l1 ∩ l2 == l2 ∩ l1 == l1 - - # rounding errors - l1 = Line(cart(3.0, 1.0), cart(2.0, 2.0)) - for k in 1:1000 - Δ = k * atol(T) - l2 = Line(cart(1.5, 1.5 + Δ), cart(3.0, 1.5 + Δ)) - p = cart(2.5 - Δ, 1.5 + Δ) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ p - end - - # lines in 3D - # not in same plane - l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) - l2 = Line(cart(1, 1, 1), cart(1, 2, 1)) - @test l1 ∩ l2 == l2 ∩ l1 === nothing - - # in same plane but parallel - l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) - l2 = Line(cart(0, 1, 1), cart(1, 1, 1)) - @test l1 ∩ l2 == l2 ∩ l1 === nothing - - # in same plane and colinear - l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) - l2 = Line(cart(2, 0, 0), cart(3, 0, 0)) - @test l1 ∩ l2 == l2 ∩ l1 == l1 - - # crossing in one point - l1 = Line(cart(1, 2, 3), cart(2, 1, 0)) - l2 = Line(cart(1, 2, 3), cart(1, 1, 1)) - @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(1, 2, 3) - - # type stability tests - l1 = Line(cart(0, 0), cart(1, 0)) - l2 = Line(cart(-1, -1), cart(-1, 1)) - @inferred someornone(l1, l2) - end + # lines in 3D + # not in same plane + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(1, 1, 1), cart(1, 2, 1)) + @test l1 ∩ l2 == l2 ∩ l1 === nothing + + # in same plane but parallel + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(0, 1, 1), cart(1, 1, 1)) + @test l1 ∩ l2 == l2 ∩ l1 === nothing + + # in same plane and colinear + l1 = Line(cart(0, 0, 0), cart(1, 0, 0)) + l2 = Line(cart(2, 0, 0), cart(3, 0, 0)) + @test l1 ∩ l2 == l2 ∩ l1 == l1 + + # crossing in one point + l1 = Line(cart(1, 2, 3), cart(2, 1, 0)) + l2 = Line(cart(1, 2, 3), cart(1, 1, 1)) + @test l1 ∩ l2 ≈ l2 ∩ l1 ≈ cart(1, 2, 3) + + # type stability tests + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(-1, -1), cart(-1, 1)) + @inferred someornone(l1, l2) +end - @testset "Chains" begin - # https://github.com/JuliaGeometry/Meshes.jl/issues/644 - r = Rope(cart(0, 0), cart(1, 1)) - @test r ∩ r == GeometrySet([Segment(cart(0, 0), cart(1, 1))]) - @inferred someornone(r, r) - end +@testitem "Chain intersection" setup = [Setup] begin + # https://github.com/JuliaGeometry/Meshes.jl/issues/644 + r = Rope(cart(0, 0), cart(1, 1)) + @test r ∩ r == GeometrySet([Segment(cart(0, 0), cart(1, 1))]) + @inferred someornone(r, r) +end - @testset "Planes" begin - # --------- - # SEGMENTS - # --------- - - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - - # intersecting segment and plane - s = Segment(cart(0, 0, 0), cart(0, 2, 2)) - @test intersection(s, p) |> type == Crossing - @test s ∩ p == cart(0, 1, 1) - - # intersecting segment and plane with λ ≈ 0 - s = Segment(cart(0, 0, 1), cart(0, 2, 2)) - @test intersection(s, p) |> type == Touching - @test s ∩ p == cart(0, 0, 1) - - # intersecting segment and plane with λ ≈ 1 - s = Segment(cart(0, 0, 2), cart(0, 2, 1)) - @test intersection(s, p) |> type == Touching - @test s ∩ p == cart(0, 2, 1) - - # segment contained within plane - s = Segment(cart(0, 0, 1), cart(0, -2, 1)) - @test intersection(s, p) |> type == Overlapping - @test s ∩ p == s - - # segment below plane, non-intersecting - s = Segment(cart(0, 0, 0), cart(0, -2, -2)) - @test intersection(s, p) |> type == NotIntersecting - @test isnothing(s ∩ p) - - # segment parallel to plane, offset, non-intersecting - s = Segment(cart(0, 0, -1), cart(0, -2, -1)) - @test intersection(s, p) |> type == NotIntersecting - @test isnothing(s ∩ p) - - # plane as first argument - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - s = Segment(cart(0, 0, 0), cart(0, 2, 2)) - @test intersection(p, s) |> type == Crossing - @test s ∩ p == p ∩ s == cart(0, 1, 1) - - # type stability tests - s = Segment(cart(0, 0, 0), cart(0, 2, 2)) - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - @inferred someornone(s, p) - - # ----- - # RAYS - # ----- - - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - - # intersecting ray and plane - r = Ray(cart(0, 0, 0), vector(0, 2, 2)) - @test intersection(r, p) |> type == Crossing - @test r ∩ p == cart(0, 1, 1) - - # intersecting ray and plane with λ ≈ 0 - r = Ray(cart(0, 0, 1), vector(0, 2, 1)) - @test intersection(r, p) |> type == Touching - @test r ∩ p == cart(0, 0, 1) - - # intersecting ray and plane with λ ≈ 1 (only case where Ray different to Segment) - r = Ray(cart(0, 0, 2), vector(0, 2, -1)) - @test intersection(r, p) |> type == Crossing - @test r ∩ p == cart(0, 2, 1) - - # ray contained within plane - r = Ray(cart(0, 0, 1), vector(0, -2, 0)) - @test intersection(r, p) |> type == Overlapping - @test r ∩ p == r - - # ray below plane, non-intersecting - r = Ray(cart(0, 0, 0), vector(0, -2, -2)) - @test intersection(r, p) |> type == NotIntersecting - @test isnothing(r ∩ p) - - # ray parallel to plane, offset, non-intersecting - r = Ray(cart(0, 0, -1), vector(0, -2, 0)) - @test intersection(r, p) |> type == NotIntersecting - @test isnothing(r ∩ p) - - # plane as first argument - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - r = Ray(cart(0, 0, 0), vector(0, 2, 2)) - @test intersection(p, r) |> type == Crossing - @test r ∩ p == p ∩ r == cart(0, 1, 1) - - # ------ - # LINES - # ------ - - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - - # intersecting line and plane - l = Line(cart(0, 0, 0), cart(0, 2, 2)) - @test intersection(l, p) |> type == Crossing - @test l ∩ p == cart(0, 1, 1) - - # intersecting line and plane with λ ≈ 0 - l = Line(cart(0, 0, 1), cart(0, 2, 2)) - @test intersection(l, p) |> type == Crossing - @test l ∩ p == cart(0, 0, 1) - - # intersecting line and plane with λ ≈ 1 - l = Line(cart(0, 0, 2), cart(0, 2, 1)) - @test intersection(l, p) |> type == Crossing - @test l ∩ p == cart(0, 2, 1) - - # line contained within plane - l = Line(cart(0, 0, 1), cart(0, -2, 1)) - @test intersection(l, p) |> type == Overlapping - @test l ∩ p == l - - # line below plane, non-intersecting - l = Line(cart(0, 0, 0), cart(0, -2, -2)) - @test intersection(l, p) |> type == Crossing - @test l ∩ p == cart(0, 1, 1) - - # line parallel to plane, offset, non-intersecting - l = Line(cart(0, 0, -1), cart(0, -2, -1)) - @test intersection(l, p) |> type == NotIntersecting - @test isnothing(l ∩ p) - - # plane as first argument - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - l = Line(cart(0, 0, 0), cart(0, 2, 2)) - @test intersection(p, l) |> type == Crossing - @test l ∩ p == p ∩ l == cart(0, 1, 1) - - # type stability tests - l = Line(cart(0, 0, 0), cart(0, 2, 2)) - p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) - @inferred someornone(l, p) - - # ------ - # PLANES - # ------ - - p1 = Plane(cart(0, 0, 0), vector(0, 0, 1)) - - # p1 parallel to p2 - p2 = Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test intersection(p1, p2) |> type == NotIntersecting - @test isnothing(p1 ∩ p2) - - # p1 intersects p2 - p2 = Plane(cart(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) - @test intersection(p1, p2) |> type == Intersecting - @test p1 ∩ p2 == Line(cart(1, 0, 0), cart(1, 1, 0)) - - # CRS propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) - p1 = Plane(Point(c1), vector(0, 0, 1)) - p2 = Plane(Point(c2), vector(1 / sqrt(2), 0, 1 / sqrt(2))) - @test crs(p1 ∩ p2) === crs(p1) - end +@testitem "Plane intersection" setup = [Setup] begin + # --------- + # SEGMENTS + # --------- + + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + + # intersecting segment and plane + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) + @test intersection(s, p) |> type == Crossing + @test s ∩ p == cart(0, 1, 1) + + # intersecting segment and plane with λ ≈ 0 + s = Segment(cart(0, 0, 1), cart(0, 2, 2)) + @test intersection(s, p) |> type == Touching + @test s ∩ p == cart(0, 0, 1) + + # intersecting segment and plane with λ ≈ 1 + s = Segment(cart(0, 0, 2), cart(0, 2, 1)) + @test intersection(s, p) |> type == Touching + @test s ∩ p == cart(0, 2, 1) + + # segment contained within plane + s = Segment(cart(0, 0, 1), cart(0, -2, 1)) + @test intersection(s, p) |> type == Overlapping + @test s ∩ p == s + + # segment below plane, non-intersecting + s = Segment(cart(0, 0, 0), cart(0, -2, -2)) + @test intersection(s, p) |> type == NotIntersecting + @test isnothing(s ∩ p) + + # segment parallel to plane, offset, non-intersecting + s = Segment(cart(0, 0, -1), cart(0, -2, -1)) + @test intersection(s, p) |> type == NotIntersecting + @test isnothing(s ∩ p) + + # plane as first argument + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) + @test intersection(p, s) |> type == Crossing + @test s ∩ p == p ∩ s == cart(0, 1, 1) + + # type stability tests + s = Segment(cart(0, 0, 0), cart(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + @inferred someornone(s, p) + + # ----- + # RAYS + # ----- + + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + + # intersecting ray and plane + r = Ray(cart(0, 0, 0), vector(0, 2, 2)) + @test intersection(r, p) |> type == Crossing + @test r ∩ p == cart(0, 1, 1) + + # intersecting ray and plane with λ ≈ 0 + r = Ray(cart(0, 0, 1), vector(0, 2, 1)) + @test intersection(r, p) |> type == Touching + @test r ∩ p == cart(0, 0, 1) + + # intersecting ray and plane with λ ≈ 1 (only case where Ray different to Segment) + r = Ray(cart(0, 0, 2), vector(0, 2, -1)) + @test intersection(r, p) |> type == Crossing + @test r ∩ p == cart(0, 2, 1) + + # ray contained within plane + r = Ray(cart(0, 0, 1), vector(0, -2, 0)) + @test intersection(r, p) |> type == Overlapping + @test r ∩ p == r + + # ray below plane, non-intersecting + r = Ray(cart(0, 0, 0), vector(0, -2, -2)) + @test intersection(r, p) |> type == NotIntersecting + @test isnothing(r ∩ p) + + # ray parallel to plane, offset, non-intersecting + r = Ray(cart(0, 0, -1), vector(0, -2, 0)) + @test intersection(r, p) |> type == NotIntersecting + @test isnothing(r ∩ p) + + # plane as first argument + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + r = Ray(cart(0, 0, 0), vector(0, 2, 2)) + @test intersection(p, r) |> type == Crossing + @test r ∩ p == p ∩ r == cart(0, 1, 1) + + # ------ + # LINES + # ------ + + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + + # intersecting line and plane + l = Line(cart(0, 0, 0), cart(0, 2, 2)) + @test intersection(l, p) |> type == Crossing + @test l ∩ p == cart(0, 1, 1) + + # intersecting line and plane with λ ≈ 0 + l = Line(cart(0, 0, 1), cart(0, 2, 2)) + @test intersection(l, p) |> type == Crossing + @test l ∩ p == cart(0, 0, 1) + + # intersecting line and plane with λ ≈ 1 + l = Line(cart(0, 0, 2), cart(0, 2, 1)) + @test intersection(l, p) |> type == Crossing + @test l ∩ p == cart(0, 2, 1) + + # line contained within plane + l = Line(cart(0, 0, 1), cart(0, -2, 1)) + @test intersection(l, p) |> type == Overlapping + @test l ∩ p == l + + # line below plane, non-intersecting + l = Line(cart(0, 0, 0), cart(0, -2, -2)) + @test intersection(l, p) |> type == Crossing + @test l ∩ p == cart(0, 1, 1) + + # line parallel to plane, offset, non-intersecting + l = Line(cart(0, 0, -1), cart(0, -2, -1)) + @test intersection(l, p) |> type == NotIntersecting + @test isnothing(l ∩ p) + + # plane as first argument + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + l = Line(cart(0, 0, 0), cart(0, 2, 2)) + @test intersection(p, l) |> type == Crossing + @test l ∩ p == p ∩ l == cart(0, 1, 1) + + # type stability tests + l = Line(cart(0, 0, 0), cart(0, 2, 2)) + p = Plane(cart(0, 0, 1), vector(1, 0, 0), vector(0, 1, 0)) + @inferred someornone(l, p) + + # ------ + # PLANES + # ------ + + p1 = Plane(cart(0, 0, 0), vector(0, 0, 1)) + + # p1 parallel to p2 + p2 = Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test intersection(p1, p2) |> type == NotIntersecting + @test isnothing(p1 ∩ p2) + + # p1 intersects p2 + p2 = Plane(cart(0, 0, 1), vector(1 / sqrt(2), 0, 1 / sqrt(2))) + @test intersection(p1, p2) |> type == Intersecting + @test p1 ∩ p2 == Line(cart(1, 0, 0), cart(1, 1, 0)) + + # CRS propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + p1 = Plane(Point(c1), vector(0, 0, 1)) + p2 = Plane(Point(c2), vector(1 / sqrt(2), 0, 1 / sqrt(2))) + @test crs(p1 ∩ p2) === crs(p1) +end - @testset "Boxes" begin - b1 = Box(cart(0, 0), cart(1, 1)) - b2 = Box(cart(0.5, 0.5), cart(2, 2)) - b3 = Box(cart(2, 2), cart(3, 3)) - b4 = Box(cart(1, 1), cart(2, 2)) - b5 = Box(cart(1.0, 0.5), cart(2, 2)) - b6 = Box(cart(0, 2), cart(1, 3)) - b7 = Box(cart(0, 1), cart(1, 2)) - b8 = Box(cart(0, -1), cart(1, 0)) - b9 = Box(cart(1, 0), cart(2, 1)) - b10 = Box(cart(-1, 0), cart(0, 1)) - @test intersection(b1, b2) |> type == Overlapping - @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) - @test intersection(b1, b3) |> type == NotIntersecting - @test isnothing(b1 ∩ b3) - @test intersection(b1, b4) |> type == CornerTouching - @test b1 ∩ b4 == cart(1, 1) - @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(cart(1.0, 0.5), cart(1, 1)) - @test intersection(b1, b6) |> type == NotIntersecting - @test isnothing(b1 ∩ b6) - @test intersection(b1, b7) |> type == Touching - @test b1 ∩ b7 == Box(cart(0, 1), cart(1, 1)) - @test intersection(b1, b8) |> type == Touching - @test b1 ∩ b8 == Box(cart(0, 0), cart(1, 0)) - @test intersection(b1, b9) |> type == Touching - @test b1 ∩ b9 == Box(cart(1, 0), cart(1, 1)) - @test intersection(b1, b10) |> type == Touching - @test b1 ∩ b10 == Box(cart(0, 0), cart(0, 1)) - - # more touching examples - b1 = Box(cart(0, 0), cart(1, 1)) - b2 = Box(cart(1.0, 0.5), cart(2, 1)) - b3 = Box(cart(-1, 0), cart(0.0, 0.5)) - b4 = Box(cart(0, 1), cart(0.5, 2.0)) - b5 = Box(cart(0.5, -1.0), cart(1, 0)) - @test intersection(b1, b2) |> type == Touching - @test b1 ∩ b2 == Box(cart(1.0, 0.5), cart(1, 1)) - @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(cart(0.0, 0.0), cart(0.0, 0.5)) - @test intersection(b1, b4) |> type == Touching - @test b1 ∩ b4 == Box(cart(0.0, 1.0), cart(0.5, 1.0)) - @test intersection(b1, b5) |> type == Touching - @test b1 ∩ b5 == Box(cart(0.5, 0.0), cart(1.0, 0.0)) - - # tricky examples with degenerate boxes - b1 = Box(cart(0, 0, 0), cart(2, 2, 0)) - b2 = Box(cart(3, 0, 0), cart(5, 2, 0)) - b3 = Box(cart(1, 0, 0), cart(3, 2, 0)) - @test intersection(b1, b2) |> type == NotIntersecting - @test isnothing(b1 ∩ b2) - @test intersection(b1, b3) |> type == Touching - @test b1 ∩ b3 == Box(cart(1, 0, 0), cart(2, 2, 0)) - - # different units - b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) - b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) - @test intersection(b1, b2) |> type == Overlapping - @test unit(Meshes.lentype(b1 ∩ b2)) == u"cm" - @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) - - # type stability tests - b1 = Box(cart(0, 0), cart(1, 1)) - b2 = Box(cart(0.5, 0.5), cart(2, 2)) - @inferred someornone(b1, b2) - b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) - b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) - @inferred someornone(b1, b2) - - # CRS propagation - b1 = Box(merc(0, 0), merc(1, 1)) - b2 = Box(merc(0.5, 0.5), merc(2, 2)) - @test crs(b1 ∩ b2) === crs(b1) - - # Ray-Box intersection - b = Box(cart(0, 0, 0), cart(1, 1, 1)) - - r = Ray(cart(0, 0, 0), vector(1, 1, 1)) - @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(cart(0, 0, 0), cart(1, 1, 1)) - - r = Ray(cart(-0.5, 0, 0), vector(1.0, 1.0, 1.0)) - @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(cart(0.0, 0.5, 0.5), cart(0.5, 1.0, 1.0)) - - r = Ray(cart(3.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) - @test intersection(r, b) |> type == NotIntersecting - - r = Ray(cart(2.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) - @test intersection(r, b) |> type == Touching - @test r ∩ b == cart(1.0, 1.0, 0.5) - - # the ray on a face of the box, got NaN in calculation - r = Ray(cart(1.5, 0.0, 0.0), vector(-1.0, 1.0, 0.0)) - @test intersection(r, b) |> type == Crossing - @test r ∩ b == Segment(cart(1.0, 0.5, 0.0), cart(0.5, 1.0, 0.0)) - end +@testitem "Box intersection" setup = [Setup] begin + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(0.5, 0.5), cart(2, 2)) + b3 = Box(cart(2, 2), cart(3, 3)) + b4 = Box(cart(1, 1), cart(2, 2)) + b5 = Box(cart(1.0, 0.5), cart(2, 2)) + b6 = Box(cart(0, 2), cart(1, 3)) + b7 = Box(cart(0, 1), cart(1, 2)) + b8 = Box(cart(0, -1), cart(1, 0)) + b9 = Box(cart(1, 0), cart(2, 1)) + b10 = Box(cart(-1, 0), cart(0, 1)) + @test intersection(b1, b2) |> type == Overlapping + @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) + @test intersection(b1, b3) |> type == NotIntersecting + @test isnothing(b1 ∩ b3) + @test intersection(b1, b4) |> type == CornerTouching + @test b1 ∩ b4 == cart(1, 1) + @test intersection(b1, b5) |> type == Touching + @test b1 ∩ b5 == Box(cart(1.0, 0.5), cart(1, 1)) + @test intersection(b1, b6) |> type == NotIntersecting + @test isnothing(b1 ∩ b6) + @test intersection(b1, b7) |> type == Touching + @test b1 ∩ b7 == Box(cart(0, 1), cart(1, 1)) + @test intersection(b1, b8) |> type == Touching + @test b1 ∩ b8 == Box(cart(0, 0), cart(1, 0)) + @test intersection(b1, b9) |> type == Touching + @test b1 ∩ b9 == Box(cart(1, 0), cart(1, 1)) + @test intersection(b1, b10) |> type == Touching + @test b1 ∩ b10 == Box(cart(0, 0), cart(0, 1)) + + # more touching examples + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(1.0, 0.5), cart(2, 1)) + b3 = Box(cart(-1, 0), cart(0.0, 0.5)) + b4 = Box(cart(0, 1), cart(0.5, 2.0)) + b5 = Box(cart(0.5, -1.0), cart(1, 0)) + @test intersection(b1, b2) |> type == Touching + @test b1 ∩ b2 == Box(cart(1.0, 0.5), cart(1, 1)) + @test intersection(b1, b3) |> type == Touching + @test b1 ∩ b3 == Box(cart(0.0, 0.0), cart(0.0, 0.5)) + @test intersection(b1, b4) |> type == Touching + @test b1 ∩ b4 == Box(cart(0.0, 1.0), cart(0.5, 1.0)) + @test intersection(b1, b5) |> type == Touching + @test b1 ∩ b5 == Box(cart(0.5, 0.0), cart(1.0, 0.0)) + + # tricky examples with degenerate boxes + b1 = Box(cart(0, 0, 0), cart(2, 2, 0)) + b2 = Box(cart(3, 0, 0), cart(5, 2, 0)) + b3 = Box(cart(1, 0, 0), cart(3, 2, 0)) + @test intersection(b1, b2) |> type == NotIntersecting + @test isnothing(b1 ∩ b2) + @test intersection(b1, b3) |> type == Touching + @test b1 ∩ b3 == Box(cart(1, 0, 0), cart(2, 2, 0)) + + # different units + b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) + b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) + @test intersection(b1, b2) |> type == Overlapping + @test unit(Meshes.lentype(b1 ∩ b2)) == u"cm" + @test b1 ∩ b2 == Box(cart(0.5, 0.5), cart(1, 1)) + + # type stability tests + b1 = Box(cart(0, 0), cart(1, 1)) + b2 = Box(cart(0.5, 0.5), cart(2, 2)) + @inferred someornone(b1, b2) + b1 = Box((T(0) * u"cm", T(0) * u"cm"), (T(100) * u"cm", T(100) * u"cm")) + b2 = Box((T(500) * u"mm", T(500) * u"mm"), (T(2000) * u"mm", T(2000) * u"mm")) + @inferred someornone(b1, b2) + + # CRS propagation + b1 = Box(merc(0, 0), merc(1, 1)) + b2 = Box(merc(0.5, 0.5), merc(2, 2)) + @test crs(b1 ∩ b2) === crs(b1) + + # Ray-Box intersection + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + + r = Ray(cart(0, 0, 0), vector(1, 1, 1)) + @test intersection(r, b) |> type == Crossing + @test r ∩ b == Segment(cart(0, 0, 0), cart(1, 1, 1)) + + r = Ray(cart(-0.5, 0, 0), vector(1.0, 1.0, 1.0)) + @test intersection(r, b) |> type == Crossing + @test r ∩ b == Segment(cart(0.0, 0.5, 0.5), cart(0.5, 1.0, 1.0)) + + r = Ray(cart(3.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) + @test intersection(r, b) |> type == NotIntersecting + + r = Ray(cart(2.0, 0.0, 0.5), vector(-1.0, 1.0, 0.0)) + @test intersection(r, b) |> type == Touching + @test r ∩ b == cart(1.0, 1.0, 0.5) + + # the ray on a face of the box, got NaN in calculation + r = Ray(cart(1.5, 0.0, 0.0), vector(-1.0, 1.0, 0.0)) + @test intersection(r, b) |> type == Crossing + @test r ∩ b == Segment(cart(1.0, 0.5, 0.0), cart(0.5, 1.0, 0.0)) +end - @testset "Triangles" begin - # utility to reverse segments, to more fully - # test branches in the intersection algorithm - reverse_segment(s) = Segment(vertices(s)[2], vertices(s)[1]) - - # intersections with triangle lying in XY plane - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - - # intersects through t - s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) - @test intersection(s, t) |> type == Intersecting - @test s ∩ t == cart(0.2, 0.2, 0.0) - - # intersects at a vertex of t - s = Segment(cart(0.0, 0.0, 1.0), cart(0.0, 0.0, -1.0)) - @test intersection(s, t) |> type == Intersecting - @test s ∩ t == cart(0.0, 0.0, 0.0) - - # normal to, doesn't intersect with t - s = Segment(cart(0.9, 0.9, 1.0), cart(0.9, 0.9, -1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # coplanar, doesn't intersect with t - s = Segment(cart(-0.2, -0.2, 0.0), cart(1.2, -0.2, 0.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # parallel, above, doesn't intersect with t - s = Segment(cart(-0.2, 0.2, 1.0), cart(1.2, 0.2, 1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # parallel, below, doesn't intersect with t - s = Segment(cart(-0.2, 0.2, -1.0), cart(1.2, 0.2, -1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # coplanar, within bounding box of t, no intersection - s = Segment(cart(0.7, 0.8, 0.0), cart(0.8, 0.7, 0.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment above and to right of t, no intersection - s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, 1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment below t, no intersection - s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, 1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment left of t, no intersection - s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, 1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment above and to right of t, no intersection - s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, -1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - @test intersection(reverse_segment(s), t) |> type == NotIntersecting - @test isnothing(reverse_segment(s) ∩ t) - - # segment below t, no intersection - s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, -1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - @test intersection(reverse_segment(s), t) |> type == NotIntersecting - @test isnothing(reverse_segment(s) ∩ t) - - # segment left of t, no intersection - s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, -1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - @test intersection(reverse_segment(s), t) |> type == NotIntersecting - @test isnothing(reverse_segment(s) ∩ t) - - # segment above and to right of t, no intersection - s = Segment(cart(1.0, 1.0, 1.0), cart(1.0, 1.0, 0.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment below t, no intersection - s = Segment(cart(0.5, -1.0, 1.0), cart(0.5, -1.0, 0.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # segment left of t, no intersection - s = Segment(cart(-1.0, 0.5, 1.0), cart(-1.0, 0.5, 0.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # intersections with an inclined inclined triangle t - t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) - - # doesn't reach t, no intersection - s = Segment(cart(0.5, 0.5, 1.9), cart(0.5, 0.5, 1.8)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # parallel, offset from t, no intersection - s = Segment(cart(0.0, 0.5, 1.0), cart(1.0, 0.5, 1.0)) - @test intersection(s, t) |> type == NotIntersecting - @test isnothing(s ∩ t) - - # triangle as first argument - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) - @test intersection(t, s) |> type == Intersecting - @test s ∩ t == t ∩ s == cart(0.2, 0.2, 0.0) - - # type stability tests - s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - @inferred someornone(s, t) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/728 - s = Segment(cart(0.5, 0.5, 0.0), cart(0.5, 0.5, 2.0)) - t = Triangle(cart(1.0, 0.0, 0.0), cart(0.0, 1.0, 0.0), cart(0.0, 0.0, 1.0)) - @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) - s = Segment(cart(0.5, 0.5, 2.0), cart(0.5, 0.5, 0.0)) - @test intersection(s, t) |> type == Intersecting - @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) - - # Intersection for a triangle and a ray - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - - # intersects through t - r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == Crossing - @test r ∩ t == cart(0.2, 0.2, 0.0) - # origin of ray intersects with middle of triangle - r = Ray(cart(0.2, 0.2, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == Touching - @test r ∩ t == cart(0.2, 0.2, 0.0) - # Special case: the direction vector is not length enough to cross triangle - r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -0.00001)) - @test intersection(r, t) |> type == Crossing - if T == Float64 - @test r ∩ t ≈ cart(0.2, 0.2, 0.0) - end - # Special case: reverse direction vector should not hit the triangle - r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, 1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # intersects at a vertex of t - r = Ray(cart(0.0, 0.0, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == CornerCrossing - @test r ∩ t ≈ cart(0.0, 0.0, 0.0) - - # normal to, doesn't intersect with t - r = Ray(cart(0.9, 0.9, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # coplanar, doesn't intersect with t - r = Ray(cart(-0.2, -0.2, 0.0), vector(1.0, 0.0, 0.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # parallel, above, doesn't intersect with t - r = Ray(cart(-0.2, 0.2, 1.0), vector(1.0, 0.0, 0.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # parallel, below, doesn't intersect with t - r = Ray(cart(-0.2, 0.2, -1.0), vector(1.0, 0.0, 0.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # coplanar, within bounding box of t, no intersection - r = Ray(cart(0.7, 0.8, 0.0), vector(1.0, -1.0, 0.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray above and to right of t, no intersection - r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, 1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray below t, no intersection - r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, 1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray left of t, no intersection - r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, 1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray above and to right of t, no intersection - r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray below t, no intersection - r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray left of t, no intersection - r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray above and to right of t, no intersection - r = Ray(cart(1.0, 1.0, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray below t, no intersection - r = Ray(cart(0.5, -1.0, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # ray left of t, no intersection - r = Ray(cart(-1.0, 0.5, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # intersections with an inclined inclined triangle t - t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) - - # doesn't reach t, but a ray can hit the triangle - r = Ray(cart(0.5, 0.5, 1.9), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == Crossing - @test r ∩ t ≈ cart(0.5, 0.5, 0.5) - - # parallel, offset from t, no intersection - r = Ray(cart(0.0, 0.5, 1.0), vector(1.0, 0.0, 0.0)) - @test intersection(r, t) |> type == NotIntersecting - @test isnothing(r ∩ t) - - # origin of ray intersects with vertex of triangle - r = Ray(cart(0.0, 0.0, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == CornerTouching - @test r ∩ t ≈ cart(0.0, 0.0, 0.0) - - # origin of ray intersects with edge of triangle - r = Ray(cart(0.5, 0.0, 0.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == EdgeTouching - @test r ∩ t ≈ cart(0.5, 0.0, 0.0) - - # ray intersects with edge of triangle - r = Ray(cart(0.5, 0.0, 1.0), vector(0.0, 0.0, -1.0)) - @test intersection(r, t) |> type == EdgeCrossing - @test r ∩ t ≈ cart(0.5, 0.0, 0.0) +@testitem "Triangle intersection" setup = [Setup] begin + # utility to reverse segments, to more fully + # test branches in the intersection algorithm + reverse_segment(s) = Segment(vertices(s)[2], vertices(s)[1]) + + # intersections with triangle lying in XY plane + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + + # intersects through t + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) + @test intersection(s, t) |> type == Intersecting + @test s ∩ t == cart(0.2, 0.2, 0.0) + + # intersects at a vertex of t + s = Segment(cart(0.0, 0.0, 1.0), cart(0.0, 0.0, -1.0)) + @test intersection(s, t) |> type == Intersecting + @test s ∩ t == cart(0.0, 0.0, 0.0) + + # normal to, doesn't intersect with t + s = Segment(cart(0.9, 0.9, 1.0), cart(0.9, 0.9, -1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # coplanar, doesn't intersect with t + s = Segment(cart(-0.2, -0.2, 0.0), cart(1.2, -0.2, 0.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # parallel, above, doesn't intersect with t + s = Segment(cart(-0.2, 0.2, 1.0), cart(1.2, 0.2, 1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # parallel, below, doesn't intersect with t + s = Segment(cart(-0.2, 0.2, -1.0), cart(1.2, 0.2, -1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # coplanar, within bounding box of t, no intersection + s = Segment(cart(0.7, 0.8, 0.0), cart(0.8, 0.7, 0.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment above and to right of t, no intersection + s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, 1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment below t, no intersection + s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, 1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment left of t, no intersection + s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, 1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment above and to right of t, no intersection + s = Segment(cart(1.0, 1.0, 0.0), cart(1.0, 1.0, -1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + @test intersection(reverse_segment(s), t) |> type == NotIntersecting + @test isnothing(reverse_segment(s) ∩ t) + + # segment below t, no intersection + s = Segment(cart(0.5, -1.0, 0.0), cart(0.5, -1.0, -1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + @test intersection(reverse_segment(s), t) |> type == NotIntersecting + @test isnothing(reverse_segment(s) ∩ t) + + # segment left of t, no intersection + s = Segment(cart(-1.0, 0.5, 0.0), cart(-1.0, 0.5, -1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + @test intersection(reverse_segment(s), t) |> type == NotIntersecting + @test isnothing(reverse_segment(s) ∩ t) + + # segment above and to right of t, no intersection + s = Segment(cart(1.0, 1.0, 1.0), cart(1.0, 1.0, 0.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment below t, no intersection + s = Segment(cart(0.5, -1.0, 1.0), cart(0.5, -1.0, 0.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # segment left of t, no intersection + s = Segment(cart(-1.0, 0.5, 1.0), cart(-1.0, 0.5, 0.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # intersections with an inclined inclined triangle t + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) + + # doesn't reach t, no intersection + s = Segment(cart(0.5, 0.5, 1.9), cart(0.5, 0.5, 1.8)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # parallel, offset from t, no intersection + s = Segment(cart(0.0, 0.5, 1.0), cart(1.0, 0.5, 1.0)) + @test intersection(s, t) |> type == NotIntersecting + @test isnothing(s ∩ t) + + # triangle as first argument + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) + @test intersection(t, s) |> type == Intersecting + @test s ∩ t == t ∩ s == cart(0.2, 0.2, 0.0) + + # type stability tests + s = Segment(cart(0.2, 0.2, 1.0), cart(0.2, 0.2, -1.0)) + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + @inferred someornone(s, t) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/728 + s = Segment(cart(0.5, 0.5, 0.0), cart(0.5, 0.5, 2.0)) + t = Triangle(cart(1.0, 0.0, 0.0), cart(0.0, 1.0, 0.0), cart(0.0, 0.0, 1.0)) + @test intersection(s, t) |> type == Intersecting + @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) + s = Segment(cart(0.5, 0.5, 2.0), cart(0.5, 0.5, 0.0)) + @test intersection(s, t) |> type == Intersecting + @test s ∩ t == t ∩ s == cart(0.5, 0.5, 0.0) + + # Intersection for a triangle and a ray + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + + # intersects through t + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == Crossing + @test r ∩ t == cart(0.2, 0.2, 0.0) + # origin of ray intersects with middle of triangle + r = Ray(cart(0.2, 0.2, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == Touching + @test r ∩ t == cart(0.2, 0.2, 0.0) + # Special case: the direction vector is not length enough to cross triangle + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, -0.00001)) + @test intersection(r, t) |> type == Crossing + if T == Float64 + @test r ∩ t ≈ cart(0.2, 0.2, 0.0) end + # Special case: reverse direction vector should not hit the triangle + r = Ray(cart(0.2, 0.2, 1.0), vector(0.0, 0.0, 1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # intersects at a vertex of t + r = Ray(cart(0.0, 0.0, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == CornerCrossing + @test r ∩ t ≈ cart(0.0, 0.0, 0.0) + + # normal to, doesn't intersect with t + r = Ray(cart(0.9, 0.9, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # coplanar, doesn't intersect with t + r = Ray(cart(-0.2, -0.2, 0.0), vector(1.0, 0.0, 0.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # parallel, above, doesn't intersect with t + r = Ray(cart(-0.2, 0.2, 1.0), vector(1.0, 0.0, 0.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # parallel, below, doesn't intersect with t + r = Ray(cart(-0.2, 0.2, -1.0), vector(1.0, 0.0, 0.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # coplanar, within bounding box of t, no intersection + r = Ray(cart(0.7, 0.8, 0.0), vector(1.0, -1.0, 0.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray above and to right of t, no intersection + r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, 1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray below t, no intersection + r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, 1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray left of t, no intersection + r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, 1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray above and to right of t, no intersection + r = Ray(cart(1.0, 1.0, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray below t, no intersection + r = Ray(cart(0.5, -1.0, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray left of t, no intersection + r = Ray(cart(-1.0, 0.5, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray above and to right of t, no intersection + r = Ray(cart(1.0, 1.0, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray below t, no intersection + r = Ray(cart(0.5, -1.0, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # ray left of t, no intersection + r = Ray(cart(-1.0, 0.5, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # intersections with an inclined inclined triangle t + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) + + # doesn't reach t, but a ray can hit the triangle + r = Ray(cart(0.5, 0.5, 1.9), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == Crossing + @test r ∩ t ≈ cart(0.5, 0.5, 0.5) + + # parallel, offset from t, no intersection + r = Ray(cart(0.0, 0.5, 1.0), vector(1.0, 0.0, 0.0)) + @test intersection(r, t) |> type == NotIntersecting + @test isnothing(r ∩ t) + + # origin of ray intersects with vertex of triangle + r = Ray(cart(0.0, 0.0, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == CornerTouching + @test r ∩ t ≈ cart(0.0, 0.0, 0.0) + + # origin of ray intersects with edge of triangle + r = Ray(cart(0.5, 0.0, 0.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == EdgeTouching + @test r ∩ t ≈ cart(0.5, 0.0, 0.0) + + # ray intersects with edge of triangle + r = Ray(cart(0.5, 0.0, 1.0), vector(0.0, 0.0, -1.0)) + @test intersection(r, t) |> type == EdgeCrossing + @test r ∩ t ≈ cart(0.5, 0.0, 0.0) +end - @testset "Ngons" begin - o = Octagon( - cart(0.0, 0.0, 1.0), - cart(0.5, -0.5, 0.0), - cart(1.0, 0.0, 0.0), - cart(1.5, 0.5, -0.5), - cart(1.0, 1.0, 0.0), - cart(0.5, 1.5, 0.0), - cart(0.0, 1.0, 0.0), - cart(-0.5, 0.5, 0.0) - ) - - r = Ray(cart(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) - @test intersection(r, o) |> type == Intersecting - @test r ∩ o == PointSet(cart(0.0, 0.0, 0.0)) - - r = Ray(cart(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) - @test intersection(r, o) |> type == NotIntersecting - @test isnothing(r ∩ o) - - t = Triangle(cart(0.9356498598903396, 6.5), cart(1.3571428571428377, 6.5), cart(1.0, 7.0)) - q = Quadrangle(cart(0.0, 0.0), cart(6.0, 0.0), cart(1.0, 7.0), cart(1.0, 6.0)) - @test intersection(t, q) |> type == Intersecting - @test t ∩ q isa PolyArea - @test q ∩ t isa PolyArea - - t1 = Triangle(cart(0.0, 0.0), cart(0.0, 1.000000000000001), cart(1.0, 1.0)) - t2 = Triangle(cart(0.0, 1.0), cart(0.0, 2.0), cart(1.0, 1.000000000001)) - @test intersection(t1, t2) |> type == Intersecting - @test t1 ∩ t2 isa PolyArea - end +@testitem "Ngon intersection" setup = [Setup] begin + o = Octagon( + cart(0.0, 0.0, 1.0), + cart(0.5, -0.5, 0.0), + cart(1.0, 0.0, 0.0), + cart(1.5, 0.5, -0.5), + cart(1.0, 1.0, 0.0), + cart(0.5, 1.5, 0.0), + cart(0.0, 1.0, 0.0), + cart(-0.5, 0.5, 0.0) + ) + + r = Ray(cart(-1.0, -1.0, -1.0), vector(1.0, 1.0, 1.0)) + @test intersection(r, o) |> type == Intersecting + @test r ∩ o == PointSet(cart(0.0, 0.0, 0.0)) + + r = Ray(cart(-1.0, -1.0, -1.0), vector(-1.0, -1.0, -1.0)) + @test intersection(r, o) |> type == NotIntersecting + @test isnothing(r ∩ o) + + t = Triangle(cart(0.9356498598903396, 6.5), cart(1.3571428571428377, 6.5), cart(1.0, 7.0)) + q = Quadrangle(cart(0.0, 0.0), cart(6.0, 0.0), cart(1.0, 7.0), cart(1.0, 6.0)) + @test intersection(t, q) |> type == Intersecting + @test t ∩ q isa PolyArea + @test q ∩ t isa PolyArea + + t1 = Triangle(cart(0.0, 0.0), cart(0.0, 1.000000000000001), cart(1.0, 1.0)) + t2 = Triangle(cart(0.0, 1.0), cart(0.0, 2.0), cart(1.0, 1.000000000001)) + @test intersection(t1, t2) |> type == Intersecting + @test t1 ∩ t2 isa PolyArea +end - @testset "Polygons" begin - # triangle - poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - @test intersection(poly, other) |> type == Intersecting - @test all(vertices(poly ∩ other) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) - - # octagon - poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - @test intersection(poly, other) |> type == Intersecting - @test all( - vertices(poly ∩ other) .≈ - [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] - ) - - # inside - poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - @test intersection(poly, other) |> type == Intersecting - @test all(vertices(poly ∩ other) .≈ vertices(poly)) - - # outside - poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) - other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - @test intersection(poly, other) |> type == NotIntersecting - @test isnothing(poly ∩ other) - - # convex and non-convex polygons - quad = Quadrangle(cart(0, 0), cart(0.1, 0.0), cart(0.1, 0.1), cart(0.0, 0.1)) - poly = PolyArea(cart(0, 0), cart(2, 0), cart(1, 1), cart(1, 0.5)) - @test intersection(quad, poly) |> type == Intersecting - @test all(vertices(quad ∩ poly) .≈ [cart(0, 0), cart(0.1, 0), cart(0.1, 0.05)]) - end +@testitem "Polygon intersection" setup = [Setup] begin + # triangle + poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + @test intersection(poly, other) |> type == Intersecting + @test all(vertices(poly ∩ other) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) + + # octagon + poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + @test intersection(poly, other) |> type == Intersecting + @test all( + vertices(poly ∩ other) .≈ + [cart(3, 4), cart(4, 3), cart(5, 3), cart(5, 2), cart(4, 1), cart(2, 1), cart(2, 0), cart(5, 0), cart(5, 4)] + ) + + # inside + poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + @test intersection(poly, other) |> type == Intersecting + @test all(vertices(poly ∩ other) .≈ vertices(poly)) + + # outside + poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) + other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) + @test intersection(poly, other) |> type == NotIntersecting + @test isnothing(poly ∩ other) + + # convex and non-convex polygons + quad = Quadrangle(cart(0, 0), cart(0.1, 0.0), cart(0.1, 0.1), cart(0.0, 0.1)) + poly = PolyArea(cart(0, 0), cart(2, 0), cart(1, 1), cart(1, 0.5)) + @test intersection(quad, poly) |> type == Intersecting + @test all(vertices(quad ∩ poly) .≈ [cart(0, 0), cart(0.1, 0), cart(0.1, 0.05)]) +end - @testset "Domains" begin - grid = cartgrid(4, 4) - pset = PointSet(centroid.(grid)) - ball = Ball(cart(0, 0), T(1)) - @test pset ∩ pset == pset - @test pset ∩ grid == grid ∩ pset == pset - @test pset ∩ ball == ball ∩ pset == PointSet(cart(0.5, 0.5)) - end +@testitem "Domain intersection" setup = [Setup] begin + grid = cartgrid(4, 4) + pset = PointSet(centroid.(grid)) + ball = Ball(cart(0, 0), T(1)) + @test pset ∩ pset == pset + @test pset ∩ grid == grid ∩ pset == pset + @test pset ∩ ball == ball ∩ pset == PointSet(cart(0.5, 0.5)) end diff --git a/test/matrices.jl b/test/matrices.jl index 38edbd31e..63be9fcf7 100644 --- a/test/matrices.jl +++ b/test/matrices.jl @@ -1,85 +1,83 @@ -@testset "Matrices" begin - @testset "Laplace" begin - # uniform weights for simple mesh - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - L = laplacematrix(mesh, kind=:uniform) - @test L == [ - -1 1/3 1/3 0 1/3 - 1/3 -1 0 1/3 1/3 - 1/3 0 -1 1/3 1/3 - 0 1/3 1/3 -1 1/3 - 1/4 1/4 1/4 1/4 -1 - ] +@testitem "Laplace matrix" setup = [Setup] begin + # uniform weights for simple mesh + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + L = laplacematrix(mesh, kind=:uniform) + @test L == [ + -1 1/3 1/3 0 1/3 + 1/3 -1 0 1/3 1/3 + 1/3 0 -1 1/3 1/3 + 0 1/3 1/3 -1 1/3 + 1/4 1/4 1/4 1/4 -1 + ] - # cotangent weights for simple mesh - L = laplacematrix(mesh, kind=:cotangent) - @test size(L) == (5, 5) + # cotangent weights for simple mesh + L = laplacematrix(mesh, kind=:cotangent) + @test size(L) == (5, 5) - # cotangent weights only defined for triangle meshes - points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - connec = connect.([(1, 2, 3, 4)], Quadrangle) - mesh = SimpleMesh(points, connec) - @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) + # cotangent weights only defined for triangle meshes + points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + connec = connect.([(1, 2, 3, 4)], Quadrangle) + mesh = SimpleMesh(points, connec) + @test_throws AssertionError laplacematrix(mesh, kind=:cotangent) - # uniform weights for Cartesian grid - grid = CartesianGrid(10, 10) - L = laplacematrix(grid, kind=:uniform) - @test size(L) == (11 * 11, 11 * 11) - grid = CartesianGrid(10, 10, 10) - L = laplacematrix(grid, kind=:uniform) - @test size(L) == (11 * 11 * 11, 11 * 11 * 11) - end + # uniform weights for Cartesian grid + grid = CartesianGrid(10, 10) + L = laplacematrix(grid, kind=:uniform) + @test size(L) == (11 * 11, 11 * 11) + grid = CartesianGrid(10, 10, 10) + L = laplacematrix(grid, kind=:uniform) + @test size(L) == (11 * 11 * 11, 11 * 11 * 11) +end - @testset "Measure" begin - # measure matrix of simple mesh - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - M = measurematrix(mesh) - @test size(M) == (5, 5) - @test isdiag(M) - end +@testitem "Measure matrix" setup = [Setup] begin + # measure matrix of simple mesh + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + M = measurematrix(mesh) + @test size(M) == (5, 5) + @test isdiag(M) +end - @testset "Adjacency" begin - # adjacency of CartesianGrid - grid = cartgrid(100, 100) - A = adjacencymatrix(grid) - d = sum(A, dims=2) - @test size(A) == (10000, 10000) - @test issymmetric(A) - @test issparse(A) - @test minimum(d) == 2 - @test maximum(d) == 4 - @test length(findall(==(2), d)) == 4 - A = adjacencymatrix(grid, rank=0) - @test size(A) == (101 * 101, 101 * 101) +@testitem "Adjacency matrix" setup = [Setup] begin + # adjacency of CartesianGrid + grid = cartgrid(100, 100) + A = adjacencymatrix(grid) + d = sum(A, dims=2) + @test size(A) == (10000, 10000) + @test issymmetric(A) + @test issparse(A) + @test minimum(d) == 2 + @test maximum(d) == 4 + @test length(findall(==(2), d)) == 4 + A = adjacencymatrix(grid, rank=0) + @test size(A) == (101 * 101, 101 * 101) - # adjacency of SimpleMesh - points = cart.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) - connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) - mesh = SimpleMesh(points, connec, relations=true) - A = adjacencymatrix(mesh) - @test A == [0 1; 1 0] - A = adjacencymatrix(mesh, rank=0) - @test A == [ - 0 1 1 0 0 - 1 0 1 1 0 - 1 1 0 0 1 - 0 1 0 0 1 - 0 0 1 1 0 - ] - end + # adjacency of SimpleMesh + points = cart.([(0, 0), (1, -1), (1, 1), (2, -1), (2, 1)]) + connec = connect.([(1, 2, 3), (3, 2, 4, 5)]) + mesh = SimpleMesh(points, connec, relations=true) + A = adjacencymatrix(mesh) + @test A == [0 1; 1 0] + A = adjacencymatrix(mesh, rank=0) + @test A == [ + 0 1 1 0 0 + 1 0 1 1 0 + 1 1 0 0 1 + 0 1 0 0 1 + 0 0 1 1 0 + ] +end - @testset "Miscellaneous" begin - # full Laplace-Beltrami operator - sphere = Sphere(cart(0, 0, 0)) - mesh = simplexify(sphere) - L = laplacematrix(mesh) - M = measurematrix(mesh) - @test issymmetric(L) - @test issparse(L) - @test isdiag(M) - end +@testitem "Misc matrix" setup = [Setup] begin + # full Laplace-Beltrami operator + sphere = Sphere(cart(0, 0, 0)) + mesh = simplexify(sphere) + L = laplacematrix(mesh) + M = measurematrix(mesh) + @test issymmetric(L) + @test issparse(L) + @test isdiag(M) end diff --git a/test/merging.jl b/test/merging.jl index 664d81235..b897a1404 100644 --- a/test/merging.jl +++ b/test/merging.jl @@ -1,4 +1,4 @@ -@testset "Merging" begin +@testitem "Merging" setup = [Setup] begin s = Sphere(cart(0, 0, 0), T(1)) c = CylinderSurface(T(1)) m = merge(s, c) diff --git a/test/meshes.jl b/test/meshes.jl index 70b7e1beb..af0143070 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -1,832 +1,830 @@ -@testset "Meshes" begin - @testset "CartesianGrid" begin - grid = cartgrid(100) - @test embeddim(grid) == 1 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (100,) - @test minimum(grid) == cart(0) - @test maximum(grid) == cart(100) - @test extrema(grid) == (cart(0), cart(100)) - @test spacing(grid) == (T(1) * u"m",) - @test nelements(grid) == 100 - @test eltype(grid) <: Segment - @test measure(grid) ≈ T(100) * u"m" - @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) - @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1] == Segment(cart(0), cart(1)) - @test grid[100] == Segment(cart(99), cart(100)) - - grid = cartgrid(200, 100) - @test embeddim(grid) == 2 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (200, 100) - @test minimum(grid) == cart(0, 0) - @test maximum(grid) == cart(200, 100) - @test extrema(grid) == (cart(0, 0), cart(200, 100)) - @test spacing(grid) == (T(1) * u"m", T(1) * u"m") - @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle - @test measure(grid) ≈ T(200 * 100) * u"m^2" - @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) - @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1, 1] == grid[1] - @test grid[200, 100] == grid[20000] - - grid = CartesianGrid((200, 100, 50), T.((0, 0, 0)), T.((1, 1, 1))) - @test embeddim(grid) == 3 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (200, 100, 50) - @test minimum(grid) == cart(0, 0, 0) - @test maximum(grid) == cart(200, 100, 50) - @test extrema(grid) == (cart(0, 0, 0), cart(200, 100, 50)) - @test spacing(grid) == (T(1) * u"m", T(1) * u"m", T(1) * u"m") - @test nelements(grid) == 200 * 100 * 50 - @test eltype(grid) <: Hexahedron - @test measure(grid) ≈ T(200 * 100 * 50) * u"m^3" - @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) - @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1, 1, 1] == grid[1] - @test grid[200, 100, 50] == grid[1000000] - - grid = CartesianGrid(T.((0, 0, 0)), T.((1, 1, 1)), T.((0.1, 0.1, 0.1))) - @test embeddim(grid) == 3 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (10, 10, 10) - @test minimum(grid) == cart(0, 0, 0) - @test maximum(grid) == cart(1, 1, 1) - @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m", T(0.1) * u"m") - - grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) - @test embeddim(grid) == 2 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (200, 100) - @test minimum(grid) == cart(-1.0, -1.0) - @test maximum(grid) == cart(1.0, 1.0) - @test spacing(grid) == (T(2 / 200) * u"m", T(2 / 100) * u"m") - @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle - - grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) - @test embeddim(grid) == 3 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (20, 10, 5) - @test minimum(grid) == cart(0, 0, 0) - @test maximum(grid) == cart(100, 50, 25) - @test extrema(grid) == (cart(0, 0, 0), cart(100, 50, 25)) - @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") - @test nelements(grid) == 20 * 10 * 5 - @test eltype(grid) <: Hexahedron - @test vertices(grid[1]) == ( - cart(0, 0, 0), - cart(5, 0, 0), - cart(5, 5, 0), - cart(0, 5, 0), - cart(0, 0, 5), - cart(5, 0, 5), - cart(5, 5, 5), - cart(0, 5, 5) - ) - @test all(centroid(grid, i) == centroid(grid[i]) for i in 1:nelements(grid)) - - # constructor with offset - grid = CartesianGrid((10, 10), T.((1.0, 1.0)), T.((1.0, 1.0)), (2, 2)) - @test embeddim(grid) == 2 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (10, 10) - @test minimum(grid) == cart(0.0, 0.0) - @test maximum(grid) == cart(10.0, 10.0) - @test spacing(grid) == (T(1) * u"m", T(1) * u"m") - @test nelements(grid) == 10 * 10 - @test eltype(grid) <: Quadrangle - - # mixed units - grid = CartesianGrid((10, 10), (T(0) * u"m", T(0) * u"cm"), (T(100) * u"cm", T(1) * u"m")) - @test unit(Meshes.lentype(grid)) == u"m" - grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), (T(100) * u"cm", T(1) * u"m")) - @test unit(Meshes.lentype(grid)) == u"m" - grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), dims=(10, 10)) - @test unit(Meshes.lentype(grid)) == u"m" - - # indexing into a subgrid - grid = cartgrid(10, 10) - sub = grid[1:2, 1:2] - @test size(sub) == (2, 2) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == minimum(grid) - @test maximum(sub) == cart(2, 2) - sub = grid[1:1, 2:3] - @test size(sub) == (1, 2) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == cart(0, 1) - @test maximum(sub) == cart(1, 3) - sub = grid[2:4, 3:7] - @test size(sub) == (3, 5) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == cart(1, 2) - @test maximum(sub) == cart(4, 7) - grid = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) - sub = grid[2:4, 3:7] - @test size(sub) == (3, 5) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == cart(2, 3) - @test maximum(sub) == cart(5, 8) - sub = grid[2, 3:7] - @test size(sub) == (1, 5) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == cart(2, 3) - @test maximum(sub) == cart(3, 8) - sub = grid[:, 3:7] - @test size(sub) == (10, 5) - @test spacing(sub) == spacing(grid) - @test minimum(sub) == cart(1, 3) - @test maximum(sub) == cart(11, 8) - @test_throws BoundsError grid[3:11, :] - - # subgrid with comparable vertices of grid - grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.2, 1.2))) - sub = grid[2:4, 5:7] - @test sub == CartesianGrid((3, 3), cart(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) - ind = reshape(reshape(1:121, 11, 11)[2:5, 5:8], :) - @test vertices(grid)[ind] == vertices(sub) - - # subgrid from Cartesian ranges - grid = cartgrid(10, 10) - sub1 = grid[1:2, 4:6] - sub2 = grid[CartesianIndex(1, 4):CartesianIndex(2, 6)] - @test sub1 == sub2 - - grid = cartgrid(200, 100) - @test centroid(grid, 1) == cart(0.5, 0.5) - @test centroid(grid, 2) == cart(1.5, 0.5) - @test centroid(grid, 200 * 100) == cart(199.5, 99.5) - @test nelements(grid) == 200 * 100 - @test eltype(grid) <: Quadrangle - @test grid[1] == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test grid[2] == Quadrangle(cart(1, 0), cart(2, 0), cart(2, 1), cart(1, 1)) - - # expand CartesianGrid with comparable vertices - grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.0, 1.0))) - left, right = (1, 1), (1, 1) - newdim = size(grid) .+ left .+ right - newoffset = offset(grid) .+ left - grid2 = CartesianGrid(newdim, minimum(grid), spacing(grid), newoffset) - @test issubset(vertices(grid), vertices(grid2)) - - # GridTopology from CartesianGrid - grid = cartgrid(5, 5) - topo = topology(grid) - vs = vertices(grid) - for i in 1:nelements(grid) - inds = indices(element(topo, i)) - @test vs[[inds...]] == pointify(element(grid, i)) - end - - # convert topology - grid = cartgrid(10, 10) - mesh = topoconvert(HalfEdgeTopology, grid) - @test mesh isa SimpleMesh - @test nvertices(mesh) == 121 - @test nelements(mesh) == 100 - @test eltype(mesh) <: Quadrangle - - # single vertex access - grid = cartgrid(10, 10) - @test vertex(grid, 1) == cart(0, 0) - @test vertex(grid, 121) == cart(10, 10) - - # xyz - g1D = cartgrid(10) - g2D = cartgrid(10, 10) - g3D = cartgrid(10, 10, 10) - @test Meshes.xyz(g1D) == (T.(0:10) * u"m",) - @test Meshes.xyz(g2D) == (T.(0:10) * u"m", T.(0:10) * u"m") - @test Meshes.xyz(g3D) == (T.(0:10) * u"m", T.(0:10) * u"m", T.(0:10) * u"m") - - # XYZ - g1D = cartgrid(10) - g2D = cartgrid(10, 10) - g3D = cartgrid(10, 10, 10) - x = T.(0:10) * u"m" - y = T.(0:10)' * u"m" - z = reshape(T.(0:10), 1, 1, 11) * u"m" - @test Meshes.XYZ(g1D) == (x,) - @test Meshes.XYZ(g2D) == (repeat(x, 1, 11), repeat(y, 11, 1)) - @test Meshes.XYZ(g3D) == (repeat(x, 1, 11, 11), repeat(y, 11, 1, 11), repeat(z, 11, 11, 1)) - - # units - grid = CartesianGrid((10, 10), cart(0, 0), (T(1) * u"m", T(1) * u"m")) - o = minimum(grid) - s = spacing(grid) - @test unit(Meshes.lentype(o)) == u"m" - @test Unitful.numtype(Meshes.lentype(o)) === T - @test unit(eltype(s)) == u"m" - @test Unitful.numtype(eltype(s)) === T - - grid = cartgrid(200, 100) - if T == Float32 - @test sprint(show, MIME"text/plain"(), grid) == """ - 200×100 CartesianGrid - ├─ minimum: Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ maximum: Point(x: 200.0f0 m, y: 100.0f0 m) - └─ spacing: (1.0f0 m, 1.0f0 m)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), grid) == """ - 200×100 CartesianGrid - ├─ minimum: Point(x: 0.0 m, y: 0.0 m) - ├─ maximum: Point(x: 200.0 m, y: 100.0 m) - └─ spacing: (1.0 m, 1.0 m)""" - end +@testitem "CartesianGrid" setup = [Setup] begin + grid = cartgrid(100) + @test embeddim(grid) == 1 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (100,) + @test minimum(grid) == cart(0) + @test maximum(grid) == cart(100) + @test extrema(grid) == (cart(0), cart(100)) + @test spacing(grid) == (T(1) * u"m",) + @test nelements(grid) == 100 + @test eltype(grid) <: Segment + @test measure(grid) ≈ T(100) * u"m" + @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) + @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) + @test grid[1] == Segment(cart(0), cart(1)) + @test grid[100] == Segment(cart(99), cart(100)) + + grid = cartgrid(200, 100) + @test embeddim(grid) == 2 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (200, 100) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(200, 100) + @test extrema(grid) == (cart(0, 0), cart(200, 100)) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m") + @test nelements(grid) == 200 * 100 + @test eltype(grid) <: Quadrangle + @test measure(grid) ≈ T(200 * 100) * u"m^2" + @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) + @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) + @test grid[1, 1] == grid[1] + @test grid[200, 100] == grid[20000] + + grid = CartesianGrid((200, 100, 50), T.((0, 0, 0)), T.((1, 1, 1))) + @test embeddim(grid) == 3 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (200, 100, 50) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(200, 100, 50) + @test extrema(grid) == (cart(0, 0, 0), cart(200, 100, 50)) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m", T(1) * u"m") + @test nelements(grid) == 200 * 100 * 50 + @test eltype(grid) <: Hexahedron + @test measure(grid) ≈ T(200 * 100 * 50) * u"m^3" + @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) + @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) + @test grid[1, 1, 1] == grid[1] + @test grid[200, 100, 50] == grid[1000000] + + grid = CartesianGrid(T.((0, 0, 0)), T.((1, 1, 1)), T.((0.1, 0.1, 0.1))) + @test embeddim(grid) == 3 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 10, 10) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(1, 1, 1) + @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m", T(0.1) * u"m") + + grid = CartesianGrid(T.((-1.0, -1.0)), T.((1.0, 1.0)), dims=(200, 100)) + @test embeddim(grid) == 2 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (200, 100) + @test minimum(grid) == cart(-1.0, -1.0) + @test maximum(grid) == cart(1.0, 1.0) + @test spacing(grid) == (T(2 / 200) * u"m", T(2 / 100) * u"m") + @test nelements(grid) == 200 * 100 + @test eltype(grid) <: Quadrangle + + grid = CartesianGrid((20, 10, 5), T.((0, 0, 0)), T.((5, 5, 5))) + @test embeddim(grid) == 3 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (20, 10, 5) + @test minimum(grid) == cart(0, 0, 0) + @test maximum(grid) == cart(100, 50, 25) + @test extrema(grid) == (cart(0, 0, 0), cart(100, 50, 25)) + @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") + @test nelements(grid) == 20 * 10 * 5 + @test eltype(grid) <: Hexahedron + @test vertices(grid[1]) == ( + cart(0, 0, 0), + cart(5, 0, 0), + cart(5, 5, 0), + cart(0, 5, 0), + cart(0, 0, 5), + cart(5, 0, 5), + cart(5, 5, 5), + cart(0, 5, 5) + ) + @test all(centroid(grid, i) == centroid(grid[i]) for i in 1:nelements(grid)) + + # constructor with offset + grid = CartesianGrid((10, 10), T.((1.0, 1.0)), T.((1.0, 1.0)), (2, 2)) + @test embeddim(grid) == 2 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 10) + @test minimum(grid) == cart(0.0, 0.0) + @test maximum(grid) == cart(10.0, 10.0) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m") + @test nelements(grid) == 10 * 10 + @test eltype(grid) <: Quadrangle + + # mixed units + grid = CartesianGrid((10, 10), (T(0) * u"m", T(0) * u"cm"), (T(100) * u"cm", T(1) * u"m")) + @test unit(Meshes.lentype(grid)) == u"m" + grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), (T(100) * u"cm", T(1) * u"m")) + @test unit(Meshes.lentype(grid)) == u"m" + grid = CartesianGrid((T(0) * u"cm", T(0) * u"m"), (T(10) * u"m", T(1000) * u"cm"), dims=(10, 10)) + @test unit(Meshes.lentype(grid)) == u"m" + + # indexing into a subgrid + grid = cartgrid(10, 10) + sub = grid[1:2, 1:2] + @test size(sub) == (2, 2) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == minimum(grid) + @test maximum(sub) == cart(2, 2) + sub = grid[1:1, 2:3] + @test size(sub) == (1, 2) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == cart(0, 1) + @test maximum(sub) == cart(1, 3) + sub = grid[2:4, 3:7] + @test size(sub) == (3, 5) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) + grid = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) + sub = grid[2:4, 3:7] + @test size(sub) == (3, 5) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == cart(2, 3) + @test maximum(sub) == cart(5, 8) + sub = grid[2, 3:7] + @test size(sub) == (1, 5) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == cart(2, 3) + @test maximum(sub) == cart(3, 8) + sub = grid[:, 3:7] + @test size(sub) == (10, 5) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == cart(1, 3) + @test maximum(sub) == cart(11, 8) + @test_throws BoundsError grid[3:11, :] + + # subgrid with comparable vertices of grid + grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.2, 1.2))) + sub = grid[2:4, 5:7] + @test sub == CartesianGrid((3, 3), cart(0.0, 0.0), T.((1.2, 1.2)), (0, -3)) + ind = reshape(reshape(1:121, 11, 11)[2:5, 5:8], :) + @test vertices(grid)[ind] == vertices(sub) + + # subgrid from Cartesian ranges + grid = cartgrid(10, 10) + sub1 = grid[1:2, 4:6] + sub2 = grid[CartesianIndex(1, 4):CartesianIndex(2, 6)] + @test sub1 == sub2 + + grid = cartgrid(200, 100) + @test centroid(grid, 1) == cart(0.5, 0.5) + @test centroid(grid, 2) == cart(1.5, 0.5) + @test centroid(grid, 200 * 100) == cart(199.5, 99.5) + @test nelements(grid) == 200 * 100 + @test eltype(grid) <: Quadrangle + @test grid[1] == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test grid[2] == Quadrangle(cart(1, 0), cart(2, 0), cart(2, 1), cart(1, 1)) + + # expand CartesianGrid with comparable vertices + grid = CartesianGrid((10, 10), cart(0.0, 0.0), T.((1.0, 1.0))) + left, right = (1, 1), (1, 1) + newdim = size(grid) .+ left .+ right + newoffset = offset(grid) .+ left + grid2 = CartesianGrid(newdim, minimum(grid), spacing(grid), newoffset) + @test issubset(vertices(grid), vertices(grid2)) + + # GridTopology from CartesianGrid + grid = cartgrid(5, 5) + topo = topology(grid) + vs = vertices(grid) + for i in 1:nelements(grid) + inds = indices(element(topo, i)) + @test vs[[inds...]] == pointify(element(grid, i)) end - @testset "RectilinearGrid" begin - x = range(zero(T), stop=one(T), length=6) - y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] - grid = RectilinearGrid(x, y) - @test embeddim(grid) == 2 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (5, 5) - @test minimum(grid) == cart(0, 0) - @test maximum(grid) == cart(1, 1) - @test extrema(grid) == (cart(0, 0), cart(1, 1)) - @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle - @test measure(grid) ≈ T(1) * u"m^2" - @test centroid(grid, 1) ≈ cart(0.1, 0.05) - @test centroid(grid[1]) ≈ cart(0.1, 0.05) - @test centroid(grid, 2) ≈ cart(0.3, 0.05) - @test centroid(grid[2]) ≈ cart(0.3, 0.05) - @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) - @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1, 1] == grid[1] - @test grid[5, 5] == grid[25] - sub = grid[2:4, 3:5] - @test size(sub) == (3, 3) - @test minimum(sub) == cart(0.2, 0.3) - @test maximum(sub) == cart(0.8, 1.0) - sub = grid[2, 3:5] - @test size(sub) == (1, 3) - @test minimum(sub) == cart(0.2, 0.3) - @test maximum(sub) == cart(0.4, 1.0) - sub = grid[:, 3:5] - @test size(sub) == (5, 3) - @test minimum(sub) == cart(0.0, 0.3) - @test maximum(sub) == cart(1.0, 1.0) - @test_throws BoundsError grid[2:6, :] - @test Meshes.xyz(grid) == (x * u"m", y * u"m") - @test Meshes.XYZ(grid) == (repeat(x, 1, 6) * u"m", repeat(y', 6, 1) * u"m") - - # single vertex access - grid = RectilinearGrid(T.(0:10), T.(0:10)) - @test vertex(grid, 1) == cart(0, 0) - @test vertex(grid, 121) == cart(10, 10) - - # constructor with datum & datum propagation - grid = RectilinearGrid{WGS84Latest}(x, y) - @test datum(crs(grid)) === WGS84Latest - @test datum(crs(centroid(grid))) === WGS84Latest - - # conversion - cg = cartgrid(10, 10) - rg = convert(RectilinearGrid, cg) - @test size(rg) == size(cg) - @test nvertices(rg) == nvertices(cg) - @test nelements(rg) == nelements(cg) - @test topology(rg) == topology(cg) - @test vertices(rg) == vertices(cg) - - cg = cartgrid(10, 20, 30) - rg = convert(RectilinearGrid, cg) - @test size(rg) == size(cg) - @test nvertices(rg) == nvertices(cg) - @test nelements(rg) == nelements(cg) - @test topology(rg) == topology(cg) - @test vertices(rg) == vertices(cg) - - x = range(zero(T), stop=one(T), length=6) - y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] - grid = RectilinearGrid(x, y) - @test sprint(show, grid) == "5×5 RectilinearGrid" - if T == Float32 - @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 RectilinearGrid - 36 vertices - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 0.2f0 m, y: 0.0f0 m) - ├─ Point(x: 0.4f0 m, y: 0.0f0 m) - ├─ Point(x: 0.6f0 m, y: 0.0f0 m) - ├─ Point(x: 0.8f0 m, y: 0.0f0 m) - ⋮ - ├─ Point(x: 0.2f0 m, y: 1.0f0 m) - ├─ Point(x: 0.4f0 m, y: 1.0f0 m) - ├─ Point(x: 0.6f0 m, y: 1.0f0 m) - ├─ Point(x: 0.8f0 m, y: 1.0f0 m) - └─ Point(x: 1.0f0 m, y: 1.0f0 m) - 25 elements - ├─ Quadrangle(1, 2, 8, 7) - ├─ Quadrangle(2, 3, 9, 8) - ├─ Quadrangle(3, 4, 10, 9) - ├─ Quadrangle(4, 5, 11, 10) - ├─ Quadrangle(5, 6, 12, 11) - ⋮ - ├─ Quadrangle(25, 26, 32, 31) - ├─ Quadrangle(26, 27, 33, 32) - ├─ Quadrangle(27, 28, 34, 33) - ├─ Quadrangle(28, 29, 35, 34) - └─ Quadrangle(29, 30, 36, 35)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 RectilinearGrid - 36 vertices - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 0.2 m, y: 0.0 m) - ├─ Point(x: 0.4 m, y: 0.0 m) - ├─ Point(x: 0.6 m, y: 0.0 m) - ├─ Point(x: 0.8 m, y: 0.0 m) - ⋮ - ├─ Point(x: 0.2 m, y: 1.0 m) - ├─ Point(x: 0.4 m, y: 1.0 m) - ├─ Point(x: 0.6 m, y: 1.0 m) - ├─ Point(x: 0.8 m, y: 1.0 m) - └─ Point(x: 1.0 m, y: 1.0 m) - 25 elements - ├─ Quadrangle(1, 2, 8, 7) - ├─ Quadrangle(2, 3, 9, 8) - ├─ Quadrangle(3, 4, 10, 9) - ├─ Quadrangle(4, 5, 11, 10) - ├─ Quadrangle(5, 6, 12, 11) - ⋮ - ├─ Quadrangle(25, 26, 32, 31) - ├─ Quadrangle(26, 27, 33, 32) - ├─ Quadrangle(27, 28, 34, 33) - ├─ Quadrangle(28, 29, 35, 34) - └─ Quadrangle(29, 30, 36, 35)""" - end + # convert topology + grid = cartgrid(10, 10) + mesh = topoconvert(HalfEdgeTopology, grid) + @test mesh isa SimpleMesh + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + + # single vertex access + grid = cartgrid(10, 10) + @test vertex(grid, 1) == cart(0, 0) + @test vertex(grid, 121) == cart(10, 10) + + # xyz + g1D = cartgrid(10) + g2D = cartgrid(10, 10) + g3D = cartgrid(10, 10, 10) + @test Meshes.xyz(g1D) == (T.(0:10) * u"m",) + @test Meshes.xyz(g2D) == (T.(0:10) * u"m", T.(0:10) * u"m") + @test Meshes.xyz(g3D) == (T.(0:10) * u"m", T.(0:10) * u"m", T.(0:10) * u"m") + + # XYZ + g1D = cartgrid(10) + g2D = cartgrid(10, 10) + g3D = cartgrid(10, 10, 10) + x = T.(0:10) * u"m" + y = T.(0:10)' * u"m" + z = reshape(T.(0:10), 1, 1, 11) * u"m" + @test Meshes.XYZ(g1D) == (x,) + @test Meshes.XYZ(g2D) == (repeat(x, 1, 11), repeat(y, 11, 1)) + @test Meshes.XYZ(g3D) == (repeat(x, 1, 11, 11), repeat(y, 11, 1, 11), repeat(z, 11, 11, 1)) + + # units + grid = CartesianGrid((10, 10), cart(0, 0), (T(1) * u"m", T(1) * u"m")) + o = minimum(grid) + s = spacing(grid) + @test unit(Meshes.lentype(o)) == u"m" + @test Unitful.numtype(Meshes.lentype(o)) === T + @test unit(eltype(s)) == u"m" + @test Unitful.numtype(eltype(s)) === T + + grid = cartgrid(200, 100) + if T == Float32 + @test sprint(show, MIME"text/plain"(), grid) == """ + 200×100 CartesianGrid + ├─ minimum: Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ maximum: Point(x: 200.0f0 m, y: 100.0f0 m) + └─ spacing: (1.0f0 m, 1.0f0 m)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), grid) == """ + 200×100 CartesianGrid + ├─ minimum: Point(x: 0.0 m, y: 0.0 m) + ├─ maximum: Point(x: 200.0 m, y: 100.0 m) + └─ spacing: (1.0 m, 1.0 m)""" end +end + +@testitem "RectilinearGrid" setup = [Setup] begin + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + grid = RectilinearGrid(x, y) + @test embeddim(grid) == 2 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (5, 5) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(1, 1) + @test extrema(grid) == (cart(0, 0), cart(1, 1)) + @test nelements(grid) == 25 + @test eltype(grid) <: Quadrangle + @test measure(grid) ≈ T(1) * u"m^2" + @test centroid(grid, 1) ≈ cart(0.1, 0.05) + @test centroid(grid[1]) ≈ cart(0.1, 0.05) + @test centroid(grid, 2) ≈ cart(0.3, 0.05) + @test centroid(grid[2]) ≈ cart(0.3, 0.05) + @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) + @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) + @test grid[1, 1] == grid[1] + @test grid[5, 5] == grid[25] + sub = grid[2:4, 3:5] + @test size(sub) == (3, 3) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.8, 1.0) + sub = grid[2, 3:5] + @test size(sub) == (1, 3) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.4, 1.0) + sub = grid[:, 3:5] + @test size(sub) == (5, 3) + @test minimum(sub) == cart(0.0, 0.3) + @test maximum(sub) == cart(1.0, 1.0) + @test_throws BoundsError grid[2:6, :] + @test Meshes.xyz(grid) == (x * u"m", y * u"m") + @test Meshes.XYZ(grid) == (repeat(x, 1, 6) * u"m", repeat(y', 6, 1) * u"m") + + # single vertex access + grid = RectilinearGrid(T.(0:10), T.(0:10)) + @test vertex(grid, 1) == cart(0, 0) + @test vertex(grid, 121) == cart(10, 10) + + # constructor with datum & datum propagation + grid = RectilinearGrid{WGS84Latest}(x, y) + @test datum(crs(grid)) === WGS84Latest + @test datum(crs(centroid(grid))) === WGS84Latest + + # conversion + cg = cartgrid(10, 10) + rg = convert(RectilinearGrid, cg) + @test size(rg) == size(cg) + @test nvertices(rg) == nvertices(cg) + @test nelements(rg) == nelements(cg) + @test topology(rg) == topology(cg) + @test vertices(rg) == vertices(cg) + + cg = cartgrid(10, 20, 30) + rg = convert(RectilinearGrid, cg) + @test size(rg) == size(cg) + @test nvertices(rg) == nvertices(cg) + @test nelements(rg) == nelements(cg) + @test topology(rg) == topology(cg) + @test vertices(rg) == vertices(cg) + + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + grid = RectilinearGrid(x, y) + @test sprint(show, grid) == "5×5 RectilinearGrid" + if T == Float32 + @test sprint(show, MIME"text/plain"(), grid) == """ + 5×5 RectilinearGrid + 36 vertices + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.2f0 m, y: 0.0f0 m) + ├─ Point(x: 0.4f0 m, y: 0.0f0 m) + ├─ Point(x: 0.6f0 m, y: 0.0f0 m) + ├─ Point(x: 0.8f0 m, y: 0.0f0 m) + ⋮ + ├─ Point(x: 0.2f0 m, y: 1.0f0 m) + ├─ Point(x: 0.4f0 m, y: 1.0f0 m) + ├─ Point(x: 0.6f0 m, y: 1.0f0 m) + ├─ Point(x: 0.8f0 m, y: 1.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m) + 25 elements + ├─ Quadrangle(1, 2, 8, 7) + ├─ Quadrangle(2, 3, 9, 8) + ├─ Quadrangle(3, 4, 10, 9) + ├─ Quadrangle(4, 5, 11, 10) + ├─ Quadrangle(5, 6, 12, 11) + ⋮ + ├─ Quadrangle(25, 26, 32, 31) + ├─ Quadrangle(26, 27, 33, 32) + ├─ Quadrangle(27, 28, 34, 33) + ├─ Quadrangle(28, 29, 35, 34) + └─ Quadrangle(29, 30, 36, 35)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), grid) == """ + 5×5 RectilinearGrid + 36 vertices + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 0.2 m, y: 0.0 m) + ├─ Point(x: 0.4 m, y: 0.0 m) + ├─ Point(x: 0.6 m, y: 0.0 m) + ├─ Point(x: 0.8 m, y: 0.0 m) + ⋮ + ├─ Point(x: 0.2 m, y: 1.0 m) + ├─ Point(x: 0.4 m, y: 1.0 m) + ├─ Point(x: 0.6 m, y: 1.0 m) + ├─ Point(x: 0.8 m, y: 1.0 m) + └─ Point(x: 1.0 m, y: 1.0 m) + 25 elements + ├─ Quadrangle(1, 2, 8, 7) + ├─ Quadrangle(2, 3, 9, 8) + ├─ Quadrangle(3, 4, 10, 9) + ├─ Quadrangle(4, 5, 11, 10) + ├─ Quadrangle(5, 6, 12, 11) + ⋮ + ├─ Quadrangle(25, 26, 32, 31) + ├─ Quadrangle(26, 27, 33, 32) + ├─ Quadrangle(27, 28, 34, 33) + ├─ Quadrangle(28, 29, 35, 34) + └─ Quadrangle(29, 30, 36, 35)""" + end +end - @testset "StructuredGrid" begin - X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) - Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) - grid = StructuredGrid(X, Y) - @test embeddim(grid) == 2 - @test crs(grid) <: Cartesian{NoDatum} - @test Meshes.lentype(grid) == ℳ - @test size(grid) == (5, 5) - @test minimum(grid) == cart(0, 0) - @test maximum(grid) == cart(1, 1) - @test extrema(grid) == (cart(0, 0), cart(1, 1)) - @test nelements(grid) == 25 - @test eltype(grid) <: Quadrangle - @test measure(grid) ≈ T(1) * u"m^2" - @test centroid(grid, 1) ≈ cart(0.1, 0.05) - @test centroid(grid[1]) ≈ cart(0.1, 0.05) - @test centroid(grid, 2) ≈ cart(0.3, 0.05) - @test centroid(grid[2]) ≈ cart(0.3, 0.05) - @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) - @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) - @test grid[1, 1] == grid[1] - @test grid[5, 5] == grid[25] - sub = grid[2:4, 3:5] - @test size(sub) == (3, 3) - @test minimum(sub) == cart(0.2, 0.3) - @test maximum(sub) == cart(0.8, 1.0) - sub = grid[2, 3:5] - @test size(sub) == (1, 3) - @test minimum(sub) == cart(0.2, 0.3) - @test maximum(sub) == cart(0.4, 1.0) - sub = grid[:, 3:5] - @test size(sub) == (5, 3) - @test minimum(sub) == cart(0.0, 0.3) - @test maximum(sub) == cart(1.0, 1.0) - @test_throws BoundsError grid[2:6, :] - @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") - - # constructor with datum - grid = StructuredGrid{WGS84Latest}(X, Y) - @test datum(crs(grid)) === WGS84Latest - - # conversion - cg = cartgrid(10, 10) - sg = convert(StructuredGrid, cg) - @test size(sg) == size(cg) - @test nvertices(sg) == nvertices(cg) - @test nelements(sg) == nelements(cg) - @test topology(sg) == topology(cg) - @test vertices(sg) == vertices(cg) - - cg = cartgrid(10, 20, 30) - sg = convert(StructuredGrid, cg) - @test size(sg) == size(cg) - @test nvertices(sg) == nvertices(cg) - @test nelements(sg) == nelements(cg) - @test topology(sg) == topology(cg) - @test vertices(sg) == vertices(cg) - - rg = RectilinearGrid(T.(0:10), T.(0:10)) - sg = convert(StructuredGrid, rg) - @test size(sg) == size(rg) - @test nvertices(sg) == nvertices(rg) - @test nelements(sg) == nelements(rg) - @test topology(sg) == topology(rg) - @test vertices(sg) == vertices(rg) - - rg = RectilinearGrid(T.(0:10), T.(0:20), T.(0:30)) - sg = convert(StructuredGrid, rg) - @test size(sg) == size(rg) - @test nvertices(sg) == nvertices(rg) - @test nelements(sg) == nelements(rg) - @test topology(sg) == topology(rg) - @test vertices(sg) == vertices(rg) - - X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) - Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) - grid = StructuredGrid(X, Y) - @test sprint(show, grid) == "5×5 StructuredGrid" - if T == Float32 - @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 StructuredGrid - 36 vertices - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 0.2f0 m, y: 0.0f0 m) - ├─ Point(x: 0.4f0 m, y: 0.0f0 m) - ├─ Point(x: 0.6f0 m, y: 0.0f0 m) - ├─ Point(x: 0.8f0 m, y: 0.0f0 m) - ⋮ - ├─ Point(x: 0.2f0 m, y: 1.0f0 m) - ├─ Point(x: 0.4f0 m, y: 1.0f0 m) - ├─ Point(x: 0.6f0 m, y: 1.0f0 m) - ├─ Point(x: 0.8f0 m, y: 1.0f0 m) - └─ Point(x: 1.0f0 m, y: 1.0f0 m) - 25 elements - ├─ Quadrangle(1, 2, 8, 7) - ├─ Quadrangle(2, 3, 9, 8) - ├─ Quadrangle(3, 4, 10, 9) - ├─ Quadrangle(4, 5, 11, 10) - ├─ Quadrangle(5, 6, 12, 11) - ⋮ - ├─ Quadrangle(25, 26, 32, 31) - ├─ Quadrangle(26, 27, 33, 32) - ├─ Quadrangle(27, 28, 34, 33) - ├─ Quadrangle(28, 29, 35, 34) - └─ Quadrangle(29, 30, 36, 35)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), grid) == """ - 5×5 StructuredGrid - 36 vertices - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 0.2 m, y: 0.0 m) - ├─ Point(x: 0.4 m, y: 0.0 m) - ├─ Point(x: 0.6 m, y: 0.0 m) - ├─ Point(x: 0.8 m, y: 0.0 m) - ⋮ - ├─ Point(x: 0.2 m, y: 1.0 m) - ├─ Point(x: 0.4 m, y: 1.0 m) - ├─ Point(x: 0.6 m, y: 1.0 m) - ├─ Point(x: 0.8 m, y: 1.0 m) - └─ Point(x: 1.0 m, y: 1.0 m) - 25 elements - ├─ Quadrangle(1, 2, 8, 7) - ├─ Quadrangle(2, 3, 9, 8) - ├─ Quadrangle(3, 4, 10, 9) - ├─ Quadrangle(4, 5, 11, 10) - ├─ Quadrangle(5, 6, 12, 11) - ⋮ - ├─ Quadrangle(25, 26, 32, 31) - ├─ Quadrangle(26, 27, 33, 32) - ├─ Quadrangle(27, 28, 34, 33) - ├─ Quadrangle(28, 29, 35, 34) - └─ Quadrangle(29, 30, 36, 35)""" - end +@testitem "StructuredGrid" setup = [Setup] begin + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) + grid = StructuredGrid(X, Y) + @test embeddim(grid) == 2 + @test crs(grid) <: Cartesian{NoDatum} + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (5, 5) + @test minimum(grid) == cart(0, 0) + @test maximum(grid) == cart(1, 1) + @test extrema(grid) == (cart(0, 0), cart(1, 1)) + @test nelements(grid) == 25 + @test eltype(grid) <: Quadrangle + @test measure(grid) ≈ T(1) * u"m^2" + @test centroid(grid, 1) ≈ cart(0.1, 0.05) + @test centroid(grid[1]) ≈ cart(0.1, 0.05) + @test centroid(grid, 2) ≈ cart(0.3, 0.05) + @test centroid(grid[2]) ≈ cart(0.3, 0.05) + @test vertex(grid, 1) == vertex(grid, ntuple(i -> 1, embeddim(grid))) + @test vertex(grid, nvertices(grid)) == vertex(grid, size(grid) .+ 1) + @test grid[1, 1] == grid[1] + @test grid[5, 5] == grid[25] + sub = grid[2:4, 3:5] + @test size(sub) == (3, 3) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.8, 1.0) + sub = grid[2, 3:5] + @test size(sub) == (1, 3) + @test minimum(sub) == cart(0.2, 0.3) + @test maximum(sub) == cart(0.4, 1.0) + sub = grid[:, 3:5] + @test size(sub) == (5, 3) + @test minimum(sub) == cart(0.0, 0.3) + @test maximum(sub) == cart(1.0, 1.0) + @test_throws BoundsError grid[2:6, :] + @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") + + # constructor with datum + grid = StructuredGrid{WGS84Latest}(X, Y) + @test datum(crs(grid)) === WGS84Latest + + # conversion + cg = cartgrid(10, 10) + sg = convert(StructuredGrid, cg) + @test size(sg) == size(cg) + @test nvertices(sg) == nvertices(cg) + @test nelements(sg) == nelements(cg) + @test topology(sg) == topology(cg) + @test vertices(sg) == vertices(cg) + + cg = cartgrid(10, 20, 30) + sg = convert(StructuredGrid, cg) + @test size(sg) == size(cg) + @test nvertices(sg) == nvertices(cg) + @test nelements(sg) == nelements(cg) + @test topology(sg) == topology(cg) + @test vertices(sg) == vertices(cg) + + rg = RectilinearGrid(T.(0:10), T.(0:10)) + sg = convert(StructuredGrid, rg) + @test size(sg) == size(rg) + @test nvertices(sg) == nvertices(rg) + @test nelements(sg) == nelements(rg) + @test topology(sg) == topology(rg) + @test vertices(sg) == vertices(rg) + + rg = RectilinearGrid(T.(0:10), T.(0:20), T.(0:30)) + sg = convert(StructuredGrid, rg) + @test size(sg) == size(rg) + @test nvertices(sg) == nvertices(rg) + @test nelements(sg) == nelements(rg) + @test topology(sg) == topology(rg) + @test vertices(sg) == vertices(rg) + + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) + grid = StructuredGrid(X, Y) + @test sprint(show, grid) == "5×5 StructuredGrid" + if T == Float32 + @test sprint(show, MIME"text/plain"(), grid) == """ + 5×5 StructuredGrid + 36 vertices + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.2f0 m, y: 0.0f0 m) + ├─ Point(x: 0.4f0 m, y: 0.0f0 m) + ├─ Point(x: 0.6f0 m, y: 0.0f0 m) + ├─ Point(x: 0.8f0 m, y: 0.0f0 m) + ⋮ + ├─ Point(x: 0.2f0 m, y: 1.0f0 m) + ├─ Point(x: 0.4f0 m, y: 1.0f0 m) + ├─ Point(x: 0.6f0 m, y: 1.0f0 m) + ├─ Point(x: 0.8f0 m, y: 1.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m) + 25 elements + ├─ Quadrangle(1, 2, 8, 7) + ├─ Quadrangle(2, 3, 9, 8) + ├─ Quadrangle(3, 4, 10, 9) + ├─ Quadrangle(4, 5, 11, 10) + ├─ Quadrangle(5, 6, 12, 11) + ⋮ + ├─ Quadrangle(25, 26, 32, 31) + ├─ Quadrangle(26, 27, 33, 32) + ├─ Quadrangle(27, 28, 34, 33) + ├─ Quadrangle(28, 29, 35, 34) + └─ Quadrangle(29, 30, 36, 35)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), grid) == """ + 5×5 StructuredGrid + 36 vertices + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 0.2 m, y: 0.0 m) + ├─ Point(x: 0.4 m, y: 0.0 m) + ├─ Point(x: 0.6 m, y: 0.0 m) + ├─ Point(x: 0.8 m, y: 0.0 m) + ⋮ + ├─ Point(x: 0.2 m, y: 1.0 m) + ├─ Point(x: 0.4 m, y: 1.0 m) + ├─ Point(x: 0.6 m, y: 1.0 m) + ├─ Point(x: 0.8 m, y: 1.0 m) + └─ Point(x: 1.0 m, y: 1.0 m) + 25 elements + ├─ Quadrangle(1, 2, 8, 7) + ├─ Quadrangle(2, 3, 9, 8) + ├─ Quadrangle(3, 4, 10, 9) + ├─ Quadrangle(4, 5, 11, 10) + ├─ Quadrangle(5, 6, 12, 11) + ⋮ + ├─ Quadrangle(25, 26, 32, 31) + ├─ Quadrangle(26, 27, 33, 32) + ├─ Quadrangle(27, 28, 34, 33) + ├─ Quadrangle(28, 29, 35, 34) + └─ Quadrangle(29, 30, 36, 35)""" end +end - @testset "SimpleMesh" begin - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - triangles = - Triangle.([ - (cart(0.0, 0.0), cart(1.0, 0.0), cart(0.5, 0.5)), - (cart(1.0, 0.0), cart(1.0, 1.0), cart(0.5, 0.5)), - (cart(1.0, 1.0), cart(0.0, 1.0), cart(0.5, 0.5)), - (cart(0.0, 1.0), cart(0.0, 0.0), cart(0.5, 0.5)) - ]) - @test crs(mesh) <: Cartesian{NoDatum} - @test Meshes.lentype(mesh) == ℳ - @test vertices(mesh) == points - @test collect(faces(mesh, 2)) == triangles - @test collect(elements(mesh)) == triangles - @test nelements(mesh) == 4 - for i in 1:length(triangles) - @test mesh[i] == triangles[i] - end - @test eltype(mesh) <: Triangle - @test measure(mesh) ≈ T(1) * u"m^2" - @test area(mesh) ≈ T(1) * u"m^2" - @test extrema(mesh) == (cart(0, 0), cart(1, 1)) - - # test constructors - coords = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(coords, SimpleTopology(connec)) - @test eltype(mesh) <: Triangle - @test topology(mesh) isa SimpleTopology - @test nvertices(mesh) == 5 - @test nelements(mesh) == 4 - mesh = SimpleMesh(coords, connec) - @test eltype(mesh) <: Triangle - @test topology(mesh) isa SimpleTopology - @test nvertices(mesh) == 5 - @test nelements(mesh) == 4 - mesh = SimpleMesh(coords, connec, relations=true) - @test eltype(mesh) <: Triangle - @test topology(mesh) isa HalfEdgeTopology - @test nvertices(mesh) == 5 - @test nelements(mesh) == 4 - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) - Δs = connect.([(3, 1, 5), (4, 6, 2)], Triangle) - □s = connect.([(1, 2, 6, 5), (5, 6, 4, 3)], Quadrangle) - mesh = SimpleMesh(points, [Δs; □s]) - elms = [ - Triangle(cart(0.0, 1.0), cart(0.0, 0.0), cart(0.25, 0.5)), - Triangle(cart(1.0, 1.0), cart(0.75, 0.5), cart(1.0, 0.0)), - Quadrangle(cart(0.0, 0.0), cart(1.0, 0.0), cart(0.75, 0.5), cart(0.25, 0.5)), - Quadrangle(cart(0.25, 0.5), cart(0.75, 0.5), cart(1.0, 1.0), cart(0.0, 1.0)) - ] - @test collect(elements(mesh)) == elms - @test nelements(mesh) == 4 - for i in 1:length(elms) - @test mesh[i] == elms[i] - end - @test eltype(mesh) <: Polygon - - # test for https://github.com/JuliaGeometry/Meshes.jl/issues/177 - points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) - connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) - mesh = SimpleMesh(points, connec) - topo = topology(mesh) - @test collect(faces(topo, 2)) == [connect((3, 4, 1), Triangle)] - @test collect(faces(topo, 3)) == [connect((1, 2, 3, 4), Tetrahedron)] - - # test for https://github.com/JuliaGeometry/Meshes.jl/issues/187 - points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) - connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) - mesh = SimpleMesh(points[4:-1:1], connec) - meshvp = SimpleMesh(view(points, 4:-1:1), connec) - @test mesh == meshvp - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - bytes = @allocated faces(mesh, 2) - @test bytes < 100 - cells = faces(mesh, 2) - bytes = @allocated collect(cells) - @test bytes < 800 - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - @test centroid(mesh, 1) == centroid(Triangle(cart(0, 0), cart(1, 0), cart(0.5, 0.5))) - @test centroid(mesh, 2) == centroid(Triangle(cart(1, 0), cart(1, 1), cart(0.5, 0.5))) - @test centroid(mesh, 3) == centroid(Triangle(cart(1, 1), cart(0, 1), cart(0.5, 0.5))) - @test centroid(mesh, 4) == centroid(Triangle(cart(0, 1), cart(0, 0), cart(0.5, 0.5))) - - # merge operation with 2D geometries - mesh₁ = SimpleMesh(cart.([(0, 0), (1, 0), (0, 1)]), connect.([(1, 2, 3)])) - mesh₂ = SimpleMesh(cart.([(1, 0), (1, 1), (0, 1)]), connect.([(1, 2, 3)])) - mesh = merge(mesh₁, mesh₂) - @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] - @test collect(elements(topology(mesh))) == connect.([(1, 2, 3), (4, 5, 6)]) - - # merge operation with 3D geometries - mesh₁ = SimpleMesh(cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) - mesh₂ = SimpleMesh(cart.([(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) - mesh = merge(mesh₁, mesh₂) - @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] - @test collect(elements(topology(mesh))) == connect.([(1, 2, 3, 4), (5, 6, 7, 8)], Tetrahedron) - - # convert any mesh to SimpleMesh - grid = cartgrid(10, 10) - mesh = convert(SimpleMesh, grid) - @test mesh isa SimpleMesh - @test topology(mesh) == GridTopology(10, 10) - @test nvertices(mesh) == 121 - @test nelements(mesh) == 100 - @test eltype(mesh) <: Quadrangle - # grid interface - @test size(mesh) == (10, 10) - @test minimum(mesh) == cart(0, 0) - @test maximum(mesh) == cart(10, 10) - @test extrema(mesh) == (cart(0, 0), cart(10, 10)) - @test vertex(mesh, 1) == vertex(mesh, ntuple(i -> 1, embeddim(mesh))) - @test vertex(mesh, nvertices(mesh)) == vertex(mesh, size(mesh) .+ 1) - @test mesh[1, 1] == mesh[1] - @test mesh[10, 10] == mesh[100] - sub = mesh[2:4, 3:7] - @test size(sub) == (3, 5) - @test minimum(sub) == cart(1, 2) - @test maximum(sub) == cart(4, 7) - sub = mesh[2, 3:7] - @test size(sub) == (1, 5) - @test minimum(sub) == cart(1, 2) - @test maximum(sub) == cart(2, 7) - sub = mesh[:, 3:7] - @test size(sub) == (10, 5) - @test minimum(sub) == cart(0, 2) - @test maximum(sub) == cart(10, 7) - @test_throws BoundsError grid[3:11, :] - - # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 - points = randpoint2(5) - connec = [connect((1, 2, 3))] - mesh = SimpleMesh(points, connec) - @test nvertices(mesh) == length(vertices(mesh)) == 5 - - # single vertex access - points = randpoint2(5) - connec = [connect((1, 2, 3))] - mesh = SimpleMesh(points, connec) - @test vertex(mesh, 1) == points[1] - @test vertex(mesh, 2) == points[2] - @test vertex(mesh, 3) == points[3] - @test vertex(mesh, 4) == points[4] - @test vertex(mesh, 5) == points[5] - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - @test sprint(show, mesh) == "4 SimpleMesh" - if T == Float32 - @test sprint(show, MIME"text/plain"(), mesh) == """ - 4 SimpleMesh - 5 vertices - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 1.0f0 m) - ├─ Point(x: 1.0f0 m, y: 1.0f0 m) - └─ Point(x: 0.5f0 m, y: 0.5f0 m) - 4 elements - ├─ Triangle(1, 2, 5) - ├─ Triangle(2, 4, 5) - ├─ Triangle(4, 3, 5) - └─ Triangle(3, 1, 5)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), mesh) == """ - 4 SimpleMesh - 5 vertices - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m) - ├─ Point(x: 0.0 m, y: 1.0 m) - ├─ Point(x: 1.0 m, y: 1.0 m) - └─ Point(x: 0.5 m, y: 0.5 m) - 4 elements - ├─ Triangle(1, 2, 5) - ├─ Triangle(2, 4, 5) - ├─ Triangle(4, 3, 5) - └─ Triangle(3, 1, 5)""" - end +@testitem "SimpleMesh" setup = [Setup] begin + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + triangles = + Triangle.([ + (cart(0.0, 0.0), cart(1.0, 0.0), cart(0.5, 0.5)), + (cart(1.0, 0.0), cart(1.0, 1.0), cart(0.5, 0.5)), + (cart(1.0, 1.0), cart(0.0, 1.0), cart(0.5, 0.5)), + (cart(0.0, 1.0), cart(0.0, 0.0), cart(0.5, 0.5)) + ]) + @test crs(mesh) <: Cartesian{NoDatum} + @test Meshes.lentype(mesh) == ℳ + @test vertices(mesh) == points + @test collect(faces(mesh, 2)) == triangles + @test collect(elements(mesh)) == triangles + @test nelements(mesh) == 4 + for i in 1:length(triangles) + @test mesh[i] == triangles[i] + end + @test eltype(mesh) <: Triangle + @test measure(mesh) ≈ T(1) * u"m^2" + @test area(mesh) ≈ T(1) * u"m^2" + @test extrema(mesh) == (cart(0, 0), cart(1, 1)) + + # test constructors + coords = [T.((0, 0)), T.((1, 0)), T.((0, 1)), T.((1, 1)), T.((0.5, 0.5))] + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(coords, SimpleTopology(connec)) + @test eltype(mesh) <: Triangle + @test topology(mesh) isa SimpleTopology + @test nvertices(mesh) == 5 + @test nelements(mesh) == 4 + mesh = SimpleMesh(coords, connec) + @test eltype(mesh) <: Triangle + @test topology(mesh) isa SimpleTopology + @test nvertices(mesh) == 5 + @test nelements(mesh) == 4 + mesh = SimpleMesh(coords, connec, relations=true) + @test eltype(mesh) <: Triangle + @test topology(mesh) isa HalfEdgeTopology + @test nvertices(mesh) == 5 + @test nelements(mesh) == 4 + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + Δs = connect.([(3, 1, 5), (4, 6, 2)], Triangle) + □s = connect.([(1, 2, 6, 5), (5, 6, 4, 3)], Quadrangle) + mesh = SimpleMesh(points, [Δs; □s]) + elms = [ + Triangle(cart(0.0, 1.0), cart(0.0, 0.0), cart(0.25, 0.5)), + Triangle(cart(1.0, 1.0), cart(0.75, 0.5), cart(1.0, 0.0)), + Quadrangle(cart(0.0, 0.0), cart(1.0, 0.0), cart(0.75, 0.5), cart(0.25, 0.5)), + Quadrangle(cart(0.25, 0.5), cart(0.75, 0.5), cart(1.0, 1.0), cart(0.0, 1.0)) + ] + @test collect(elements(mesh)) == elms + @test nelements(mesh) == 4 + for i in 1:length(elms) + @test mesh[i] == elms[i] end + @test eltype(mesh) <: Polygon + + # test for https://github.com/JuliaGeometry/Meshes.jl/issues/177 + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) + connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) + mesh = SimpleMesh(points, connec) + topo = topology(mesh) + @test collect(faces(topo, 2)) == [connect((3, 4, 1), Triangle)] + @test collect(faces(topo, 3)) == [connect((1, 2, 3, 4), Tetrahedron)] + + # test for https://github.com/JuliaGeometry/Meshes.jl/issues/187 + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 1), (0, 1, 0)]) + connec = connect.([(1, 2, 3, 4), (3, 4, 1)], [Tetrahedron, Triangle]) + mesh = SimpleMesh(points[4:-1:1], connec) + meshvp = SimpleMesh(view(points, 4:-1:1), connec) + @test mesh == meshvp + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + bytes = @allocated faces(mesh, 2) + @test bytes < 100 + cells = faces(mesh, 2) + bytes = @allocated collect(cells) + @test bytes < 800 + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + @test centroid(mesh, 1) == centroid(Triangle(cart(0, 0), cart(1, 0), cart(0.5, 0.5))) + @test centroid(mesh, 2) == centroid(Triangle(cart(1, 0), cart(1, 1), cart(0.5, 0.5))) + @test centroid(mesh, 3) == centroid(Triangle(cart(1, 1), cart(0, 1), cart(0.5, 0.5))) + @test centroid(mesh, 4) == centroid(Triangle(cart(0, 1), cart(0, 0), cart(0.5, 0.5))) + + # merge operation with 2D geometries + mesh₁ = SimpleMesh(cart.([(0, 0), (1, 0), (0, 1)]), connect.([(1, 2, 3)])) + mesh₂ = SimpleMesh(cart.([(1, 0), (1, 1), (0, 1)]), connect.([(1, 2, 3)])) + mesh = merge(mesh₁, mesh₂) + @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] + @test collect(elements(topology(mesh))) == connect.([(1, 2, 3), (4, 5, 6)]) + + # merge operation with 3D geometries + mesh₁ = SimpleMesh(cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh₂ = SimpleMesh(cart.([(1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1)]), connect.([(1, 2, 3, 4)], Tetrahedron)) + mesh = merge(mesh₁, mesh₂) + @test vertices(mesh) == [vertices(mesh₁); vertices(mesh₂)] + @test collect(elements(topology(mesh))) == connect.([(1, 2, 3, 4), (5, 6, 7, 8)], Tetrahedron) + + # convert any mesh to SimpleMesh + grid = cartgrid(10, 10) + mesh = convert(SimpleMesh, grid) + @test mesh isa SimpleMesh + @test topology(mesh) == GridTopology(10, 10) + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + # grid interface + @test size(mesh) == (10, 10) + @test minimum(mesh) == cart(0, 0) + @test maximum(mesh) == cart(10, 10) + @test extrema(mesh) == (cart(0, 0), cart(10, 10)) + @test vertex(mesh, 1) == vertex(mesh, ntuple(i -> 1, embeddim(mesh))) + @test vertex(mesh, nvertices(mesh)) == vertex(mesh, size(mesh) .+ 1) + @test mesh[1, 1] == mesh[1] + @test mesh[10, 10] == mesh[100] + sub = mesh[2:4, 3:7] + @test size(sub) == (3, 5) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) + sub = mesh[2, 3:7] + @test size(sub) == (1, 5) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(2, 7) + sub = mesh[:, 3:7] + @test size(sub) == (10, 5) + @test minimum(sub) == cart(0, 2) + @test maximum(sub) == cart(10, 7) + @test_throws BoundsError grid[3:11, :] + + # test for https://github.com/JuliaGeometry/Meshes.jl/issues/261 + points = randpoint2(5) + connec = [connect((1, 2, 3))] + mesh = SimpleMesh(points, connec) + @test nvertices(mesh) == length(vertices(mesh)) == 5 + + # single vertex access + points = randpoint2(5) + connec = [connect((1, 2, 3))] + mesh = SimpleMesh(points, connec) + @test vertex(mesh, 1) == points[1] + @test vertex(mesh, 2) == points[2] + @test vertex(mesh, 3) == points[3] + @test vertex(mesh, 4) == points[4] + @test vertex(mesh, 5) == points[5] + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + @test sprint(show, mesh) == "4 SimpleMesh" + if T == Float32 + @test sprint(show, MIME"text/plain"(), mesh) == """ + 4 SimpleMesh + 5 vertices + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + └─ Point(x: 0.5f0 m, y: 0.5f0 m) + 4 elements + ├─ Triangle(1, 2, 5) + ├─ Triangle(2, 4, 5) + ├─ Triangle(4, 3, 5) + └─ Triangle(3, 1, 5)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), mesh) == """ + 4 SimpleMesh + 5 vertices + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m) + └─ Point(x: 0.5 m, y: 0.5 m) + 4 elements + ├─ Triangle(1, 2, 5) + ├─ Triangle(2, 4, 5) + ├─ Triangle(4, 3, 5) + └─ Triangle(3, 1, 5)""" + end +end - @testset "TransformedMesh" begin - grid = cartgrid(10, 10) - rgrid = convert(RectilinearGrid, grid) - sgrid = convert(StructuredGrid, grid) - mesh = convert(SimpleMesh, grid) - trans = Identity() - tmesh = TransformedMesh(mesh, trans) - @test crs(tmesh) <: Cartesian{NoDatum} - @test Meshes.lentype(tmesh) == ℳ - @test parent(tmesh) === mesh - @test Meshes.transform(tmesh) === trans - @test TransformedMesh(grid, trans) == grid - @test TransformedMesh(rgrid, trans) == rgrid - @test TransformedMesh(sgrid, trans) == sgrid - @test TransformedMesh(mesh, trans) == mesh - trans = Translate(T(10), T(10)) → Translate(T(-10), T(-10)) - @test TransformedMesh(grid, trans) == grid - @test TransformedMesh(rgrid, trans) == rgrid - @test TransformedMesh(sgrid, trans) == sgrid - @test TransformedMesh(mesh, trans) == mesh - trans1 = Translate(T(10), T(10)) - trans2 = Translate(T(-10), T(-10)) - @test TransformedMesh(TransformedMesh(grid, trans1), trans2) == TransformedMesh(grid, trans1 → trans2) - - # transforms that change the Manifold and/or CRS - points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - mesh = SimpleMesh(points, connec) - trans = Proj(Cartesian) - tmesh = TransformedMesh(mesh, trans) - @test manifold(tmesh) === 🌐 - @test crs(tmesh) <: Cartesian - trans = Proj(Polar) - tgrid = TransformedMesh(grid, trans) - @test tgrid isa TransformedGrid - @test manifold(tgrid) === 𝔼{2} - @test crs(tgrid) <: Polar - - # grid interface - trans = Identity() - tgrid = TransformedMesh(grid, trans) - @test tgrid isa TransformedGrid - @test size(tgrid) == (10, 10) - @test minimum(tgrid) == cart(0, 0) - @test maximum(tgrid) == cart(10, 10) - @test extrema(tgrid) == (cart(0, 0), cart(10, 10)) - @test vertex(tgrid, 1) == vertex(tgrid, ntuple(i -> 1, embeddim(tgrid))) - @test vertex(tgrid, nvertices(tgrid)) == vertex(tgrid, size(tgrid) .+ 1) - @test tgrid[1, 1] == tgrid[1] - @test tgrid[10, 10] == tgrid[100] - sub = tgrid[2:4, 3:7] - @test size(sub) == (3, 5) - @test minimum(sub) == cart(1, 2) - @test maximum(sub) == cart(4, 7) - sub = tgrid[2, 3:7] - @test size(sub) == (1, 5) - @test minimum(sub) == cart(1, 2) - @test maximum(sub) == cart(2, 7) - sub = tgrid[:, 3:7] - @test size(sub) == (10, 5) - @test minimum(sub) == cart(0, 2) - @test maximum(sub) == cart(10, 7) - - # optimization of centroid - trans = Rotate(T(π / 4)) - cgrid = cartgrid(10, 10) - tmesh = TransformedMesh(cgrid, trans) - centr = centroid(tmesh, 1) - @test @allocated(centroid(tmesh, 1)) < 50 - - # optimization of == - trans = Rotate(T(π / 4)) - cgrid = cartgrid(1000, 1000) - tmesh = TransformedMesh(cgrid, trans) - @test tmesh == tmesh - - @test sprint(show, tgrid) == "10×10 TransformedGrid" - if T == Float32 - @test sprint(show, MIME"text/plain"(), tgrid) == """ - 10×10 TransformedGrid - 121 vertices - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m) - ├─ Point(x: 2.0f0 m, y: 0.0f0 m) - ├─ Point(x: 3.0f0 m, y: 0.0f0 m) - ├─ Point(x: 4.0f0 m, y: 0.0f0 m) - ⋮ - ├─ Point(x: 6.0f0 m, y: 10.0f0 m) - ├─ Point(x: 7.0f0 m, y: 10.0f0 m) - ├─ Point(x: 8.0f0 m, y: 10.0f0 m) - ├─ Point(x: 9.0f0 m, y: 10.0f0 m) - └─ Point(x: 10.0f0 m, y: 10.0f0 m) - 100 elements - ├─ Quadrangle(1, 2, 13, 12) - ├─ Quadrangle(2, 3, 14, 13) - ├─ Quadrangle(3, 4, 15, 14) - ├─ Quadrangle(4, 5, 16, 15) - ├─ Quadrangle(5, 6, 17, 16) - ⋮ - ├─ Quadrangle(105, 106, 117, 116) - ├─ Quadrangle(106, 107, 118, 117) - ├─ Quadrangle(107, 108, 119, 118) - ├─ Quadrangle(108, 109, 120, 119) - └─ Quadrangle(109, 110, 121, 120)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), tgrid) == """ - 10×10 TransformedGrid - 121 vertices - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m) - ├─ Point(x: 2.0 m, y: 0.0 m) - ├─ Point(x: 3.0 m, y: 0.0 m) - ├─ Point(x: 4.0 m, y: 0.0 m) - ⋮ - ├─ Point(x: 6.0 m, y: 10.0 m) - ├─ Point(x: 7.0 m, y: 10.0 m) - ├─ Point(x: 8.0 m, y: 10.0 m) - ├─ Point(x: 9.0 m, y: 10.0 m) - └─ Point(x: 10.0 m, y: 10.0 m) - 100 elements - ├─ Quadrangle(1, 2, 13, 12) - ├─ Quadrangle(2, 3, 14, 13) - ├─ Quadrangle(3, 4, 15, 14) - ├─ Quadrangle(4, 5, 16, 15) - ├─ Quadrangle(5, 6, 17, 16) - ⋮ - ├─ Quadrangle(105, 106, 117, 116) - ├─ Quadrangle(106, 107, 118, 117) - ├─ Quadrangle(107, 108, 119, 118) - ├─ Quadrangle(108, 109, 120, 119) - └─ Quadrangle(109, 110, 121, 120)""" - end - @test_throws BoundsError grid[3:11, :] +@testitem "TransformedMesh" setup = [Setup] begin + grid = cartgrid(10, 10) + rgrid = convert(RectilinearGrid, grid) + sgrid = convert(StructuredGrid, grid) + mesh = convert(SimpleMesh, grid) + trans = Identity() + tmesh = TransformedMesh(mesh, trans) + @test crs(tmesh) <: Cartesian{NoDatum} + @test Meshes.lentype(tmesh) == ℳ + @test parent(tmesh) === mesh + @test Meshes.transform(tmesh) === trans + @test TransformedMesh(grid, trans) == grid + @test TransformedMesh(rgrid, trans) == rgrid + @test TransformedMesh(sgrid, trans) == sgrid + @test TransformedMesh(mesh, trans) == mesh + trans = Translate(T(10), T(10)) → Translate(T(-10), T(-10)) + @test TransformedMesh(grid, trans) == grid + @test TransformedMesh(rgrid, trans) == rgrid + @test TransformedMesh(sgrid, trans) == sgrid + @test TransformedMesh(mesh, trans) == mesh + trans1 = Translate(T(10), T(10)) + trans2 = Translate(T(-10), T(-10)) + @test TransformedMesh(TransformedMesh(grid, trans1), trans2) == TransformedMesh(grid, trans1 → trans2) + + # transforms that change the Manifold and/or CRS + points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + trans = Proj(Cartesian) + tmesh = TransformedMesh(mesh, trans) + @test manifold(tmesh) === 🌐 + @test crs(tmesh) <: Cartesian + trans = Proj(Polar) + tgrid = TransformedMesh(grid, trans) + @test tgrid isa TransformedGrid + @test manifold(tgrid) === 𝔼{2} + @test crs(tgrid) <: Polar + + # grid interface + trans = Identity() + tgrid = TransformedMesh(grid, trans) + @test tgrid isa TransformedGrid + @test size(tgrid) == (10, 10) + @test minimum(tgrid) == cart(0, 0) + @test maximum(tgrid) == cart(10, 10) + @test extrema(tgrid) == (cart(0, 0), cart(10, 10)) + @test vertex(tgrid, 1) == vertex(tgrid, ntuple(i -> 1, embeddim(tgrid))) + @test vertex(tgrid, nvertices(tgrid)) == vertex(tgrid, size(tgrid) .+ 1) + @test tgrid[1, 1] == tgrid[1] + @test tgrid[10, 10] == tgrid[100] + sub = tgrid[2:4, 3:7] + @test size(sub) == (3, 5) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(4, 7) + sub = tgrid[2, 3:7] + @test size(sub) == (1, 5) + @test minimum(sub) == cart(1, 2) + @test maximum(sub) == cart(2, 7) + sub = tgrid[:, 3:7] + @test size(sub) == (10, 5) + @test minimum(sub) == cart(0, 2) + @test maximum(sub) == cart(10, 7) + + # optimization of centroid + trans = Rotate(T(π / 4)) + cgrid = cartgrid(10, 10) + tmesh = TransformedMesh(cgrid, trans) + centr = centroid(tmesh, 1) + @test @allocated(centroid(tmesh, 1)) < 50 + + # optimization of == + trans = Rotate(T(π / 4)) + cgrid = cartgrid(1000, 1000) + tmesh = TransformedMesh(cgrid, trans) + @test tmesh == tmesh + + @test sprint(show, tgrid) == "10×10 TransformedGrid" + if T == Float32 + @test sprint(show, MIME"text/plain"(), tgrid) == """ + 10×10 TransformedGrid + 121 vertices + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 2.0f0 m, y: 0.0f0 m) + ├─ Point(x: 3.0f0 m, y: 0.0f0 m) + ├─ Point(x: 4.0f0 m, y: 0.0f0 m) + ⋮ + ├─ Point(x: 6.0f0 m, y: 10.0f0 m) + ├─ Point(x: 7.0f0 m, y: 10.0f0 m) + ├─ Point(x: 8.0f0 m, y: 10.0f0 m) + ├─ Point(x: 9.0f0 m, y: 10.0f0 m) + └─ Point(x: 10.0f0 m, y: 10.0f0 m) + 100 elements + ├─ Quadrangle(1, 2, 13, 12) + ├─ Quadrangle(2, 3, 14, 13) + ├─ Quadrangle(3, 4, 15, 14) + ├─ Quadrangle(4, 5, 16, 15) + ├─ Quadrangle(5, 6, 17, 16) + ⋮ + ├─ Quadrangle(105, 106, 117, 116) + ├─ Quadrangle(106, 107, 118, 117) + ├─ Quadrangle(107, 108, 119, 118) + ├─ Quadrangle(108, 109, 120, 119) + └─ Quadrangle(109, 110, 121, 120)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), tgrid) == """ + 10×10 TransformedGrid + 121 vertices + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 2.0 m, y: 0.0 m) + ├─ Point(x: 3.0 m, y: 0.0 m) + ├─ Point(x: 4.0 m, y: 0.0 m) + ⋮ + ├─ Point(x: 6.0 m, y: 10.0 m) + ├─ Point(x: 7.0 m, y: 10.0 m) + ├─ Point(x: 8.0 m, y: 10.0 m) + ├─ Point(x: 9.0 m, y: 10.0 m) + └─ Point(x: 10.0 m, y: 10.0 m) + 100 elements + ├─ Quadrangle(1, 2, 13, 12) + ├─ Quadrangle(2, 3, 14, 13) + ├─ Quadrangle(3, 4, 15, 14) + ├─ Quadrangle(4, 5, 16, 15) + ├─ Quadrangle(5, 6, 17, 16) + ⋮ + ├─ Quadrangle(105, 106, 117, 116) + ├─ Quadrangle(106, 107, 118, 117) + ├─ Quadrangle(107, 108, 119, 118) + ├─ Quadrangle(108, 109, 120, 119) + └─ Quadrangle(109, 110, 121, 120)""" end + @test_throws BoundsError grid[3:11, :] end diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 9c6a8c16b..0a585fc38 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -1,4 +1,4 @@ -@testset "Multi" begin +@testitem "Multigeometries" setup = [Setup] begin outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) diff --git a/test/neighborhoods.jl b/test/neighborhoods.jl index 2a4e51647..c540b2743 100644 --- a/test/neighborhoods.jl +++ b/test/neighborhoods.jl @@ -1,77 +1,75 @@ -@testset "Neighborhoods" begin - @testset "MetricBall" begin - # Euclidean metric - b = MetricBall(T(1 / 2)) - r = radius(b) - m = metric(b) - @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r - @test evaluate(m, T[0] * u"m", T[1] * u"m") > r - @test radii(b) == (T(1 / 2) * u"m",) +@testitem "MetricBall" setup = [Setup] begin + # Euclidean metric + b = MetricBall(T(1 / 2)) + r = radius(b) + m = metric(b) + @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r + @test evaluate(m, T[0] * u"m", T[1] * u"m") > r + @test radii(b) == (T(1 / 2) * u"m",) - b = MetricBall(T(1)) - r = radius(b) - m = metric(b) - @test evaluate(m, T[0, 0] * u"m", T[0, 0] * u"m") ≤ r - @test evaluate(m, T[0, 0] * u"m", T[1, 0] * u"m") ≤ r - @test evaluate(m, T[0, 0] * u"m", T[0, 1] * u"m") ≤ r - @test isisotropic(b) - if T === Float32 - @test sprint(show, b) == "MetricBall(1.0f0 m, Euclidean)" - else - @test sprint(show, b) == "MetricBall(1.0 m, Euclidean)" - end + b = MetricBall(T(1)) + r = radius(b) + m = metric(b) + @test evaluate(m, T[0, 0] * u"m", T[0, 0] * u"m") ≤ r + @test evaluate(m, T[0, 0] * u"m", T[1, 0] * u"m") ≤ r + @test evaluate(m, T[0, 0] * u"m", T[0, 1] * u"m") ≤ r + @test isisotropic(b) + if T === Float32 + @test sprint(show, b) == "MetricBall(1.0f0 m, Euclidean)" + else + @test sprint(show, b) == "MetricBall(1.0 m, Euclidean)" + end - # Chebyshev metric - b = MetricBall(T(1 / 2), Chebyshev()) - r = radius(b) - m = metric(b) - @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r - @test evaluate(m, T[0] * u"m", T[1] * u"m") > r + # Chebyshev metric + b = MetricBall(T(1 / 2), Chebyshev()) + r = radius(b) + m = metric(b) + @test evaluate(m, T[0] * u"m", T[0] * u"m") ≤ r + @test evaluate(m, T[0] * u"m", T[1] * u"m") > r - for r in T[1.0, 2.0, 3.0, 4.0, 5.0] - b = MetricBall(r, Chebyshev()) - r = radius(b) - m = metric(b) - for i in zero(r):oneunit(r):r, j in zero(r):oneunit(r):r - @test evaluate(m, T[0, 0] * u"m", [i, j]) ≤ r - end + for r in T[1.0, 2.0, 3.0, 4.0, 5.0] + o = MetricBall(r, Chebyshev()) + r = radius(o) + d = metric(o) + for i in zero(r):oneunit(r):r, j in zero(r):oneunit(r):r + @test evaluate(d, T[0, 0] * u"m", [i, j]) ≤ r end + end - # 2D simple test of default convention - b = MetricBall(T.((1, 1))) - m = metric(b) - @test radius(b) == oneunit(ℳ) - @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") == evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") + # 2D simple test of default convention + b = MetricBall(T.((1, 1))) + m = metric(b) + @test radius(b) == oneunit(ℳ) + @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") == evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") - b = MetricBall(T.((1, 2))) - m = metric(b) - @test radius(b) == oneunit(ℳ) - @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") != evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") + b = MetricBall(T.((1, 2))) + m = metric(b) + @test radius(b) == oneunit(ℳ) + @test evaluate(m, T[1, 0] * u"m", T[0, 0] * u"m") != evaluate(m, T[0, 1] * u"m", T[0, 0] * u"m") - # 3D simple test of default convention - b = MetricBall(T.((1.0, 0.5, 0.5)), RotZYX(T(-π / 4), T(0), T(0))) - m = metric(b) - @test radius(b) == oneunit(ℳ) - @test evaluate(m, T[1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(8) * u"m" - @test evaluate(m, T[-1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(2) * u"m" + # 3D simple test of default convention + b = MetricBall(T.((1.0, 0.5, 0.5)), RotZYX(T(-π / 4), T(0), T(0))) + m = metric(b) + @test radius(b) == oneunit(ℳ) + @test evaluate(m, T[1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(8) * u"m" + @test evaluate(m, T[-1.0, 1.0, 0.0] * u"m", T[0.0, 0.0, 0.0] * u"m") ≈ √T(2) * u"m" - # make sure the correct constructor is called - m = metric(MetricBall(T.((1.0, 0.5, 0.2)), RotXYX(T(0), T(0), T(0)))) - @test m isa Mahalanobis + # make sure the correct constructor is called + m = metric(MetricBall(T.((1.0, 0.5, 0.2)), RotXYX(T(0), T(0), T(0)))) + @test m isa Mahalanobis - # make sure the angle is clockwise - m = metric(MetricBall(T.((20.0, 5.0)), Angle2d(T(π / 2)))) - @test m isa Mahalanobis - @test evaluate(m, T[1.0, 0.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.2) * u"m" - @test evaluate(m, T[0.0, 1.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.05) * u"m" + # make sure the angle is clockwise + m = metric(MetricBall(T.((20.0, 5.0)), Angle2d(T(π / 2)))) + @test m isa Mahalanobis + @test evaluate(m, T[1.0, 0.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.2) * u"m" + @test evaluate(m, T[0.0, 1.0] * u"m", T[0.0, 0.0] * u"m") ≈ T(0.05) * u"m" - # basic multiplication - @test 2MetricBall(T(1)) == MetricBall(T(2)) - @test 2MetricBall(T.((1, 2, 3))) == MetricBall(T.((2, 4, 6))) + # basic multiplication + @test 2MetricBall(T(1)) == MetricBall(T(2)) + @test 2MetricBall(T.((1, 2, 3))) == MetricBall(T.((2, 4, 6))) - # access to rotation - @test rotation(MetricBall(T(1))) == I - @test rotation(MetricBall(T.((1, 2, 3)))) == I - @test rotation(MetricBall(T.((1, 2)), Angle2d(T(π / 2)))) == Angle2d(T(π / 2)) - end + # access to rotation + @test rotation(MetricBall(T(1))) == I + @test rotation(MetricBall(T.((1, 2, 3)))) == I + @test rotation(MetricBall(T.((1, 2)), Angle2d(T(π / 2)))) == Angle2d(T(π / 2)) end diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index 130c722d6..ac779ef4b 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -1,113 +1,111 @@ -@testset "Neighbor search" begin - @testset "BallSearch" begin - 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) +@testitem "BallSearch" setup = [Setup] begin + 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - s = BallSearch(𝒟, MetricBall(T(1))) - n = search(cart(0, 0), s) - @test Set(n) == Set([1, 2, 11]) - n = search(cart(9, 0), s) - @test Set(n) == Set([9, 10, 20]) - n = search(cart(0, 9), s) - @test Set(n) == Set([91, 81, 92]) - n = search(cart(9, 9), s) - @test Set(n) == Set([100, 99, 90]) + s = BallSearch(𝒟, MetricBall(T(1))) + n = search(cart(0, 0), s) + @test Set(n) == Set([1, 2, 11]) + n = search(cart(9, 0), s) + @test Set(n) == Set([9, 10, 20]) + n = search(cart(0, 9), s) + @test Set(n) == Set([91, 81, 92]) + n = search(cart(9, 9), s) + @test Set(n) == Set([100, 99, 90]) - s = BallSearch(𝒟, MetricBall(T(√2 + eps(T)))) - n = search(cart(0, 0), s) - @test Set(n) == Set([1, 2, 11, 12]) - n = search(cart(9, 0), s) - @test Set(n) == Set([9, 10, 19, 20]) - n = search(cart(0, 9), s) - @test Set(n) == Set([81, 82, 91, 92]) - n = search(cart(9, 9), s) - @test Set(n) == Set([89, 90, 99, 100]) + s = BallSearch(𝒟, MetricBall(T(√2 + eps(T)))) + n = search(cart(0, 0), s) + @test Set(n) == Set([1, 2, 11, 12]) + n = search(cart(9, 0), s) + @test Set(n) == Set([9, 10, 19, 20]) + n = search(cart(0, 9), s) + @test Set(n) == Set([81, 82, 91, 92]) + n = search(cart(9, 9), s) + @test Set(n) == Set([89, 90, 99, 100]) - # non MinkowskiMetric example - 𝒟 = CartesianGrid((360, 180), T.((0.0, -90.0)), T.((1.0, 1.0))) - s = BallSearch(𝒟, MetricBall(T(150), Haversine(T(6371)))) - n = search(cart(0, 0), s) - @test Set(n) == Set([32041, 32400, 32401, 32760]) + # non MinkowskiMetric example + 𝒟 = CartesianGrid((360, 180), T.((0.0, -90.0)), T.((1.0, 1.0))) + s = BallSearch(𝒟, MetricBall(T(150), Haversine(T(6371)))) + n = search(cart(0, 0), s) + @test Set(n) == Set([32041, 32400, 32401, 32760]) - # construct from vector of geometries - s = BallSearch(randpoint2(100), MetricBall(T(1))) - @test s isa BallSearch - end + # construct from vector of geometries + s = BallSearch(randpoint2(100), MetricBall(T(1))) + @test s isa BallSearch +end - @testset "KNearestSearch" begin - 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - s = KNearestSearch(𝒟, 3) - n = search(cart(0, 0), s) - @test Set(n) == Set([1, 2, 11]) - n = search(cart(9, 0), s) - @test Set(n) == Set([9, 10, 20]) - n = search(cart(0, 9), s) - @test Set(n) == Set([91, 81, 92]) - n = search(cart(9, 9), s) - @test Set(n) == Set([100, 99, 90]) - n, d = searchdists(cart(9, 9), s) - @test Set(n) == Set([100, 99, 90]) - @test length(d) == 3 - n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, cart(9, 9), s) - @test nn == 3 - @test Set(n[1:nn]) == Set([100, 99, 90]) - n = Vector{Int}(undef, maxneighbors(s)) - d = Vector{ℳ}(undef, maxneighbors(s)) - nn = searchdists!(n, d, cart(9, 9), s) - @test nn == 3 - @test Set(n[1:nn]) == Set([100, 99, 90]) +@testitem "KNearestSearch" setup = [Setup] begin + 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + s = KNearestSearch(𝒟, 3) + n = search(cart(0, 0), s) + @test Set(n) == Set([1, 2, 11]) + n = search(cart(9, 0), s) + @test Set(n) == Set([9, 10, 20]) + n = search(cart(0, 9), s) + @test Set(n) == Set([91, 81, 92]) + n = search(cart(9, 9), s) + @test Set(n) == Set([100, 99, 90]) + n, d = searchdists(cart(9, 9), s) + @test Set(n) == Set([100, 99, 90]) + @test length(d) == 3 + n = Vector{Int}(undef, maxneighbors(s)) + nn = search!(n, cart(9, 9), s) + @test nn == 3 + @test Set(n[1:nn]) == Set([100, 99, 90]) + n = Vector{Int}(undef, maxneighbors(s)) + d = Vector{ℳ}(undef, maxneighbors(s)) + nn = searchdists!(n, d, cart(9, 9), s) + @test nn == 3 + @test Set(n[1:nn]) == Set([100, 99, 90]) - # construct from vector of geometries - s = KNearestSearch(randpoint2(100), 3) - @test s isa KNearestSearch - end + # construct from vector of geometries + s = KNearestSearch(randpoint2(100), 3) + @test s isa KNearestSearch +end - @testset "KBallSearch" begin - 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) +@testitem "KBallSearch" setup = [Setup] begin + 𝒟 = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - s = KBallSearch(𝒟, 10, MetricBall(T(100))) - n = search(cart(5, 5), s) - @test length(n) == 10 + s = KBallSearch(𝒟, 10, MetricBall(T(100))) + n = search(cart(5, 5), s) + @test length(n) == 10 - s = KBallSearch(𝒟, 10, MetricBall(T.((100, 100)))) - n = search(cart(5, 5), s) - @test length(n) == 10 + s = KBallSearch(𝒟, 10, MetricBall(T.((100, 100)))) + n = search(cart(5, 5), s) + @test length(n) == 10 - s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n = search(cart(5, 5), s) - @test length(n) == 5 - @test n[1] == 56 + s = KBallSearch(𝒟, 10, MetricBall(T(1))) + n = search(cart(5, 5), s) + @test length(n) == 5 + @test n[1] == 56 - s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n, d = searchdists(cart(5, 5), s) - @test length(n) == 5 - @test length(d) == 5 + s = KBallSearch(𝒟, 10, MetricBall(T(1))) + n, d = searchdists(cart(5, 5), s) + @test length(n) == 5 + @test length(d) == 5 - s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n = Vector{Int}(undef, maxneighbors(s)) - nn = search!(n, cart(5, 5), s) - @test nn == 5 + s = KBallSearch(𝒟, 10, MetricBall(T(1))) + n = Vector{Int}(undef, maxneighbors(s)) + nn = search!(n, cart(5, 5), s) + @test nn == 5 - s = KBallSearch(𝒟, 10, MetricBall(T(1))) - n = Vector{Int}(undef, maxneighbors(s)) - d = Vector{ℳ}(undef, maxneighbors(s)) - nn = searchdists!(n, d, cart(5, 5), s) - @test nn == 5 + s = KBallSearch(𝒟, 10, MetricBall(T(1))) + n = Vector{Int}(undef, maxneighbors(s)) + d = Vector{ℳ}(undef, maxneighbors(s)) + nn = searchdists!(n, d, cart(5, 5), s) + @test nn == 5 - mask = trues(nelements(𝒟)) - mask[56] = false - n = search(cart(5, 5), s, mask=mask) - @test length(n) == 4 - n = search(cart(-0.2, -0.2), s) - @test length(n) == 1 - n = search(cart(-10, -10), s) - @test length(n) == 0 - n, d = searchdists(cart(5, 5), s, mask=mask) - @test length(n) == 4 - @test length(d) == 4 + mask = trues(nelements(𝒟)) + mask[56] = false + n = search(cart(5, 5), s, mask=mask) + @test length(n) == 4 + n = search(cart(-0.2, -0.2), s) + @test length(n) == 1 + n = search(cart(-10, -10), s) + @test length(n) == 0 + n, d = searchdists(cart(5, 5), s, mask=mask) + @test length(n) == 4 + @test length(d) == 4 - # construct from vector of geometries - s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) - @test s isa KBallSearch - end + # construct from vector of geometries + s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) + @test s isa KBallSearch end diff --git a/test/orientation.jl b/test/orientation.jl index c0d9d4623..a01a835b9 100644 --- a/test/orientation.jl +++ b/test/orientation.jl @@ -1,4 +1,4 @@ -@testset "orientation" begin +@testitem "orientation" setup = [Setup] begin # test orientation t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test orientation(t) == CCW diff --git a/test/partitioning.jl b/test/partitioning.jl index a074ae5a2..34243513e 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -1,373 +1,370 @@ -@testset "Partitioning" begin - setify(lists) = Set(Set.(lists)) - +@testitem "UniformPartition" setup = [Setup] begin + rng = StableRNG(123) g = cartgrid(10, 10) p = partition(g, UniformPartition(100)) @test parent(p) == g @test length(p) == 100 - @testset "UniformPartition" begin - rng = StableRNG(123) - g = cartgrid(3, 3) - p = partition(rng, g, UniformPartition(3, false)) - @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - rng = StableRNG(123) - p = partition(rng, g, UniformPartition(3)) - @test setify(indices(p)) == setify([[5, 4, 2], [6, 7, 8], [9, 3, 1]]) - - g = cartgrid(2, 3) - p = partition(g, UniformPartition(3, false)) - @test setify(indices(p)) == setify([[1, 2], [3, 4], [5, 6]]) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, UniformPartition(3)) - rng = StableRNG(123) - p2 = partition(rng, g, UniformPartition(3)) - @test p1 == p2 - end + rng = StableRNG(123) + g = cartgrid(3, 3) + p = partition(rng, g, UniformPartition(3, false)) + @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + rng = StableRNG(123) + p = partition(rng, g, UniformPartition(3)) + @test setify(indices(p)) == setify([[5, 4, 2], [6, 7, 8], [9, 3, 1]]) + + g = cartgrid(2, 3) + p = partition(g, UniformPartition(3, false)) + @test setify(indices(p)) == setify([[1, 2], [3, 4], [5, 6]]) + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, UniformPartition(3)) + rng = StableRNG(123) + p2 = partition(rng, g, UniformPartition(3)) + @test p1 == p2 +end + +@testitemm "DirectionPartition" setup = [Setup] begin + g = cartgrid(3, 3) + + # basic checks on small grids + p = partition(g, DirectionPartition(T.((1, 0)))) + @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @testset "DirectionPartition" begin - g = cartgrid(3, 3) + p = partition(g, DirectionPartition(T.((0, 1)))) + @test setify(indices(p)) == setify([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) + + p = partition(g, DirectionPartition(T.((1, 1)))) + @test setify(indices(p)) == setify([[1, 5, 9], [2, 6], [3], [4, 8], [7]]) + + p = partition(g, DirectionPartition(T.((1, -1)))) + @test setify(indices(p)) == setify([[1], [2, 4], [3, 5, 7], [6, 8], [9]]) + + # opposite directions produce same partition + dir1 = (rand(T), rand(T)) + dir2 = .-dir1 + p1 = partition(g, DirectionPartition(dir1)) + p2 = partition(g, DirectionPartition(dir2)) + @test setify(indices(p1)) == setify(indices(p2)) + + # partition of arbitrarily large grid always + # returns the "lines" and "columns" + for n in [10, 100, 200] + g = cartgrid(n, n) - # basic checks on small grids p = partition(g, DirectionPartition(T.((1, 0)))) - @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @test setify(indices(p)) == setify([collect(((i - 1) * n + 1):(i * n)) for i in 1:n]) + ns = [nelements(d) for d in p] + @test all(ns .== n) p = partition(g, DirectionPartition(T.((0, 1)))) - @test setify(indices(p)) == setify([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) - - p = partition(g, DirectionPartition(T.((1, 1)))) - @test setify(indices(p)) == setify([[1, 5, 9], [2, 6], [3], [4, 8], [7]]) - - p = partition(g, DirectionPartition(T.((1, -1)))) - @test setify(indices(p)) == setify([[1], [2, 4], [3, 5, 7], [6, 8], [9]]) - - # opposite directions produce same partition - dir1 = (rand(T), rand(T)) - dir2 = .-dir1 - p1 = partition(g, DirectionPartition(dir1)) - p2 = partition(g, DirectionPartition(dir2)) - @test setify(indices(p1)) == setify(indices(p2)) - - # partition of arbitrarily large grid always - # returns the "lines" and "columns" - for n in [10, 100, 200] - g = cartgrid(n, n) - - p = partition(g, DirectionPartition(T.((1, 0)))) - @test setify(indices(p)) == setify([collect(((i - 1) * n + 1):(i * n)) for i in 1:n]) - ns = [nelements(d) for d in p] - @test all(ns .== n) - - p = partition(g, DirectionPartition(T.((0, 1)))) - @test setify(indices(p)) == setify([collect(i:n:(n * n)) for i in 1:n]) - ns = [nelements(d) for d in p] - @test all(ns .== n) - end - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, DirectionPartition(T.((1, 0)))) - rng = StableRNG(123) - p2 = partition(rng, g, DirectionPartition(T.((1, 0)))) - @test p1 == p2 + @test setify(indices(p)) == setify([collect(i:n:(n * n)) for i in 1:n]) + ns = [nelements(d) for d in p] + @test all(ns .== n) end - @testset "FractionPartition" begin - g = cartgrid(10, 10) + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, DirectionPartition(T.((1, 0)))) + rng = StableRNG(123) + p2 = partition(rng, g, DirectionPartition(T.((1, 0)))) + @test p1 == p2 +end - p = partition(g, FractionPartition(T(0.5))) - @test nelements(p[1]) == nelements(p[2]) == 50 - @test length(p) == 2 +@testitem "FractionPartition" setup = [Setup] begin + g = cartgrid(10, 10) - p = partition(g, FractionPartition(T(0.7))) - @test nelements(p[1]) == 70 - @test nelements(p[2]) == 30 + p = partition(g, FractionPartition(T(0.5))) + @test nelements(p[1]) == nelements(p[2]) == 50 + @test length(p) == 2 - p = partition(g, FractionPartition(T(0.3))) - @test nelements(p[1]) == 30 - @test nelements(p[2]) == 70 + p = partition(g, FractionPartition(T(0.7))) + @test nelements(p[1]) == 70 + @test nelements(p[2]) == 30 - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, FractionPartition(T(0.5))) - rng = StableRNG(123) - p2 = partition(rng, g, FractionPartition(T(0.5))) - @test p1 == p2 - end + p = partition(g, FractionPartition(T(0.3))) + @test nelements(p[1]) == 30 + @test nelements(p[2]) == 70 - @testset "BlockPartition" begin - g = cartgrid(10, 10) - - p = partition(g, BlockPartition(T(5), T(5))) - @test length(p) == 4 - @test all(nelements.(p) .== 25) - - p = partition(g, BlockPartition(T(5), T(2))) - @test length(p) == 12 - @test Set(nelements.(p)) == Set([5, 10]) - - g = cartgrid(50, 50, 50) - - p = partition(g, BlockPartition(T(1.0), T(1.0), T(1.0), neighbors=false)) - @test length(p) == 125000 - @test Set(nelements.(p)) == Set(1) - @test metadata(p) == Dict{Any,Any}() - - p = partition(g, BlockPartition(T(5.0), T(5.0), T(5.0), neighbors=true)) - @test length(p) == 1000 - @test Set(nelements.(p)) == Set(125) - n = metadata(p)[:neighbors] - @test length(n) == length(p) - @test all(0 .< length.(n) .< 27) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, BlockPartition(T(5), T(2))) - rng = StableRNG(123) - p2 = partition(rng, g, BlockPartition(T(5), T(2))) - @test p1 == p2 - - m1 = BlockPartition((T(5), T(2))) - m2 = BlockPartition(T(5), T(2)) - m3 = BlockPartition((T(5), T(2)), neighbors=false) - m4 = BlockPartition(T(5), T(2), neighbors=false) - @test m1 == m2 == m3 == m4 - m1 = BlockPartition(T(1)) - m2 = BlockPartition(T(1), neighbors=false) - @test m1 == m2 - end + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, FractionPartition(T(0.5))) + rng = StableRNG(123) + p2 = partition(rng, g, FractionPartition(T(0.5))) + @test p1 == p2 +end - @testset "BisectPointPartition" begin - g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - - p = partition(g, BisectPointPartition(T.((0.0, 1.0)), T.((5.0, 5.1)))) - p1, p2 = p[1], p[2] - @test nelements(p1) == 60 - @test nelements(p2) == 40 - - # all points in p1 are below those in p2 - pts1 = [centroid(p1, i) for i in 1:nelements(p1)] - pts2 = [centroid(p2, i) for i in 1:nelements(p2)] - X1 = reduce(hcat, to.(pts1)) - X2 = reduce(hcat, to.(pts2)) - M1 = maximum(X1, dims=2) - m2 = minimum(X2, dims=2) - @test all(X1[2, j] < m2[2] for j in 1:size(X1, 2)) - @test all(X2[2, j] > M1[2] for j in 1:size(X2, 2)) - - # flipping normal direction is equivalent to swapping subsets - p₁ = partition(g, BisectPointPartition(T.((1.0, 0.0)), T.((5.1, 5.0)))) - p₂ = partition(g, BisectPointPartition(T.((-1.0, 0.0)), T.((5.1, 5.0)))) - @test nelements(p₁[1]) == nelements(p₂[2]) == 60 - @test nelements(p₁[2]) == nelements(p₂[1]) == 40 - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) - rng = StableRNG(123) - p2 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) - @test p1 == p2 - end +@testitem "BlockPartition" setup = [Setup] begin + g = cartgrid(10, 10) - @testset "BisectFractionPartition" begin - g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - - p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - p1, p2 = p[1], p[2] - @test nelements(p1) == 20 - @test nelements(p2) == 80 - - # all points in p1 are to the left of p2 - pts1 = [centroid(p1, i) for i in 1:nelements(p1)] - pts2 = [centroid(p2, i) for i in 1:nelements(p2)] - X1 = reduce(hcat, to.(pts1)) - X2 = reduce(hcat, to.(pts2)) - M1 = maximum(X1, dims=2) - m2 = minimum(X2, dims=2) - @test all(X1[1, j] < m2[1] for j in 1:size(X1, 2)) - @test all(X2[1, j] > M1[1] for j in 1:size(X2, 2)) - - # flipping normal direction is equivalent to swapping subsets - p₁ = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - p₂ = partition(g, BisectFractionPartition(T.((-1.0, 0.0)), T(0.8))) - @test nelements(p₁[1]) == nelements(p₂[2]) == 20 - @test nelements(p₁[2]) == nelements(p₂[1]) == 80 - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) - rng = StableRNG(123) - p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) - @test p1 == p2 - - # CRS propagation - g = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) - p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) - @test crs(first(p)) === crs(g) - end + p = partition(g, BlockPartition(T(5), T(5))) + @test length(p) == 4 + @test all(nelements.(p) .== 25) - @testset "BallPartition" begin - pset = PointSet(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1), cart(0.2, 0.2)) - - # 3 balls with 1 point, and 1 ball with 2 points - p = partition(pset, BallPartition(T(0.5))) - n = nelements.(p) - @test length(p) == 4 - @test count(i -> i == 1, n) == 3 - @test count(i -> i == 2, n) == 1 - @test setify(indices(p)) == setify([[1, 5], [2], [3], [4]]) - - # 5 balls with 1 point each - p = partition(pset, BallPartition(T(0.2))) - @test length(p) == 5 - @test all(nelements.(p) .== 1) - @test setify(indices(p)) == setify([[1], [2], [3], [4], [5]]) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, BallPartition(T(2))) - rng = StableRNG(123) - p2 = partition(rng, g, BallPartition(T(2))) - @test p1 == p2 - end + p = partition(g, BlockPartition(T(5), T(2))) + @test length(p) == 12 + @test Set(nelements.(p)) == Set([5, 10]) - @testset "PlanePartition" begin - g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(g, PlanePartition(T.((0, 1)))) - @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - - g = CartesianGrid((4, 4), T.((-0.5, -0.5)), T.((1.0, 1.0))) - p = partition(g, PlanePartition(T.((0, 1)))) - @test setify(indices(p)) == setify([1:4, 5:8, 9:12, 13:16]) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, PlanePartition(T.((1, 0)))) - rng = StableRNG(123) - p2 = partition(rng, g, PlanePartition(T.((1, 0)))) - @test p1 == p2 - end + g = cartgrid(50, 50, 50) - @testset "PredicatePartition" begin - g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) - - # partition even from odd locations - pred(i, j) = iseven(i + j) - partitioner = PredicatePartition(pred) - p = partition(g, partitioner) - @test setify(indices(p)) == setify([1:2:9, 2:2:8]) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, partitioner) - rng = StableRNG(123) - p2 = partition(rng, g, partitioner) - @test p1 == p2 - end + p = partition(g, BlockPartition(T(1.0), T(1.0), T(1.0), neighbors=false)) + @test length(p) == 125000 + @test Set(nelements.(p)) == Set(1) + @test metadata(p) == Dict{Any,Any}() - @testset "SpatialPredicatePartition" begin - g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) - - # check if there are 100 partitions, each one having only 1 point - sp = SpatialPredicatePartition((x, y) -> norm(x - y) < T(1) * u"m") - s = indices(partition(g, sp)) - @test length(s) == 100 - nelms = [nelements(d) for d in partition(g, sp)] - @test all(nelms .== 1) - - # defining a predicate to check if points x and y belong to the square [0.,5.]x[0.,5.] - pred(x, y) = all(T[0, 0] * u"m" .<= x .<= T[5, 5] * u"m") && all(T[0, 0] * u"m" .<= y .<= T[5, 5] * u"m") - sp = SpatialPredicatePartition(pred) - p = partition(g, sp) - s = indices(p) - n = nelements.(p) - - # There will be 65 subsets: - # 1 subset with 36 points (inside square [0.,5.]x[0.,5.]) - # 64 subsets with only 1 point inside each of them - @test length(s) == 65 - @test maximum(length.(s)) == 36 - @test count(i -> i == 1, n) == 64 - @test count(i -> i == 36, n) == 1 - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, sp) - rng = StableRNG(123) - p2 = partition(rng, g, sp) - @test p1 == p2 - end + p = partition(g, BlockPartition(T(5.0), T(5.0), T(5.0), neighbors=true)) + @test length(p) == 1000 + @test Set(nelements.(p)) == Set(125) + n = metadata(p)[:neighbors] + @test length(n) == length(p) + @test all(0 .< length.(n) .< 27) - @testset "ProductPartition" begin - g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) - bm = BlockPartition(T(10), T(10)) - bn = BlockPartition(T(5), T(5)) - bmn = ProductPartition(bm, bn) - - # Bm x Bn = Bn with m > n - s1 = indices(partition(g, bmn)) - s2 = indices(partition(g, bn)) - @test setify(s1) == setify(s2) - - # pXp=p (for deterministic p) - for p in [BlockPartition(T(10), T(10)), BisectFractionPartition(T.((0.1, 0.1)))] - pp = ProductPartition(p, p) - s1 = indices(partition(g, pp)) - s2 = indices(partition(g, p)) - @test setify(s1) == setify(s2) - end - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, bmn) - rng = StableRNG(123) - p2 = partition(rng, g, bmn) - @test p1 == p2 - end + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, BlockPartition(T(5), T(2))) + rng = StableRNG(123) + p2 = partition(rng, g, BlockPartition(T(5), T(2))) + @test p1 == p2 + + m1 = BlockPartition((T(5), T(2))) + m2 = BlockPartition(T(5), T(2)) + m3 = BlockPartition((T(5), T(2)), neighbors=false) + m4 = BlockPartition(T(5), T(2), neighbors=false) + @test m1 == m2 == m3 == m4 + m1 = BlockPartition(T(1)) + m2 = BlockPartition(T(1), neighbors=false) + @test m1 == m2 +end - @testset "HierarchicalPartition" begin - g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) - bm = BlockPartition(T(10), T(10)) - bn = BlockPartition(T(5), T(5)) - bmn = HierarchicalPartition(bm, bn) - - # Bn -> Bm = Bm with m > n - s1 = indices(partition(g, bmn)) - s2 = indices(partition(g, bn)) - @test setify(s1) == setify(s2) - - # reproducible results with rng - rng = StableRNG(123) - g = cartgrid(10, 10) - p1 = partition(rng, g, bmn) - rng = StableRNG(123) - p2 = partition(rng, g, bmn) - @test p1 == p2 - end +@testitem "BisectPointPartition" setup = [Setup] begin + g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + + p = partition(g, BisectPointPartition(T.((0.0, 1.0)), T.((5.0, 5.1)))) + p1, p2 = p[1], p[2] + @test nelements(p1) == 60 + @test nelements(p2) == 40 + + # all points in p1 are below those in p2 + pts1 = [centroid(p1, i) for i in 1:nelements(p1)] + pts2 = [centroid(p2, i) for i in 1:nelements(p2)] + X1 = reduce(hcat, to.(pts1)) + X2 = reduce(hcat, to.(pts2)) + M1 = maximum(X1, dims=2) + m2 = minimum(X2, dims=2) + @test all(X1[2, j] < m2[2] for j in 1:size(X1, 2)) + @test all(X2[2, j] > M1[2] for j in 1:size(X2, 2)) + + # flipping normal direction is equivalent to swapping subsets + p₁ = partition(g, BisectPointPartition(T.((1.0, 0.0)), T.((5.1, 5.0)))) + p₂ = partition(g, BisectPointPartition(T.((-1.0, 0.0)), T.((5.1, 5.0)))) + @test nelements(p₁[1]) == nelements(p₂[2]) == 60 + @test nelements(p₁[2]) == nelements(p₂[1]) == 40 + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) + rng = StableRNG(123) + p2 = partition(rng, g, BisectPointPartition(T.((1, 0)), T.((5, 5)))) + @test p1 == p2 +end + +@testitem "BisectFractionPartition" setup = [Setup] begin + g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + + p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + p1, p2 = p[1], p[2] + @test nelements(p1) == 20 + @test nelements(p2) == 80 + + # all points in p1 are to the left of p2 + pts1 = [centroid(p1, i) for i in 1:nelements(p1)] + pts2 = [centroid(p2, i) for i in 1:nelements(p2)] + X1 = reduce(hcat, to.(pts1)) + X2 = reduce(hcat, to.(pts2)) + M1 = maximum(X1, dims=2) + m2 = minimum(X2, dims=2) + @test all(X1[1, j] < m2[1] for j in 1:size(X1, 2)) + @test all(X2[1, j] > M1[1] for j in 1:size(X2, 2)) + + # flipping normal direction is equivalent to swapping subsets + p₁ = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + p₂ = partition(g, BisectFractionPartition(T.((-1.0, 0.0)), T(0.8))) + @test nelements(p₁[1]) == nelements(p₂[2]) == 20 + @test nelements(p₁[2]) == nelements(p₂[1]) == 80 + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) + rng = StableRNG(123) + p2 = partition(rng, g, BisectFractionPartition(T.((1, 0)), T(0.5))) + @test p1 == p2 + + # CRS propagation + g = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) + p = partition(g, BisectFractionPartition(T.((1.0, 0.0)), T(0.2))) + @test crs(first(p)) === crs(g) +end + +@testitem "BallPartition" setup = [Setup] begin + pset = PointSet(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1), cart(0.2, 0.2)) + + # 3 balls with 1 point, and 1 ball with 2 points + p = partition(pset, BallPartition(T(0.5))) + n = nelements.(p) + @test length(p) == 4 + @test count(i -> i == 1, n) == 3 + @test count(i -> i == 2, n) == 1 + @test setify(indices(p)) == setify([[1, 5], [2], [3], [4]]) + + # 5 balls with 1 point each + p = partition(pset, BallPartition(T(0.2))) + @test length(p) == 5 + @test all(nelements.(p) .== 1) + @test setify(indices(p)) == setify([[1], [2], [3], [4], [5]]) + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, BallPartition(T(2))) + rng = StableRNG(123) + p2 = partition(rng, g, BallPartition(T(2))) + @test p1 == p2 +end + +@testitem "PlanePartition" setup = [Setup] begin + g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) + p = partition(g, PlanePartition(T.((0, 1)))) + @test setify(indices(p)) == setify([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + + g = CartesianGrid((4, 4), T.((-0.5, -0.5)), T.((1.0, 1.0))) + p = partition(g, PlanePartition(T.((0, 1)))) + @test setify(indices(p)) == setify([1:4, 5:8, 9:12, 13:16]) + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, PlanePartition(T.((1, 0)))) + rng = StableRNG(123) + p2 = partition(rng, g, PlanePartition(T.((1, 0)))) + @test p1 == p2 +end + +@testitem "PredicatePartition" setup = [Setup] begin + g = CartesianGrid((3, 3), T.((-0.5, -0.5)), T.((1.0, 1.0))) + + # partition even from odd locations + pred(i, j) = iseven(i + j) + partitioner = PredicatePartition(pred) + p = partition(g, partitioner) + @test setify(indices(p)) == setify([1:2:9, 2:2:8]) - @testset "Mixed Tests" begin - g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) - bm = BlockPartition(T(10), T(10)) - bn = BlockPartition(T(5), T(5)) - bmn = ProductPartition(bm, bn) - hmn = HierarchicalPartition(bm, bn) - - # Bm*Bn = Bm->Bn - s1 = indices(partition(g, bmn)) - s2 = indices(partition(g, hmn)) - @test setify(s1) == setify(s2) + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, partitioner) + rng = StableRNG(123) + p2 = partition(rng, g, partitioner) + @test p1 == p2 +end + +@testitem "SpatialPredicatePartition" setup = [Setup] begin + g = CartesianGrid((10, 10), T.((-0.5, -0.5)), T.((1.0, 1.0))) + + # check if there are 100 partitions, each one having only 1 point + sp = SpatialPredicatePartition((x, y) -> norm(x - y) < T(1) * u"m") + s = indices(partition(g, sp)) + @test length(s) == 100 + nelms = [nelements(d) for d in partition(g, sp)] + @test all(nelms .== 1) + + # defining a predicate to check if points x and y belong to the square [0.,5.]x[0.,5.] + pred(x, y) = all(T[0, 0] * u"m" .<= x .<= T[5, 5] * u"m") && all(T[0, 0] * u"m" .<= y .<= T[5, 5] * u"m") + sp = SpatialPredicatePartition(pred) + p = partition(g, sp) + s = indices(p) + n = nelements.(p) + + # There will be 65 subsets: + # 1 subset with 36 points (inside square [0.,5.]x[0.,5.]) + # 64 subsets with only 1 point inside each of them + @test length(s) == 65 + @test maximum(length.(s)) == 36 + @test count(i -> i == 1, n) == 64 + @test count(i -> i == 36, n) == 1 + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, sp) + rng = StableRNG(123) + p2 = partition(rng, g, sp) + @test p1 == p2 +end + +@testitem "ProductPartition" setup = [Setup] begin + g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) + bm = BlockPartition(T(10), T(10)) + bn = BlockPartition(T(5), T(5)) + bmn = ProductPartition(bm, bn) + + # Bm x Bn = Bn with m > n + s1 = indices(partition(g, bmn)) + s2 = indices(partition(g, bn)) + @test setify(s1) == setify(s2) + + # pXp=p (for deterministic p) + for p in [BlockPartition(T(10), T(10)), BisectFractionPartition(T.((0.1, 0.1)))] + pp = ProductPartition(p, p) + i1 = indices(partition(g, pp)) + i2 = indices(partition(g, p)) + @test setify(i1) == setify(i2) end + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, bmn) + rng = StableRNG(123) + p2 = partition(rng, g, bmn) + @test p1 == p2 +end + +@testitem "HierarchicalPartition" setup = [Setup] begin + g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) + bm = BlockPartition(T(10), T(10)) + bn = BlockPartition(T(5), T(5)) + bmn = HierarchicalPartition(bm, bn) + + # Bn -> Bm = Bm with m > n + s1 = indices(partition(g, bmn)) + s2 = indices(partition(g, bn)) + @test setify(s1) == setify(s2) + + # reproducible results with rng + rng = StableRNG(123) + g = cartgrid(10, 10) + p1 = partition(rng, g, bmn) + rng = StableRNG(123) + p2 = partition(rng, g, bmn) + @test p1 == p2 +end + +@testitem "Misc partition" setup = [Setup] begin + g = CartesianGrid((100, 100), T.((-0.5, -0.5)), T.((1.0, 1.0))) + bm = BlockPartition(T(10), T(10)) + bn = BlockPartition(T(5), T(5)) + bmn = ProductPartition(bm, bn) + hmn = HierarchicalPartition(bm, bn) + + # Bm*Bn = Bm->Bn + s1 = indices(partition(g, bmn)) + s2 = indices(partition(g, hmn)) + @test setify(s1) == setify(s2) end diff --git a/test/pointification.jl b/test/pointification.jl index e1ecf0195..9dfb09fe8 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -1,4 +1,4 @@ -@testset "Pointification" begin +@testitem "Pointification" setup = [Setup] begin p = cart(0, 0) @test pointify(p) == [cart(0, 0)] diff --git a/test/polytopes.jl b/test/polytopes.jl index 098420d0f..ca3fd39cc 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -1,930 +1,928 @@ -@testset "Polytopes" begin - @testset "Segments" begin - @test paramdim(Segment) == 1 - @test nvertices(Segment) == 2 - - s = Segment(cart(1.0), cart(2.0)) - @test crs(s) <: Cartesian{NoDatum} - @test Meshes.lentype(s) == ℳ - @test vertex(s, 1) == cart(1.0) - @test vertex(s, 2) == cart(2.0) - @test all(cart(x) ∈ s for x in 1:0.01:2) - @test all(cart(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) - @test s ≈ s - @test !(s ≈ Segment(cart(2.0), cart(1.0))) - @test !(s ≈ Segment(cart(-1.0), cart(2.0))) - @test reverse(s) == Segment(cart(2.0), cart(1.0)) - - s = Segment(cart(0, 0), cart(1, 1)) - @test minimum(s) == cart(0, 0) - @test maximum(s) == cart(1, 1) - @test extrema(s) == (cart(0, 0), cart(1, 1)) - @test isapprox(length(s), sqrt(T(2)) * u"m") - @test s(T(0)) == cart(0, 0) - @test s(T(1)) == cart(1, 1) - @test all(cart(x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [cart(-0.1, -0.1), cart(1.1, 1.1), cart(0.5, 0.49), cart(1, 2)]) - @test_throws DomainError(T(1.2), "s(t) is not defined for t outside [0, 1].") s(T(1.2)) - @test_throws DomainError(T(-0.5), "s(t) is not defined for t outside [0, 1].") s(T(-0.5)) - @test s ≈ s - @test !(s ≈ Segment(cart(1, 1), cart(0, 0))) - @test !(s ≈ Segment(cart(1, 2), cart(0, 0))) - @test reverse(s) == Segment(cart(1, 1), cart(0, 0)) - - s = Segment(cart(0, 0, 0), cart(1, 1, 1)) - @test all(cart(x, x, x) ∈ s for x in 0:0.01:1) - @test all(p ∉ s for p in [cart(-0.1, -0.1, -0.1), cart(1.1, 1.1, 1.1)]) - @test all(p ∉ s for p in [cart(0.5, 0.5, 0.49), cart(1, 1, 2)]) - @test s ≈ s - @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 0, 0))) - @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 1, 0))) - @test reverse(s) == Segment(cart(1, 1, 1), cart(0, 0, 0)) - - s = Segment(cart(0, 0), cart(1, 1)) - equaltest(s) - isapproxtest(s) - - s = Segment(Point(1.0, 1.0, 1.0, 1.0), Point(2.0, 2.0, 2.0, 2.0)) - @test all(Point(x, x, x, x) ∈ s for x in 1:0.01:2) - @test all(p ∉ s for p in [Point(0.99, 0.99, 0.99, 0.99), Point(2.1, 2.1, 2.1, 2.1)]) - @test all(p ∉ s for p in [Point(1.5, 1.5, 1.5, 1.49), Point(1, 1, 2, 1.0)]) - @test s ≈ s - @test !(s ≈ Segment(Point(2, 2, 2, 2), Point(1, 1, 1, 1))) - @test !(s ≈ Segment(Point(1, 1, 2, 1), Point(0, 0, 0, 0))) - - s = Segment(cart(0, 0, 0), cart(1, 1, 1)) - @test boundary(s) == Multi([cart(0, 0, 0), cart(1, 1, 1)]) - @test perimeter(s) == zero(T) * u"m" - @test centroid(s) == cart(0.5, 0.5, 0.5) - @test Meshes.lentype(centroid(s)) == ℳ - - # unitful coordinates - x1 = T(0)u"m" - x2 = T(1)u"m" - s = Segment(Point(x1, x1, x1), Point(x2, x2, x2)) - @test boundary(s) == Multi([Point(x1, x1, x1), Point(x2, x2, x2)]) - @test perimeter(s) == 0u"m" - xm = T(0.5)u"m" - @test centroid(s) == Point(xm, xm, xm) - @test Meshes.lentype(centroid(s)) == typeof(xm) - - # CRS propagation - s = Segment(merc(0, 0), merc(1, 1)) - @test crs(s(T(0))) === crs(s) - - s = Segment(cart(0, 0), cart(1, 1)) - @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), s) == """ - Segment - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - └─ Point(x: 1.0f0 m, y: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), s) == """ - Segment - ├─ Point(x: 0.0 m, y: 0.0 m) - └─ Point(x: 1.0 m, y: 1.0 m)""" - end +@testitem "Segments" setup = [Setup] begin + @test paramdim(Segment) == 1 + @test nvertices(Segment) == 2 + + s = Segment(cart(1.0), cart(2.0)) + @test crs(s) <: Cartesian{NoDatum} + @test Meshes.lentype(s) == ℳ + @test vertex(s, 1) == cart(1.0) + @test vertex(s, 2) == cart(2.0) + @test all(cart(x) ∈ s for x in 1:0.01:2) + @test all(cart(x) ∉ s for x in [-1.0, 0.0, 0.99, 2.1, 5.0, 10.0]) + @test s ≈ s + @test !(s ≈ Segment(cart(2.0), cart(1.0))) + @test !(s ≈ Segment(cart(-1.0), cart(2.0))) + @test reverse(s) == Segment(cart(2.0), cart(1.0)) + + s = Segment(cart(0, 0), cart(1, 1)) + @test minimum(s) == cart(0, 0) + @test maximum(s) == cart(1, 1) + @test extrema(s) == (cart(0, 0), cart(1, 1)) + @test isapprox(length(s), sqrt(T(2)) * u"m") + @test s(T(0)) == cart(0, 0) + @test s(T(1)) == cart(1, 1) + @test all(cart(x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [cart(-0.1, -0.1), cart(1.1, 1.1), cart(0.5, 0.49), cart(1, 2)]) + @test_throws DomainError(T(1.2), "s(t) is not defined for t outside [0, 1].") s(T(1.2)) + @test_throws DomainError(T(-0.5), "s(t) is not defined for t outside [0, 1].") s(T(-0.5)) + @test s ≈ s + @test !(s ≈ Segment(cart(1, 1), cart(0, 0))) + @test !(s ≈ Segment(cart(1, 2), cart(0, 0))) + @test reverse(s) == Segment(cart(1, 1), cart(0, 0)) + + s = Segment(cart(0, 0, 0), cart(1, 1, 1)) + @test all(cart(x, x, x) ∈ s for x in 0:0.01:1) + @test all(p ∉ s for p in [cart(-0.1, -0.1, -0.1), cart(1.1, 1.1, 1.1)]) + @test all(p ∉ s for p in [cart(0.5, 0.5, 0.49), cart(1, 1, 2)]) + @test s ≈ s + @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 0, 0))) + @test !(s ≈ Segment(cart(1, 1, 1), cart(0, 1, 0))) + @test reverse(s) == Segment(cart(1, 1, 1), cart(0, 0, 0)) + + s = Segment(cart(0, 0), cart(1, 1)) + equaltest(s) + isapproxtest(s) + + s = Segment(Point(1.0, 1.0, 1.0, 1.0), Point(2.0, 2.0, 2.0, 2.0)) + @test all(Point(x, x, x, x) ∈ s for x in 1:0.01:2) + @test all(p ∉ s for p in [Point(0.99, 0.99, 0.99, 0.99), Point(2.1, 2.1, 2.1, 2.1)]) + @test all(p ∉ s for p in [Point(1.5, 1.5, 1.5, 1.49), Point(1, 1, 2, 1.0)]) + @test s ≈ s + @test !(s ≈ Segment(Point(2, 2, 2, 2), Point(1, 1, 1, 1))) + @test !(s ≈ Segment(Point(1, 1, 2, 1), Point(0, 0, 0, 0))) + + s = Segment(cart(0, 0, 0), cart(1, 1, 1)) + @test boundary(s) == Multi([cart(0, 0, 0), cart(1, 1, 1)]) + @test perimeter(s) == zero(T) * u"m" + @test centroid(s) == cart(0.5, 0.5, 0.5) + @test Meshes.lentype(centroid(s)) == ℳ + + # unitful coordinates + x1 = T(0)u"m" + x2 = T(1)u"m" + s = Segment(Point(x1, x1, x1), Point(x2, x2, x2)) + @test boundary(s) == Multi([Point(x1, x1, x1), Point(x2, x2, x2)]) + @test perimeter(s) == 0u"m" + xm = T(0.5)u"m" + @test centroid(s) == Point(xm, xm, xm) + @test Meshes.lentype(centroid(s)) == typeof(xm) + + # CRS propagation + s = Segment(merc(0, 0), merc(1, 1)) + @test crs(s(T(0))) === crs(s) + + s = Segment(cart(0, 0), cart(1, 1)) + @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), s) == """ + Segment + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + └─ Point(x: 1.0f0 m, y: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), s) == """ + Segment + ├─ Point(x: 0.0 m, y: 0.0 m) + └─ Point(x: 1.0 m, y: 1.0 m)""" end +end - @testset "Ropes/Rings" begin - c1 = Rope(cart.([(1, 1), (2, 2)])) - c2 = Rope(cart(1, 1), cart(2, 2)) - c3 = Rope(T.((1, 1.0)), T.((2.0, 2.0))) - @test c1 == c2 == c3 - c1 = Ring(cart.([(1, 1), (2, 2)])) - c2 = Ring(cart(1, 1), cart(2, 2)) - c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) - @test c1 == c2 == c3 - - c = Rope(cart(0, 0), cart(1, 0), cart(0, 1)) - equaltest(c) - isapproxtest(c) - - c = Ring(cart(0, 0), cart(1, 0), cart(0, 1)) - equaltest(c) - isapproxtest(c) - - # circular equality - c1 = Ring(cart.([(1, 1), (2, 2), (3, 3)])) - c2 = Ring(cart.([(2, 2), (3, 3), (1, 1)])) - c3 = Ring(cart.([(3, 3), (1, 1), (2, 2)])) - @test c1 ≗ c2 ≗ c3 - - c = Rope(cart.([(1, 1), (2, 2)])) - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test vertex(c, 1) == cart(1, 1) - @test vertex(c, 2) == cart(2, 2) - c = Ring(cart.([(1, 1), (2, 2)])) - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test vertex(c, 0) == cart(2, 2) - @test vertex(c, 1) == cart(1, 1) - @test vertex(c, 2) == cart(2, 2) - @test vertex(c, 3) == cart(1, 1) - @test vertex(c, 4) == cart(2, 2) - - c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) - @test collect(segments(c)) == [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3))] - c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) - @test collect(segments(c)) == - [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3)), Segment(cart(3, 3), cart(1, 1))] - - c = Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) - @test unique(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) - @test c == Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) - unique!(c) - @test c == Rope(cart.([(1, 1), (2, 2), (3, 3)])) - - c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) - @test close(c) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) - c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) - @test open(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) - - c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) - reverse!(c) - @test c == Rope(cart.([(3, 3), (2, 2), (1, 1)])) - c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) - @test reverse(c) == Rope(cart.([(3, 3), (2, 2), (1, 1)])) - - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test angles(c) ≈ [-π / 2, -π / 2, -π / 2, -π / 2] - c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test angles(c) ≈ [-π / 2, -π / 2] - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) - @test angles(c) ≈ [-atan(2), -π / 2, +π / 2, -π / 2, -π / 2, -(π - atan(2))] - @test innerangles(c) ≈ [atan(2), π / 2, 3π / 2, π / 2, π / 2, π - atan(2)] - - c1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - c2 = Ring(vertices(c1)) - @test c1 == c2 - - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test centroid(c) == cart(0.5, 0.5) - - c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test boundary(c) == Multi(cart.([(0, 0), (0, 1)])) - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test isnothing(boundary(c)) - - # should not repeat the first vertex manually - @test_throws ArgumentError Ring(cart.([(0, 0), (0, 0)])) - @test_throws ArgumentError Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) - - # degenerate rings with 1 or 2 vertices are allowed - r = Ring(cart.([(0, 0)])) - @test isclosed(r) - @test nvertices(r) == 1 - @test collect(segments(r)) == [Segment(cart(0, 0), cart(0, 0))] - r = Ring(cart.([(0, 0), (1, 1)])) - @test isclosed(r) - @test nvertices(r) == 2 - @test collect(segments(r)) == [Segment(cart(0, 0), cart(1, 1)), Segment(cart(1, 1), cart(0, 0))] - - p1 = cart(1, 1) - p2 = cart(3, 1) - p3 = cart(1, 0) - p4 = cart(3, 0) - pts = cart.([(0, 0), (2, 2), (4, 0)]) - r = Ring(pts) - @test p1 ∈ r - @test p2 ∈ r - @test p3 ∈ r - @test p4 ∈ r - r = Rope(pts) - @test p1 ∈ r - @test p2 ∈ r - @test p3 ∉ r - @test p4 ∉ r - - # approximately equal vertices - pts = - cart.( - [ - (-48.04448403189499, -18.326530800015174) - (-48.044478457836675, -18.326503670869467) - (-48.04447845783733, -18.326503670869915) - (-48.04447835073269, -18.326503149587666) - (-48.044468448930644, -18.326490894176693) - (-48.04447208741723, -18.326486301018672) - (-48.044459173572015, -18.32646700775326) - (-48.04445616736389, -18.326461847186216) - (-48.044459897846174, -18.326466190774774) - (-48.044462696066695, -18.32646303439271) - (-48.044473299571635, -18.326478565399572) - (-48.044473299571635, -18.326478565399565) - (-48.044484052460334, -18.326494315209573) - (-48.04449288424675, -18.326504598503668) - (-48.044492356262886, -18.32650647783081) - (-48.0444943180541, -18.326509351276243) - (-48.044492458690776, -18.32651322842786) - (-48.04450917793127, -18.326524641668517) - (-48.044501408820125, -18.326551273900744) - ] - ) - r1 = Rope(pts) - r2 = Ring(pts) - ur1 = unique(r1) - ur2 = unique(r2) - @test nvertices(ur1) < nvertices(r1) - @test nvertices(ur2) < nvertices(r2) - if T === Float32 - @test nvertices(ur1) == 10 - @test nvertices(ur2) == 10 - else - @test nvertices(ur1) == 17 - @test nvertices(ur2) == 17 - end - - # issimple benchmark - r = Sphere(cart(0, 0), T(1)) |> pointify |> Ring - @test issimple(r) - @test @elapsed(issimple(r)) < 0.02 - @test @allocated(issimple(r)) < 950000 - - # CRS propagation - r = Ring(merc.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test crs(centroid(r)) === crs(r) - - ri = Ring(cart.([(1, 1), (2, 2), (3, 3)])) - ro = Rope(cart.([(1, 1), (2, 2), (3, 3)])) - @test sprint(show, ri) == "Ring((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" - @test sprint(show, ro) == "Rope((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), ri) == """ - Ring - ├─ Point(x: 1.0f0 m, y: 1.0f0 m) - ├─ Point(x: 2.0f0 m, y: 2.0f0 m) - └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" - @test sprint(show, MIME("text/plain"), ro) == """ - Rope - ├─ Point(x: 1.0f0 m, y: 1.0f0 m) - ├─ Point(x: 2.0f0 m, y: 2.0f0 m) - └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), ri) == """ - Ring - ├─ Point(x: 1.0 m, y: 1.0 m) - ├─ Point(x: 2.0 m, y: 2.0 m) - └─ Point(x: 3.0 m, y: 3.0 m)""" - @test sprint(show, MIME("text/plain"), ro) == """ - Rope - ├─ Point(x: 1.0 m, y: 1.0 m) - ├─ Point(x: 2.0 m, y: 2.0 m) - └─ Point(x: 3.0 m, y: 3.0 m)""" - end +@testitem "Chains" setup = [Setup] begin + c1 = Rope(cart.([(1, 1), (2, 2)])) + c2 = Rope(cart(1, 1), cart(2, 2)) + c3 = Rope(T.((1, 1.0)), T.((2.0, 2.0))) + @test c1 == c2 == c3 + c1 = Ring(cart.([(1, 1), (2, 2)])) + c2 = Ring(cart(1, 1), cart(2, 2)) + c3 = Ring(T.((1, 1.0)), T.((2.0, 2.0))) + @test c1 == c2 == c3 + + c = Rope(cart(0, 0), cart(1, 0), cart(0, 1)) + equaltest(c) + isapproxtest(c) + + c = Ring(cart(0, 0), cart(1, 0), cart(0, 1)) + equaltest(c) + isapproxtest(c) + + # circular equality + c1 = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + c2 = Ring(cart.([(2, 2), (3, 3), (1, 1)])) + c3 = Ring(cart.([(3, 3), (1, 1), (2, 2)])) + @test c1 ≗ c2 ≗ c3 + + c = Rope(cart.([(1, 1), (2, 2)])) + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test vertex(c, 1) == cart(1, 1) + @test vertex(c, 2) == cart(2, 2) + c = Ring(cart.([(1, 1), (2, 2)])) + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test vertex(c, 0) == cart(2, 2) + @test vertex(c, 1) == cart(1, 1) + @test vertex(c, 2) == cart(2, 2) + @test vertex(c, 3) == cart(1, 1) + @test vertex(c, 4) == cart(2, 2) + + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test collect(segments(c)) == [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3))] + c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + @test collect(segments(c)) == + [Segment(cart(1, 1), cart(2, 2)), Segment(cart(2, 2), cart(3, 3)), Segment(cart(3, 3), cart(1, 1))] + + c = Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) + @test unique(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test c == Rope(cart.([(1, 1), (2, 2), (2, 2), (3, 3)])) + unique!(c) + @test c == Rope(cart.([(1, 1), (2, 2), (3, 3)])) + + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test close(c) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) + c = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + @test open(c) == Rope(cart.([(1, 1), (2, 2), (3, 3)])) + + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + reverse!(c) + @test c == Rope(cart.([(3, 3), (2, 2), (1, 1)])) + c = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test reverse(c) == Rope(cart.([(3, 3), (2, 2), (1, 1)])) + + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test angles(c) ≈ [-π / 2, -π / 2, -π / 2, -π / 2] + c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test angles(c) ≈ [-π / 2, -π / 2] + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2)])) + @test angles(c) ≈ [-atan(2), -π / 2, +π / 2, -π / 2, -π / 2, -(π - atan(2))] + @test innerangles(c) ≈ [atan(2), π / 2, 3π / 2, π / 2, π / 2, π - atan(2)] + + c1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + c2 = Ring(vertices(c1)) + @test c1 == c2 + + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(c) == cart(0.5, 0.5) + + c = Rope(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test boundary(c) == Multi(cart.([(0, 0), (0, 1)])) + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test isnothing(boundary(c)) + + # should not repeat the first vertex manually + @test_throws ArgumentError Ring(cart.([(0, 0), (0, 0)])) + @test_throws ArgumentError Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) + + # degenerate rings with 1 or 2 vertices are allowed + r = Ring(cart.([(0, 0)])) + @test isclosed(r) + @test nvertices(r) == 1 + @test collect(segments(r)) == [Segment(cart(0, 0), cart(0, 0))] + r = Ring(cart.([(0, 0), (1, 1)])) + @test isclosed(r) + @test nvertices(r) == 2 + @test collect(segments(r)) == [Segment(cart(0, 0), cart(1, 1)), Segment(cart(1, 1), cart(0, 0))] + + p1 = cart(1, 1) + p2 = cart(3, 1) + p3 = cart(1, 0) + p4 = cart(3, 0) + pts = cart.([(0, 0), (2, 2), (4, 0)]) + r = Ring(pts) + @test p1 ∈ r + @test p2 ∈ r + @test p3 ∈ r + @test p4 ∈ r + r = Rope(pts) + @test p1 ∈ r + @test p2 ∈ r + @test p3 ∉ r + @test p4 ∉ r + + # approximately equal vertices + pts = + cart.( + [ + (-48.04448403189499, -18.326530800015174) + (-48.044478457836675, -18.326503670869467) + (-48.04447845783733, -18.326503670869915) + (-48.04447835073269, -18.326503149587666) + (-48.044468448930644, -18.326490894176693) + (-48.04447208741723, -18.326486301018672) + (-48.044459173572015, -18.32646700775326) + (-48.04445616736389, -18.326461847186216) + (-48.044459897846174, -18.326466190774774) + (-48.044462696066695, -18.32646303439271) + (-48.044473299571635, -18.326478565399572) + (-48.044473299571635, -18.326478565399565) + (-48.044484052460334, -18.326494315209573) + (-48.04449288424675, -18.326504598503668) + (-48.044492356262886, -18.32650647783081) + (-48.0444943180541, -18.326509351276243) + (-48.044492458690776, -18.32651322842786) + (-48.04450917793127, -18.326524641668517) + (-48.044501408820125, -18.326551273900744) + ] + ) + r1 = Rope(pts) + r2 = Ring(pts) + ur1 = unique(r1) + ur2 = unique(r2) + @test nvertices(ur1) < nvertices(r1) + @test nvertices(ur2) < nvertices(r2) + if T === Float32 + @test nvertices(ur1) == 10 + @test nvertices(ur2) == 10 + else + @test nvertices(ur1) == 17 + @test nvertices(ur2) == 17 end - @testset "Ngons" begin - pts = (cart(0, 0), cart(1, 0), cart(0, 1)) - tups = (T.((0, 0)), T.((1, 0)), T.((0, 1))) - @test paramdim(Ngon) == 2 - @test vertices(Ngon(pts)) == pts - @test vertices(Ngon(pts...)) == pts - @test vertices(Ngon(tups...)) == pts - @test vertices(Ngon{3}(pts)) == pts - @test vertices(Ngon{3}(pts...)) == pts - @test vertices(Ngon{3}(tups...)) == pts - - NGONS = [Triangle, Quadrangle, Pentagon, Hexagon, Heptagon, Octagon, Nonagon, Decagon] - NVERT = 3:10 - for (i, NGON) in enumerate(NGONS) - @test paramdim(NGON) == 2 - @test nvertices(NGON) == NVERT[i] - end - - # error: the number of vertices must be greater than or equal to 3 - @test_throws ArgumentError Ngon(cart(0, 0), cart(1, 1)) - @test_throws ArgumentError Ngon{2}(cart(0, 0), cart(1, 1)) - - # --------- - # TRIANGLE - # --------- - - # Triangle in 2D space - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test vertex(t, 1) == cart(0, 0) - @test vertex(t, 2) == cart(1, 0) - @test vertex(t, 3) == cart(0, 1) - @test area(t) == T(0.5) * u"m^2" - t = Triangle(cart(0, 0), cart(0, 1), cart(1, 0)) - @test area(t) == T(0.5) * u"m^2" - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - for p in cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) - @test p ∈ t - end - for p in cart.([(-1, 0), (0, -1), (0.5, 1.0)]) - @test p ∉ t - end - t = Triangle(cart(0.4, 0.4), cart(0.6, 0.4), cart(0.8, 0.4)) - @test cart(0.2, 0.4) ∉ t - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test t(T(0.0), T(0.0)) == cart(0, 0) - @test t(T(1.0), T(0.0)) == cart(1, 0) - @test t(T(0.0), T(1.0)) == cart(0, 1) - @test t(T(0.5), T(0.5)) == cart(0.5, 0.5) - @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) - @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) - @test !hasholes(t) - @test unique(t) == t - @test boundary(t) == first(rings(t)) - @test rings(t) == [Ring(cart(0, 0), cart(1, 0), cart(0, 1))] - @test convexhull(t) == t - - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - equaltest(t) - isapproxtest(t) - - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" - - # https://github.com/JuliaGeometry/Meshes.jl/issues/333 - t = Triangle((0.0f0, 0.0f0), (1.0f0, 0.0f0), (0.5f0, 1.0f0)) - @test Point(0.5f0, 0.5f0) ∈ t - @test Point(0.5e0, 0.5e0) ∈ t - - # circular equality - t1 = Triangle(T.((1, 1)), T.((2, 2)), T.((3, 3))) - t2 = Triangle(T.((2, 2)), T.((3, 3)), T.((1, 1))) - t3 = Triangle(T.((3, 3)), T.((1, 1)), T.((2, 2))) - @test t1 ≗ t2 ≗ t3 - - # point at edge of triangle - @test cart(3, 1) ∈ Triangle(cart(1, 1), cart(5, 1), cart(3, 3)) - - # test angles - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45] * u"°", atol=8 * eps(T))) - @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45] * u"°", atol=8 * eps(T))) - - # Triangle in 3D space - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - @test area(t) == T(0.5) * u"m^2" - t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) - @test area(t) > T(0.7) * u"m^2" - for p in cart.([(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)]) - @test p ∈ t - end - for p in cart.([(-1, 0, 0), (1, 2, 0), (0, 1, 2)]) - @test p ∉ t - end - t = Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test t(T(0.0), T(0.0)) == cart(0, 0, 0) - @test t(T(1.0), T(0.0)) == cart(0, 1, 0) - @test t(T(0.0), T(1.0)) == cart(0, 0, 1) - @test t(T(0.5), T(0.5)) == cart(0, 0.5, 0.5) - @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) - @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) - @test isapprox(normal(t), vector(1, 0, 0)) - @test isapprox(norm(normal(t)), oneunit(ℳ)) - t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) - @test isapprox(normal(t), vector(0, -0.7071067811865475, 0.7071067811865475)) - @test isapprox(norm(normal(t)), oneunit(ℳ)) - - # CRS propagation - t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) - @test crs(t(T(0), T(0))) === crs(t) - - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), t) == """ - Triangle - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m) - └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), t) == """ - Triangle - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m) - └─ Point(x: 0.0 m, y: 1.0 m)""" - end - - # ----------- - # QUADRANGLE - # ----------- - - # Quadrangle in 2D space - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test crs(q) <: Cartesian{NoDatum} - @test Meshes.lentype(q) == ℳ - @test vertex(q, 1) == cart(0, 0) - @test vertex(q, 2) == cart(1, 0) - @test vertex(q, 3) == cart(1, 1) - @test vertex(q, 4) == cart(0, 1) - @test area(q) == T(1) * u"m^2" - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) - @test area(q) == T(1) * u"m^2" - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) - for p in cart.([(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)]) - @test p ∈ q - end - for p in cart.([(0, 1), (1.5, 0.0)]) - @test p ∉ q - end - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test !hasholes(q) - @test unique(q) == q - @test boundary(q) == first(rings(q)) - @test rings(q) == [Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1))] - @test q(T(0), T(0)) == cart(0, 0) - @test q(T(1), T(0)) == cart(1, 0) - @test q(T(1), T(1)) == cart(1, 1) - @test q(T(0), T(1)) == cart(0, 1) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - equaltest(q) - isapproxtest(q) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test perimeter(q) ≈ T(4) * u"m" - - # Quadrangle in 3D space - q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) - @test area(q) == T(1) * u"m^2" - q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 1)) - @test area(q) > T(1) * u"m^2" - @test q(T(0), T(0)) == cart(0, 0, 0) - @test q(T(1), T(0)) == cart(1, 0, 0) - @test q(T(1), T(1)) == cart(1, 1, 0) - @test q(T(0), T(1)) == cart(0, 1, 1) - - # CRS propagation - q = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) - @test crs(q(T(0), T(0))) === crs(q) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), q) == """ - Quadrangle - ├─ Point(x: 0.0f0 m, y: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 1.0f0 m) - └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), q) == """ - Quadrangle - ├─ Point(x: 0.0 m, y: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m) - ├─ Point(x: 1.0 m, y: 1.0 m) - └─ Point(x: 0.0 m, y: 1.0 m)""" - end + # issimple benchmark + r = Sphere(cart(0, 0), T(1)) |> pointify |> Ring + @test issimple(r) + @test @elapsed(issimple(r)) < 0.02 + @test @allocated(issimple(r)) < 950000 + + # CRS propagation + r = Ring(merc.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test crs(centroid(r)) === crs(r) + + ri = Ring(cart.([(1, 1), (2, 2), (3, 3)])) + ro = Rope(cart.([(1, 1), (2, 2), (3, 3)])) + @test sprint(show, ri) == "Ring((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" + @test sprint(show, ro) == "Rope((x: 1.0 m, y: 1.0 m), (x: 2.0 m, y: 2.0 m), (x: 3.0 m, y: 3.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), ri) == """ + Ring + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" + @test sprint(show, MIME("text/plain"), ro) == """ + Rope + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + ├─ Point(x: 2.0f0 m, y: 2.0f0 m) + └─ Point(x: 3.0f0 m, y: 3.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), ri) == """ + Ring + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + └─ Point(x: 3.0 m, y: 3.0 m)""" + @test sprint(show, MIME("text/plain"), ro) == """ + Rope + ├─ Point(x: 1.0 m, y: 1.0 m) + ├─ Point(x: 2.0 m, y: 2.0 m) + └─ Point(x: 3.0 m, y: 3.0 m)""" end +end - @testset "PolyAreas" begin - @test paramdim(PolyArea) == 2 - - # equality and approximate equality - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - poly = PolyArea([outer, hole1, hole2]) - @test poly == poly - @test poly ≈ poly - @test crs(poly) <: Cartesian{NoDatum} - @test Meshes.lentype(poly) == ℳ - - p = PolyArea(cart(0, 0), cart(1, 0), cart(0, 1)) - equaltest(p) - isapproxtest(p) - - # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) - # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 - fnames = ["poly$i.line" for i in 1:5] - polys1 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] - for poly in polys1 - @test !hasholes(poly) - @test issimple(poly) - @test boundary(poly) == first(rings(poly)) - @test nvertices(poly) == 30 - @test orientation(poly) == CCW - @test unique(poly) == poly - end - - # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) - # rpg --cluster 30 --algo 2opt --format line --seed 1 --output smooth1 --smooth 2 - fnames = ["smooth$i.line" for i in 1:5] - polys2 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] - for poly in polys2 - @test !hasholes(poly) - @test issimple(poly) - @test boundary(poly) == first(rings(poly)) - @test nvertices(poly) == 120 - @test orientation(poly) == CCW - @test unique(poly) == poly - end - - # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) - # rpg --cluster 30 --algo 2opt --format line --seed 1 --output hole1 --holes 2 - fnames = ["hole$i.line" for i in 1:5] - polys3 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] - for poly in polys3 - rs = rings(poly) - @test hasholes(poly) - @test !issimple(poly) - @test boundary(poly) == Multi(rs) - @test nvertices(first(rs)) < 30 - @test all(nvertices.(rs[2:end]) .< 18) - o = orientation(poly) - @test o[1] == CCW - @test all(o[2:end] .== CW) - @test unique(poly) == poly - end - - # test bridges - for poly in [polys1; polys2; polys3] - b = poly |> Bridge() - nb = nvertices(b) - np = nvertices.(rings(poly)) - @test nb ≥ sum(np) - # orientation always works even - # in the presence of self-intersections - @test orientation(b) == CCW - end - - # test uniqueness - points = cart.([(1, 1), (2, 2), (2, 2), (3, 3)]) - poly = PolyArea(points) - unique!(poly) - @test first(rings(poly)) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) - - # approximately equal vertices - poly = PolyArea( - cart.( - [ - (-48.04448403189499, -18.326530800015174) - (-48.044478457836675, -18.326503670869467) - (-48.04447845783733, -18.326503670869915) - (-48.04447835073269, -18.326503149587666) - (-48.044468448930644, -18.326490894176693) - (-48.04447208741723, -18.326486301018672) - (-48.044459173572015, -18.32646700775326) - (-48.04445616736389, -18.326461847186216) - (-48.044459897846174, -18.326466190774774) - (-48.044462696066695, -18.32646303439271) - (-48.044473299571635, -18.326478565399572) - (-48.044473299571635, -18.326478565399565) - (-48.044484052460334, -18.326494315209573) - (-48.04449288424675, -18.326504598503668) - (-48.044492356262886, -18.32650647783081) - (-48.0444943180541, -18.326509351276243) - (-48.044492458690776, -18.32651322842786) - (-48.04450917793127, -18.326524641668517) - (-48.044501408820125, -18.326551273900744) - ] - ) - ) - upoly = unique(poly) - @test nvertices(upoly) < nvertices(poly) - if T === Float32 - @test nvertices(upoly) == 10 - else - @test nvertices(upoly) == 17 - end - - # invalid inner - outer = Ring(randpoint2(10)) - p1, p2 = randpoint2(2) - inner = Ring(p1, p1, p2) - poly = PolyArea([outer, inner]) - upoly = unique(poly) +@testitem "Ngons" setup = [Setup] begin + pts = (cart(0, 0), cart(1, 0), cart(0, 1)) + tups = (T.((0, 0)), T.((1, 0)), T.((0, 1))) + @test paramdim(Ngon) == 2 + @test vertices(Ngon(pts)) == pts + @test vertices(Ngon(pts...)) == pts + @test vertices(Ngon(tups...)) == pts + @test vertices(Ngon{3}(pts)) == pts + @test vertices(Ngon{3}(pts...)) == pts + @test vertices(Ngon{3}(tups...)) == pts + + NGONS = [Triangle, Quadrangle, Pentagon, Hexagon, Heptagon, Octagon, Nonagon, Decagon] + NVERT = 3:10 + for (i, NGON) in enumerate(NGONS) + @test paramdim(NGON) == 2 + @test nvertices(NGON) == NVERT[i] + end + + # error: the number of vertices must be greater than or equal to 3 + @test_throws ArgumentError Ngon(cart(0, 0), cart(1, 1)) + @test_throws ArgumentError Ngon{2}(cart(0, 0), cart(1, 1)) + + # --------- + # TRIANGLE + # --------- + + # Triangle in 2D space + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test vertex(t, 1) == cart(0, 0) + @test vertex(t, 2) == cart(1, 0) + @test vertex(t, 3) == cart(0, 1) + @test area(t) == T(0.5) * u"m^2" + t = Triangle(cart(0, 0), cart(0, 1), cart(1, 0)) + @test area(t) == T(0.5) * u"m^2" + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + for p in cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.0), (1.0, 0.5), (0.5, 0.5)]) + @test p ∈ t + end + for p in cart.([(-1, 0), (0, -1), (0.5, 1.0)]) + @test p ∉ t + end + t = Triangle(cart(0.4, 0.4), cart(0.6, 0.4), cart(0.8, 0.4)) + @test cart(0.2, 0.4) ∉ t + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test t(T(0.0), T(0.0)) == cart(0, 0) + @test t(T(1.0), T(0.0)) == cart(1, 0) + @test t(T(0.0), T(1.0)) == cart(0, 1) + @test t(T(0.5), T(0.5)) == cart(0.5, 0.5) + @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) + @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) + @test !hasholes(t) + @test unique(t) == t + @test boundary(t) == first(rings(t)) + @test rings(t) == [Ring(cart(0, 0), cart(1, 0), cart(0, 1))] + @test convexhull(t) == t + + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + equaltest(t) + isapproxtest(t) + + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" + + # https://github.com/JuliaGeometry/Meshes.jl/issues/333 + t = Triangle((0.0f0, 0.0f0), (1.0f0, 0.0f0), (0.5f0, 1.0f0)) + @test Point(0.5f0, 0.5f0) ∈ t + @test Point(0.5e0, 0.5e0) ∈ t + + # circular equality + t1 = Triangle(T.((1, 1)), T.((2, 2)), T.((3, 3))) + t2 = Triangle(T.((2, 2)), T.((3, 3)), T.((1, 1))) + t3 = Triangle(T.((3, 3)), T.((1, 1)), T.((2, 2))) + @test t1 ≗ t2 ≗ t3 + + # point at edge of triangle + @test cart(3, 1) ∈ Triangle(cart(1, 1), cart(5, 1), cart(3, 3)) + + # test angles + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test all(isapprox.(rad2deg.(angles(t)), T[-90, -45, -45] * u"°", atol=8 * eps(T))) + @test all(isapprox.(rad2deg.(innerangles(t)), T[90, 45, 45] * u"°", atol=8 * eps(T))) + + # Triangle in 3D space + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + @test area(t) == T(0.5) * u"m^2" + t = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) + @test area(t) > T(0.7) * u"m^2" + for p in cart.([(0, 0, 0), (1, 0, 0), (0, 1, 1), (0, 0.2, 0.2)]) + @test p ∈ t + end + for p in cart.([(-1, 0, 0), (1, 2, 0), (0, 1, 2)]) + @test p ∉ t + end + t = Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test t(T(0.0), T(0.0)) == cart(0, 0, 0) + @test t(T(1.0), T(0.0)) == cart(0, 1, 0) + @test t(T(0.0), T(1.0)) == cart(0, 0, 1) + @test t(T(0.5), T(0.5)) == cart(0, 0.5, 0.5) + @test_throws DomainError((T(-0.5), T(0.0)), "invalid barycentric coordinates for triangle.") t(T(-0.5), T(0.0)) + @test_throws DomainError((T(1), T(1)), "invalid barycentric coordinates for triangle.") t(T(1), T(1)) + @test isapprox(normal(t), vector(1, 0, 0)) + @test isapprox(norm(normal(t)), oneunit(ℳ)) + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(0, 2, 2)) + @test isapprox(normal(t), vector(0, -0.7071067811865475, 0.7071067811865475)) + @test isapprox(norm(normal(t)), oneunit(ℳ)) + + # CRS propagation + t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) + @test crs(t(T(0), T(0))) === crs(t) + + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), t) == """ + Triangle + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), t) == """ + Triangle + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" + end + + # ----------- + # QUADRANGLE + # ----------- + + # Quadrangle in 2D space + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test crs(q) <: Cartesian{NoDatum} + @test Meshes.lentype(q) == ℳ + @test vertex(q, 1) == cart(0, 0) + @test vertex(q, 2) == cart(1, 0) + @test vertex(q, 3) == cart(1, 1) + @test vertex(q, 4) == cart(0, 1) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1.5, 1.0), cart(0.5, 1.0)) + for p in cart.([(0, 0), (1, 0), (1.5, 1.0), (0.5, 1.0), (0.5, 0.5)]) + @test p ∈ q + end + for p in cart.([(0, 1), (1.5, 0.0)]) + @test p ∉ q + end + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test !hasholes(q) + @test unique(q) == q + @test boundary(q) == first(rings(q)) + @test rings(q) == [Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1))] + @test q(T(0), T(0)) == cart(0, 0) + @test q(T(1), T(0)) == cart(1, 0) + @test q(T(1), T(1)) == cart(1, 1) + @test q(T(0), T(1)) == cart(0, 1) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + equaltest(q) + isapproxtest(q) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test perimeter(q) ≈ T(4) * u"m" + + # Quadrangle in 3D space + q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + @test area(q) == T(1) * u"m^2" + q = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 1)) + @test area(q) > T(1) * u"m^2" + @test q(T(0), T(0)) == cart(0, 0, 0) + @test q(T(1), T(0)) == cart(1, 0, 0) + @test q(T(1), T(1)) == cart(1, 1, 0) + @test q(T(0), T(1)) == cart(0, 1, 1) + + # CRS propagation + q = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) + @test crs(q(T(0), T(0))) === crs(q) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), q) == """ + Quadrangle + ├─ Point(x: 0.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), q) == """ + Quadrangle + ├─ Point(x: 0.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" + end +end + +@testitem "PolyAreas" setup = [Setup] begin + @test paramdim(PolyArea) == 2 + + # equality and approximate equality + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + poly = PolyArea([outer, hole1, hole2]) + @test poly == poly + @test poly ≈ poly + @test crs(poly) <: Cartesian{NoDatum} + @test Meshes.lentype(poly) == ℳ + + p = PolyArea(cart(0, 0), cart(1, 0), cart(0, 1)) + equaltest(p) + isapproxtest(p) + + # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) + # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 + fnames = ["poly$i.line" for i in 1:5] + polys1 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] + for poly in polys1 + @test !hasholes(poly) + @test issimple(poly) + @test boundary(poly) == first(rings(poly)) + @test nvertices(poly) == 30 + @test orientation(poly) == CCW + @test unique(poly) == poly + end + + # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) + # rpg --cluster 30 --algo 2opt --format line --seed 1 --output smooth1 --smooth 2 + fnames = ["smooth$i.line" for i in 1:5] + polys2 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] + for poly in polys2 + @test !hasholes(poly) + @test issimple(poly) + @test boundary(poly) == first(rings(poly)) + @test nvertices(poly) == 120 + @test orientation(poly) == CCW + @test unique(poly) == poly + end + + # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) + # rpg --cluster 30 --algo 2opt --format line --seed 1 --output hole1 --holes 2 + fnames = ["hole$i.line" for i in 1:5] + polys3 = [readpoly(T, joinpath(datadir, fname)) for fname in fnames] + for poly in polys3 + rs = rings(poly) @test hasholes(poly) - @test !hasholes(upoly) - - # centroid - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test centroid(poly) == cart(0.5, 0.5) - - # single vertex access - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - @test vertex(poly, 1) == cart(0, 0) - @test vertex(poly, 4) == cart(0, 1) - - # point in polygonal area - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) - hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) - poly = PolyArea([outer, hole1, hole2]) - @test all(p ∈ poly for p in outer) - @test cart(0.5, 0.5) ∈ poly - @test cart(0.2, 0.6) ∈ poly - @test cart(1.5, 0.5) ∉ poly - @test cart(-0.5, 0.5) ∉ poly - @test cart(0.25, 0.25) ∉ poly - @test cart(0.75, 0.25) ∉ poly - @test cart(0.75, 0.75) ∈ poly - - # area - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) - @test area(poly) ≈ T(0.92) * u"m^2" - - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly1 = PolyArea(outer) - poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) - @test sprint(show, poly1) == "PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" - @test sprint(show, poly2) == "PolyArea(4-Ring, 4-Ring, 4-Ring)" - @test sprint(show, MIME("text/plain"), poly1) == """ - PolyArea - outer - └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))""" - @test sprint(show, MIME("text/plain"), poly2) == """ - PolyArea - outer - └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) - inner - ├─ Ring((x: 0.2 m, y: 0.2 m), ..., (x: 0.4 m, y: 0.2 m)) - └─ Ring((x: 0.6 m, y: 0.2 m), ..., (x: 0.8 m, y: 0.2 m))""" - - # should not repeat the first vertex manually - @test_throws ArgumentError PolyArea(cart.([(0, 0), (0, 0)])) - @test_throws ArgumentError PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) + @test !issimple(poly) + @test boundary(poly) == Multi(rs) + @test nvertices(first(rs)) < 30 + @test all(nvertices.(rs[2:end]) .< 18) + o = orientation(poly) + @test o[1] == CCW + @test all(o[2:end] .== CW) + @test unique(poly) == poly end - @testset "Polyhedra" begin - @test paramdim(Tetrahedron) == 3 - @test nvertices(Tetrahedron) == 4 - - t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test vertex(t, 1) == cart(0, 0, 0) - @test vertex(t, 2) == cart(1, 0, 0) - @test vertex(t, 3) == cart(0, 1, 0) - @test vertex(t, 4) == cart(0, 0, 1) - @test measure(t) == T(1 / 6) * u"m^3" - m = boundary(t) - n = normal.(m) - @test m isa Mesh - @test nvertices(m) == 4 - @test nelements(m) == 4 - @test n[1] == vector(0, 0, -1) - @test n[2] == vector(0, -1, 0) - @test n[3] == vector(-1, 0, 0) - @test all(>(T(0) * u"m"), n[4]) - @test t(T(0), T(0), T(0)) ≈ cart(0, 0, 0) - @test t(T(1), T(0), T(0)) ≈ cart(1, 0, 0) - @test t(T(0), T(1), T(0)) ≈ cart(0, 1, 0) - @test t(T(0), T(0), T(1)) ≈ cart(0, 0, 1) - @test_throws DomainError((T(1), T(1), T(1)), "invalid barycentric coordinates for tetrahedron.") t(T(1), T(1), T(1)) - - t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - equaltest(t) - isapproxtest(t) - - # CRS propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) - c3 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) - c4 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) - t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) - @test crs(t(T(0), T(0), T(0))) === crs(t) - - t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), t) == """ - Tetrahedron - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), t) == """ - Tetrahedron - ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) - └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" - end - - @test paramdim(Hexahedron) == 3 - @test nvertices(Hexahedron) == 8 - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - @test crs(h) <: Cartesian{NoDatum} - @test Meshes.lentype(h) == ℳ - @test vertex(h, 1) == cart(0, 0, 0) - @test vertex(h, 8) == cart(0, 1, 1) - @test h(T(0), T(0), T(0)) == cart(0, 0, 0) - @test h(T(0), T(0), T(1)) == cart(0, 0, 1) - @test h(T(0), T(1), T(0)) == cart(0, 1, 0) - @test h(T(0), T(1), T(1)) == cart(0, 1, 1) - @test h(T(1), T(0), T(0)) == cart(1, 0, 0) - @test h(T(1), T(0), T(1)) == cart(1, 0, 1) - @test h(T(1), T(1), T(0)) == cart(1, 1, 0) - @test h(T(1), T(1), T(1)) == cart(1, 1, 1) - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - equaltest(h) - isapproxtest(h) - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - @test volume(h) ≈ T(1 * 1 * 1) * u"m^3" - h = Hexahedron( - cart(0, 0, 0), - cart(2, 0, 0), - cart(2, 2, 0), - cart(0, 2, 0), - cart(0, 0, 2), - cart(2, 0, 2), - cart(2, 2, 2), - cart(0, 2, 2) - ) - @test volume(h) ≈ T(2 * 2 * 2) * u"m^3" - - # volume formula of a frustum of a prism is V = 1/3*H*(S₁+S₂+sqrt(S₁*S₂)) - # here we build a hexahedron which is a frustum of a prism with - # bottom area S₁= 4, top area S₂= 1, height H = 2 - h = Hexahedron( - cart(0, 0, 0), - cart(2, 0, 0), - cart(2, 2, 0), - cart(0, 2, 0), - cart(0, 0, 2), - cart(1, 0, 2), - cart(1, 1, 2), - cart(0, 1, 2) - ) - @test volume(h) ≈ T(1 / 3 * 2 * (1 + 4 + sqrt(1 * 4))) * u"m^3" - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - m = boundary(h) - @test m isa Mesh - @test nvertices(m) == 8 - @test nelements(m) == 6 - - # CRS propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) - c3 = Cartesian{WGS84Latest}(T(1), T(1), T(0)) - c4 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) - c5 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) - c6 = Cartesian{WGS84Latest}(T(1), T(0), T(1)) - c7 = Cartesian{WGS84Latest}(T(1), T(1), T(1)) - c8 = Cartesian{WGS84Latest}(T(0), T(1), T(1)) - h = Hexahedron(Point(c1), Point(c2), Point(c3), Point(c4), Point(c5), Point(c6), Point(c7), Point(c8)) - @test crs(h(T(0), T(0), T(0))) === crs(h) - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) + # test bridges + for poly in [polys1; polys2; polys3] + b = poly |> Bridge() + nb = nvertices(b) + np = nvertices.(rings(poly)) + @test nb ≥ sum(np) + # orientation always works even + # in the presence of self-intersections + @test orientation(b) == CCW + end + + # test uniqueness + points = cart.([(1, 1), (2, 2), (2, 2), (3, 3)]) + poly = PolyArea(points) + unique!(poly) + @test first(rings(poly)) == Ring(cart.([(1, 1), (2, 2), (3, 3)])) + + # approximately equal vertices + poly = PolyArea( + cart.( + [ + (-48.04448403189499, -18.326530800015174) + (-48.044478457836675, -18.326503670869467) + (-48.04447845783733, -18.326503670869915) + (-48.04447835073269, -18.326503149587666) + (-48.044468448930644, -18.326490894176693) + (-48.04447208741723, -18.326486301018672) + (-48.044459173572015, -18.32646700775326) + (-48.04445616736389, -18.326461847186216) + (-48.044459897846174, -18.326466190774774) + (-48.044462696066695, -18.32646303439271) + (-48.044473299571635, -18.326478565399572) + (-48.044473299571635, -18.326478565399565) + (-48.044484052460334, -18.326494315209573) + (-48.04449288424675, -18.326504598503668) + (-48.044492356262886, -18.32650647783081) + (-48.0444943180541, -18.326509351276243) + (-48.044492458690776, -18.32651322842786) + (-48.04450917793127, -18.326524641668517) + (-48.044501408820125, -18.326551273900744) + ] ) - @test sprint(show, h) == "Hexahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), h) == """ - Hexahedron - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) - ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) - └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), h) == """ - Hexahedron - ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) - ├─ Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) - └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" - end - - @test paramdim(Pyramid) == 3 - @test nvertices(Pyramid) == 5 - - p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test crs(p) <: Cartesian{NoDatum} - @test Meshes.lentype(p) == ℳ - @test volume(p) ≈ T(1 / 3) * u"m^3" - m = boundary(p) - @test m isa Mesh - @test nelements(m) == 5 - @test m[1] isa Quadrangle - @test m[2] isa Triangle - @test m[3] isa Triangle - @test m[4] isa Triangle - @test m[5] isa Triangle - equaltest(p) - isapproxtest(p) - - p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), p) == """ - Pyramid - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), p) == """ - Pyramid - ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) - └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" - end - - @test paramdim(Wedge) == 3 - @test nvertices(Wedge) == 6 - - w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) - @test crs(w) <: Cartesian{NoDatum} - @test Meshes.lentype(w) == ℳ - @test volume(w) ≈ T(1 / 2) * u"m^3" - m = boundary(w) - @test m isa Mesh - @test nelements(m) == 5 - @test m[1] isa Triangle - @test m[2] isa Triangle - @test m[3] isa Quadrangle - @test m[4] isa Quadrangle - @test m[5] isa Quadrangle - equaltest(w) - isapproxtest(w) - - w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) - @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), w) == """ - Wedge - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) - ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) - ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) - └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), w) == """ - Wedge - ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) - ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) - ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) - └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" - end + ) + upoly = unique(poly) + @test nvertices(upoly) < nvertices(poly) + if T === Float32 + @test nvertices(upoly) == 10 + else + @test nvertices(upoly) == 17 + end + + # invalid inner + outer = Ring(randpoint2(10)) + p1, p2 = randpoint2(2) + inner = Ring(p1, p1, p2) + poly = PolyArea([outer, inner]) + upoly = unique(poly) + @test hasholes(poly) + @test !hasholes(upoly) + + # centroid + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test centroid(poly) == cart(0.5, 0.5) + + # single vertex access + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + @test vertex(poly, 1) == cart(0, 0) + @test vertex(poly, 4) == cart(0, 1) + + # point in polygonal area + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + poly = PolyArea([outer, hole1, hole2]) + @test all(p ∈ poly for p in outer) + @test cart(0.5, 0.5) ∈ poly + @test cart(0.2, 0.6) ∈ poly + @test cart(1.5, 0.5) ∉ poly + @test cart(-0.5, 0.5) ∉ poly + @test cart(0.25, 0.25) ∉ poly + @test cart(0.75, 0.25) ∉ poly + @test cart(0.75, 0.75) ∈ poly + + # area + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) + @test area(poly) ≈ T(0.92) * u"m^2" + + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly1 = PolyArea(outer) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) + @test sprint(show, poly1) == "PolyArea((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" + @test sprint(show, poly2) == "PolyArea(4-Ring, 4-Ring, 4-Ring)" + @test sprint(show, MIME("text/plain"), poly1) == """ + PolyArea + outer + └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))""" + @test sprint(show, MIME("text/plain"), poly2) == """ + PolyArea + outer + └─ Ring((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m)) + inner + ├─ Ring((x: 0.2 m, y: 0.2 m), ..., (x: 0.4 m, y: 0.2 m)) + └─ Ring((x: 0.6 m, y: 0.2 m), ..., (x: 0.8 m, y: 0.2 m))""" + + # should not repeat the first vertex manually + @test_throws ArgumentError PolyArea(cart.([(0, 0), (0, 0)])) + @test_throws ArgumentError PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) +end + +@testitem "Polyhedra" setup = [Setup] begin + @test paramdim(Tetrahedron) == 3 + @test nvertices(Tetrahedron) == 4 + + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test vertex(t, 1) == cart(0, 0, 0) + @test vertex(t, 2) == cart(1, 0, 0) + @test vertex(t, 3) == cart(0, 1, 0) + @test vertex(t, 4) == cart(0, 0, 1) + @test measure(t) == T(1 / 6) * u"m^3" + m = boundary(t) + n = normal.(m) + @test m isa Mesh + @test nvertices(m) == 4 + @test nelements(m) == 4 + @test n[1] == vector(0, 0, -1) + @test n[2] == vector(0, -1, 0) + @test n[3] == vector(-1, 0, 0) + @test all(>(T(0) * u"m"), n[4]) + @test t(T(0), T(0), T(0)) ≈ cart(0, 0, 0) + @test t(T(1), T(0), T(0)) ≈ cart(1, 0, 0) + @test t(T(0), T(1), T(0)) ≈ cart(0, 1, 0) + @test t(T(0), T(0), T(1)) ≈ cart(0, 0, 1) + @test_throws DomainError((T(1), T(1), T(1)), "invalid barycentric coordinates for tetrahedron.") t(T(1), T(1), T(1)) + + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + equaltest(t) + isapproxtest(t) + + # CRS propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) + c3 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) + c4 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + t = Tetrahedron(Point(c1), Point(c2), Point(c3), Point(c4)) + @test crs(t(T(0), T(0), T(0))) === crs(t) + + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test sprint(show, t) == "Tetrahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), t) == """ + Tetrahedron + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), t) == """ + Tetrahedron + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" + end + + @test paramdim(Hexahedron) == 3 + @test nvertices(Hexahedron) == 8 + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + @test crs(h) <: Cartesian{NoDatum} + @test Meshes.lentype(h) == ℳ + @test vertex(h, 1) == cart(0, 0, 0) + @test vertex(h, 8) == cart(0, 1, 1) + @test h(T(0), T(0), T(0)) == cart(0, 0, 0) + @test h(T(0), T(0), T(1)) == cart(0, 0, 1) + @test h(T(0), T(1), T(0)) == cart(0, 1, 0) + @test h(T(0), T(1), T(1)) == cart(0, 1, 1) + @test h(T(1), T(0), T(0)) == cart(1, 0, 0) + @test h(T(1), T(0), T(1)) == cart(1, 0, 1) + @test h(T(1), T(1), T(0)) == cart(1, 1, 0) + @test h(T(1), T(1), T(1)) == cart(1, 1, 1) + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + equaltest(h) + isapproxtest(h) + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + @test volume(h) ≈ T(1 * 1 * 1) * u"m^3" + h = Hexahedron( + cart(0, 0, 0), + cart(2, 0, 0), + cart(2, 2, 0), + cart(0, 2, 0), + cart(0, 0, 2), + cart(2, 0, 2), + cart(2, 2, 2), + cart(0, 2, 2) + ) + @test volume(h) ≈ T(2 * 2 * 2) * u"m^3" + + # volume formula of a frustum of a prism is V = 1/3*H*(S₁+S₂+sqrt(S₁*S₂)) + # here we build a hexahedron which is a frustum of a prism with + # bottom area S₁= 4, top area S₂= 1, height H = 2 + h = Hexahedron( + cart(0, 0, 0), + cart(2, 0, 0), + cart(2, 2, 0), + cart(0, 2, 0), + cart(0, 0, 2), + cart(1, 0, 2), + cart(1, 1, 2), + cart(0, 1, 2) + ) + @test volume(h) ≈ T(1 / 3 * 2 * (1 + 4 + sqrt(1 * 4))) * u"m^3" + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + m = boundary(h) + @test m isa Mesh + @test nvertices(m) == 8 + @test nelements(m) == 6 + + # CRS propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(1), T(0), T(0)) + c3 = Cartesian{WGS84Latest}(T(1), T(1), T(0)) + c4 = Cartesian{WGS84Latest}(T(0), T(1), T(0)) + c5 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + c6 = Cartesian{WGS84Latest}(T(1), T(0), T(1)) + c7 = Cartesian{WGS84Latest}(T(1), T(1), T(1)) + c8 = Cartesian{WGS84Latest}(T(0), T(1), T(1)) + h = Hexahedron(Point(c1), Point(c2), Point(c3), Point(c4), Point(c5), Point(c6), Point(c7), Point(c8)) + @test crs(h(T(0), T(0), T(0))) === crs(h) + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + @test sprint(show, h) == "Hexahedron((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), h) == """ + Hexahedron + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), h) == """ + Hexahedron + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" + end + + @test paramdim(Pyramid) == 3 + @test nvertices(Pyramid) == 5 + + p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test crs(p) <: Cartesian{NoDatum} + @test Meshes.lentype(p) == ℳ + @test volume(p) ≈ T(1 / 3) * u"m^3" + m = boundary(p) + @test m isa Mesh + @test nelements(m) == 5 + @test m[1] isa Quadrangle + @test m[2] isa Triangle + @test m[3] isa Triangle + @test m[4] isa Triangle + @test m[5] isa Triangle + equaltest(p) + isapproxtest(p) + + p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), p) == """ + Pyramid + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), p) == """ + Pyramid + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + └─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" + end + + @test paramdim(Wedge) == 3 + @test nvertices(Wedge) == 6 + + w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) + @test crs(w) <: Cartesian{NoDatum} + @test Meshes.lentype(w) == ℳ + @test volume(w) ≈ T(1 / 2) * u"m^3" + m = boundary(w) + @test m isa Mesh + @test nelements(m) == 5 + @test m[1] isa Triangle + @test m[2] isa Triangle + @test m[3] isa Quadrangle + @test m[4] isa Quadrangle + @test m[5] isa Quadrangle + equaltest(w) + isapproxtest(w) + + w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) + @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), w) == """ + Wedge + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 0.0f0 m) + ├─ Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + ├─ Point(x: 1.0f0 m, y: 0.0f0 m, z: 1.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), w) == """ + Wedge + ├─ Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 1.0 m, z: 0.0 m) + ├─ Point(x: 0.0 m, y: 0.0 m, z: 1.0 m) + ├─ Point(x: 1.0 m, y: 0.0 m, z: 1.0 m) + └─ Point(x: 0.0 m, y: 1.0 m, z: 1.0 m)""" end end diff --git a/test/predicates.jl b/test/predicates.jl index 45ff05e97..ee4c948ce 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -1,599 +1,597 @@ -@testset "Predicates" begin - @testset "issimplex" begin - @test issimplex(Segment) - @test issimplex(Segment(cart(0, 0), cart(1, 0))) +@testitem "issimplex" setup = [Setup] begin + @test issimplex(Segment) + @test issimplex(Segment(cart(0, 0), cart(1, 0))) - @test issimplex(Triangle) - @test issimplex(Triangle(cart(0, 0), cart(1, 0), cart(0, 1))) + @test issimplex(Triangle) + @test issimplex(Triangle(cart(0, 0), cart(1, 0), cart(0, 1))) - @test issimplex(Tetrahedron) - @test issimplex(Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1))) - end + @test issimplex(Tetrahedron) + @test issimplex(Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1))) +end - @testset "isconvex" begin - # primitives - r = Ray(cart(0, 0), vector(1, 1)) - @test isconvex(r) - l = Line(cart(0, 0), cart(1, 1)) - @test isconvex(l) - p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - @test isconvex(p) - b = Box(cart(0), cart(1)) - @test isconvex(b) - b = Box(cart(0, 0), cart(1, 1)) - @test isconvex(b) - b = Box(cart(0, 0, 0), cart(1, 1, 1)) - b = Ball(cart(1, 2, 3), T(5)) - @test isconvex(b) - @test isconvex(b) - s = Sphere(cart(0, 0), T(1)) - @test !isconvex(s) - s = Sphere(cart(0, 0, 0), T(1)) - @test !isconvex(s) - d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - @test isconvex(d) - c = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - @test !isconvex(c) - b = BezierCurve(cart.([(0, 0), (1, 0), (2, 0)])) - @test isconvex(b) - b = BezierCurve(cart.([(0, 0), (1, 1), (2, 2)])) - @test isconvex(b) - b = BezierCurve(cart.([(0, 0)])) - @test isconvex(b) - b = BezierCurve(cart.([(0, 0), (1, 0)])) - @test isconvex(b) - b = BezierCurve(cart.([(0, 0), (5, 3), (-10, 3), (17, 20)])) - @test !isconvex(b) - b = BezierCurve(cart.([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)])) - @test isconvex(b) - P = typeof(cart(0, 0)) - b = BezierCurve(P[]) - @test isconvex(b) - c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) - @test isconvex(c) - c = CylinderSurface(T(2)) - @test !isconvex(c) - d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - a = cart(0, 0, 1) - c = Cone(d, a) - @test isconvex(c) - d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - a = cart(0, 0, 1) - c = ConeSurface(d, a) - @test !isconvex(c) - t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) - @test !isconvex(t) - - # polytopes - s = Segment(cart(0, 0), cart(1, 1)) - @test isconvex(s) - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test isconvex(t) - q1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - q2 = Quadrangle(cart(0.8, 0.8), cart(1, 0), cart(1, 1), cart(0, 1)) - q3 = Quadrangle(cart(0, 0), cart(0.2, 0.8), cart(1, 1), cart(0, 1)) - q4 = Quadrangle(cart(0, 0), cart(1, 0), cart(0.2, 0.2), cart(0, 1)) - q5 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.8, 0.2)) - @test isconvex(q1) - @test !isconvex(q2) - @test !isconvex(q3) - @test !isconvex(q4) - @test !isconvex(q5) - q1 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) - q2 = Quadrangle(cart(0.8, 0.8, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) - q3 = Quadrangle(cart(0, 0, 0), cart(0.2, 0.8, 0), cart(1, 1, 0), cart(0, 1, 0)) - q4 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(0.2, 0.2, 0), cart(0, 1, 0)) - q5 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0.8, 0.2, 0)) - @test isconvex(q1) - @test !isconvex(q2) - @test !isconvex(q3) - @test !isconvex(q4) - @test !isconvex(q5) - t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) - @test isconvex(t) - outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) - inner = cart.([(15, 7), (10, 12), (5, 7)]) - pent = Pentagon(outer...) - tri = Triangle(inner...) - poly = PolyArea([outer, reverse(inner)]) - multi = Multi([poly, tri]) - @test isconvex(pent) - @test isconvex(tri) - @test !isconvex(poly) - @test isconvex(multi) - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly1 = PolyArea(outer) - poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) - @test isconvex(poly1) - @test !isconvex(poly2) - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) - @test !isconvex(poly) - end +@testitem "isconvex" setup = [Setup] begin + # primitives + r = Ray(cart(0, 0), vector(1, 1)) + @test isconvex(r) + l = Line(cart(0, 0), cart(1, 1)) + @test isconvex(l) + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test isconvex(p) + b = Box(cart(0), cart(1)) + @test isconvex(b) + b = Box(cart(0, 0), cart(1, 1)) + @test isconvex(b) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + b = Ball(cart(1, 2, 3), T(5)) + @test isconvex(b) + @test isconvex(b) + s = Sphere(cart(0, 0), T(1)) + @test !isconvex(s) + s = Sphere(cart(0, 0, 0), T(1)) + @test !isconvex(s) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + @test isconvex(d) + c = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + @test !isconvex(c) + b = BezierCurve(cart.([(0, 0), (1, 0), (2, 0)])) + @test isconvex(b) + b = BezierCurve(cart.([(0, 0), (1, 1), (2, 2)])) + @test isconvex(b) + b = BezierCurve(cart.([(0, 0)])) + @test isconvex(b) + b = BezierCurve(cart.([(0, 0), (1, 0)])) + @test isconvex(b) + b = BezierCurve(cart.([(0, 0), (5, 3), (-10, 3), (17, 20)])) + @test !isconvex(b) + b = BezierCurve(cart.([(5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11)])) + @test isconvex(b) + P = typeof(cart(0, 0)) + b = BezierCurve(P[]) + @test isconvex(b) + c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) + @test isconvex(c) + c = CylinderSurface(T(2)) + @test !isconvex(c) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + a = cart(0, 0, 1) + c = Cone(d, a) + @test isconvex(c) + d = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + a = cart(0, 0, 1) + c = ConeSurface(d, a) + @test !isconvex(c) + t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) + @test !isconvex(t) + + # polytopes + s = Segment(cart(0, 0), cart(1, 1)) + @test isconvex(s) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test isconvex(t) + q1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + q2 = Quadrangle(cart(0.8, 0.8), cart(1, 0), cart(1, 1), cart(0, 1)) + q3 = Quadrangle(cart(0, 0), cart(0.2, 0.8), cart(1, 1), cart(0, 1)) + q4 = Quadrangle(cart(0, 0), cart(1, 0), cart(0.2, 0.2), cart(0, 1)) + q5 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.8, 0.2)) + @test isconvex(q1) + @test !isconvex(q2) + @test !isconvex(q3) + @test !isconvex(q4) + @test !isconvex(q5) + q1 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + q2 = Quadrangle(cart(0.8, 0.8, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + q3 = Quadrangle(cart(0, 0, 0), cart(0.2, 0.8, 0), cart(1, 1, 0), cart(0, 1, 0)) + q4 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(0.2, 0.2, 0), cart(0, 1, 0)) + q5 = Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0.8, 0.2, 0)) + @test isconvex(q1) + @test !isconvex(q2) + @test !isconvex(q3) + @test !isconvex(q4) + @test !isconvex(q5) + t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) + @test isconvex(t) + outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) + inner = cart.([(15, 7), (10, 12), (5, 7)]) + pent = Pentagon(outer...) + tri = Triangle(inner...) + poly = PolyArea([outer, reverse(inner)]) + multi = Multi([poly, tri]) + @test isconvex(pent) + @test isconvex(tri) + @test !isconvex(poly) + @test isconvex(multi) + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly1 = PolyArea(outer) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) + @test isconvex(poly1) + @test !isconvex(poly2) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1)])) + @test !isconvex(poly) +end - @testset "isparametrized" begin - # primitives - @test isparametrized(Ray) - @test isparametrized(Line) - @test isparametrized(Plane) - @test isparametrized(Box{<:𝔼}) - @test isparametrized(Ball{<:𝔼}) - @test isparametrized(Sphere{<:𝔼}) - @test isparametrized(Ellipsoid) - @test isparametrized(Disk) - @test isparametrized(Circle) - @test isparametrized(BezierCurve) - @test isparametrized(Cylinder) - @test isparametrized(CylinderSurface) - @test isparametrized(ConeSurface) - @test isparametrized(ParaboloidSurface) - @test isparametrized(Torus) - - # polytopes - @test isparametrized(Segment) - @test isparametrized(Triangle) - @test isparametrized(Quadrangle) - @test isparametrized(Hexahedron) - end +@testitem "isparametrized" setup = [Setup] begin + # primitives + @test isparametrized(Ray) + @test isparametrized(Line) + @test isparametrized(Plane) + @test isparametrized(Box{<:𝔼}) + @test isparametrized(Ball{<:𝔼}) + @test isparametrized(Sphere{<:𝔼}) + @test isparametrized(Ellipsoid) + @test isparametrized(Disk) + @test isparametrized(Circle) + @test isparametrized(BezierCurve) + @test isparametrized(Cylinder) + @test isparametrized(CylinderSurface) + @test isparametrized(ConeSurface) + @test isparametrized(ParaboloidSurface) + @test isparametrized(Torus) + + # polytopes + @test isparametrized(Segment) + @test isparametrized(Triangle) + @test isparametrized(Quadrangle) + @test isparametrized(Hexahedron) +end - @testset "isperiodic" begin - # primitives - @test isperiodic(Box{𝔼{2},Cartesian2D}) == (false, false) - @test isperiodic(Box{𝔼{3},Cartesian3D}) == (false, false, false) - @test isperiodic(Ball{𝔼{2},Cartesian2D}) == (false, true) - @test isperiodic(Ball{𝔼{3},Cartesian3D}) == (false, false, true) - @test isperiodic(Sphere{𝔼{2},Cartesian2D}) == (true,) - @test isperiodic(Sphere{𝔼{3},Cartesian3D}) == (false, true) - @test isperiodic(Ellipsoid) == (false, true) - @test isperiodic(Cylinder) == (false, true, false) - @test isperiodic(CylinderSurface) == (true, false) - @test isperiodic(ParaboloidSurface) == (false, true) - @test isperiodic(Torus) == (true, true) - - # polytopes - @test isperiodic(Segment) == (false,) - @test isperiodic(Quadrangle) == (false, false) - @test isperiodic(Hexahedron) == (false, false, false) - - @test isperiodic(cartgrid(10, 10)) == (false, false) - @test isperiodic(cartgrid(10, 10, 10)) == (false, false, false) - end +@testitem "isperiodic" setup = [Setup] begin + # primitives + @test isperiodic(Box{𝔼{2},Cartesian2D}) == (false, false) + @test isperiodic(Box{𝔼{3},Cartesian3D}) == (false, false, false) + @test isperiodic(Ball{𝔼{2},Cartesian2D}) == (false, true) + @test isperiodic(Ball{𝔼{3},Cartesian3D}) == (false, false, true) + @test isperiodic(Sphere{𝔼{2},Cartesian2D}) == (true,) + @test isperiodic(Sphere{𝔼{3},Cartesian3D}) == (false, true) + @test isperiodic(Ellipsoid) == (false, true) + @test isperiodic(Cylinder) == (false, true, false) + @test isperiodic(CylinderSurface) == (true, false) + @test isperiodic(ParaboloidSurface) == (false, true) + @test isperiodic(Torus) == (true, true) + + # polytopes + @test isperiodic(Segment) == (false,) + @test isperiodic(Quadrangle) == (false, false) + @test isperiodic(Hexahedron) == (false, false, false) + + @test isperiodic(cartgrid(10, 10)) == (false, false) + @test isperiodic(cartgrid(10, 10, 10)) == (false, false, false) +end - @testset "in" begin - h = first(cartgrid(10, 10, 10)) - @test cart(0, 0, 0) ∈ h - @test cart(0.5, 0.5, 0.5) ∈ h - @test cart(-1, 0, 0) ∉ h - @test cart(0, 2, 0) ∉ h - - outer = [merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)] - hole1 = [merc(0.2, 0.2), merc(0.4, 0.2), merc(0.4, 0.4), merc(0.2, 0.4)] - hole2 = [merc(0.6, 0.2), merc(0.8, 0.2), merc(0.8, 0.4), merc(0.6, 0.4)] - poly = PolyArea([outer, hole1, hole2]) - @test all(p ∈ poly for p in outer) - @test merc(0.5, 0.5) ∈ poly - @test merc(0.2, 0.6) ∈ poly - @test merc(1.5, 0.5) ∉ poly - @test merc(-0.5, 0.5) ∉ poly - @test merc(0.25, 0.25) ∉ poly - @test merc(0.75, 0.25) ∉ poly - @test merc(0.75, 0.75) ∈ poly - end +@testitem "in" setup = [Setup] begin + h = first(cartgrid(10, 10, 10)) + @test cart(0, 0, 0) ∈ h + @test cart(0.5, 0.5, 0.5) ∈ h + @test cart(-1, 0, 0) ∉ h + @test cart(0, 2, 0) ∉ h + + outer = [merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)] + hole1 = [merc(0.2, 0.2), merc(0.4, 0.2), merc(0.4, 0.4), merc(0.2, 0.4)] + hole2 = [merc(0.6, 0.2), merc(0.8, 0.2), merc(0.8, 0.4), merc(0.6, 0.4)] + poly = PolyArea([outer, hole1, hole2]) + @test all(p ∈ poly for p in outer) + @test merc(0.5, 0.5) ∈ poly + @test merc(0.2, 0.6) ∈ poly + @test merc(1.5, 0.5) ∉ poly + @test merc(-0.5, 0.5) ∉ poly + @test merc(0.25, 0.25) ∉ poly + @test merc(0.75, 0.25) ∉ poly + @test merc(0.75, 0.75) ∈ poly +end - @testset "issubset" begin - p = cart(0.5, 0.5) - box = Box(cart(0, 0), cart(1, 1)) - ball = Ball(cart(0, 0)) - tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test p ⊆ box - @test p ⊆ ball - @test p ⊆ tri - @test p ⊆ quad - @test p ⊆ p - @test quad ⊆ quad - - s1 = Segment(cart(0, 0), cart(1, 1)) - s2 = Segment(cart(0.5, 0.5), cart(1, 1)) - s3 = Segment(cart(0, 0), cart(0.5, 0.5)) - @test s2 ⊆ s1 - @test s3 ⊆ s1 - @test s1 ⊆ s1 - - seg = Segment(cart(0, 0), cart(1, 1)) - box = Box(cart(0, 0), cart(1, 1)) - ball = Ball(cart(0, 0)) - @test seg ⊆ box - @test !(seg ⊆ ball) - - t1 = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - t2 = Triangle(cart(0, 0), cart(1, 0), cart(0.8, 0.8)) - t3 = Triangle(cart(0, 0), cart(1, 0), cart(1.1, 1.1)) - @test t2 ⊆ t1 - @test !(t3 ⊆ t1) - - tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - box = Box(cart(0, 0), cart(1, 1)) - ball = Ball(cart(0, 0)) - quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - pent = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 1.5), cart(0, 1)) - poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test tri ⊆ quad - @test !(quad ⊆ tri) - @test tri ⊆ box - @test !(box ⊆ tri) - @test !(tri ⊆ ball) - @test !(ball ⊆ tri) - @test tri ⊆ pent - @test !(pent ⊆ tri) - @test quad ⊆ pent - @test !(pent ⊆ quad) - @test tri ⊆ poly - @test !(poly ⊆ tri) - @test quad ⊆ poly - @test poly ⊆ quad - - quad1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - quad2 = Quadrangle(cart(0, 0), cart(1.1, 0), cart(1, 1), cart(0, 1)) - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - multi = Multi([poly]) - @test quad1 ⊆ poly - @test !(quad2 ⊆ poly) - @test quad1 ⊆ multi - @test !(quad2 ⊆ multi) - - p1 = cart(-1.0, 0.0) - p2 = cart(0.0, 0.0) - p3 = cart(1.0, 0.0) - l1 = Line(p1, p3) - l2 = Line(p2, p3) - @test l1 ⊆ l2 - @test l2 ⊆ l1 - @test l1 ⊆ l1 - @test l2 ⊆ l2 - - outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) - inner = cart.([(15, 7), (10, 12), (5, 7)]) - pent = Pentagon(outer...) - tri = Triangle(inner...) - poly1 = PolyArea(outer) - poly2 = PolyArea([outer, reverse(inner)]) - multi = Multi([poly2, tri]) - @test tri ⊆ pent - @test tri ⊆ poly1 - @test tri ⊈ poly2 - @test tri ⊆ multi - @test pent ⊆ poly1 - @test pent ⊈ poly2 - @test pent ⊆ multi - - poly1 = PolyArea(cart.([(-2, 8), (-3, 4), (2, -2), (13, -2), (16, 1), (16, 8), (11, 11), (4, 12)])) - poly2 = PolyArea(cart.([(9, 0), (11, 4), (10, 7), (4, 7), (1, 6), (3, 4), (1, 2), (3, 0)])) - poly3 = PolyArea(cart.([(12, 1), (14, 4), (12, 8), (3, 8), (4, 4), (3, 2)])) - poly4 = PolyArea(cart.([(10, 4), (9, 6), (5, 6), (5, 4), (8, 2)])) - poly5 = PolyArea(cart.([(10, 9), (10, 10), (6, 11), (3, 9)])) - @test poly2 ⊆ poly1 - @test poly3 ⊆ poly1 - @test poly4 ⊆ poly1 - @test poly5 ⊆ poly1 - @test poly4 ⊆ poly2 - @test poly4 ⊆ poly3 - @test poly5 ⊈ poly2 - @test poly5 ⊈ poly3 - end +@testitem "issubset" setup = [Setup] begin + p = cart(0.5, 0.5) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test p ⊆ box + @test p ⊆ ball + @test p ⊆ tri + @test p ⊆ quad + @test p ⊆ p + @test quad ⊆ quad + + s1 = Segment(cart(0, 0), cart(1, 1)) + s2 = Segment(cart(0.5, 0.5), cart(1, 1)) + s3 = Segment(cart(0, 0), cart(0.5, 0.5)) + @test s2 ⊆ s1 + @test s3 ⊆ s1 + @test s1 ⊆ s1 + + seg = Segment(cart(0, 0), cart(1, 1)) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) + @test seg ⊆ box + @test !(seg ⊆ ball) + + t1 = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + t2 = Triangle(cart(0, 0), cart(1, 0), cart(0.8, 0.8)) + t3 = Triangle(cart(0, 0), cart(1, 0), cart(1.1, 1.1)) + @test t2 ⊆ t1 + @test !(t3 ⊆ t1) + + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + box = Box(cart(0, 0), cart(1, 1)) + ball = Ball(cart(0, 0)) + quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + pent = Pentagon(cart(0, 0), cart(1, 0), cart(1, 1), cart(0.5, 1.5), cart(0, 1)) + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test tri ⊆ quad + @test !(quad ⊆ tri) + @test tri ⊆ box + @test !(box ⊆ tri) + @test !(tri ⊆ ball) + @test !(ball ⊆ tri) + @test tri ⊆ pent + @test !(pent ⊆ tri) + @test quad ⊆ pent + @test !(pent ⊆ quad) + @test tri ⊆ poly + @test !(poly ⊆ tri) + @test quad ⊆ poly + @test poly ⊆ quad + + quad1 = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + quad2 = Quadrangle(cart(0, 0), cart(1.1, 0), cart(1, 1), cart(0, 1)) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + multi = Multi([poly]) + @test quad1 ⊆ poly + @test !(quad2 ⊆ poly) + @test quad1 ⊆ multi + @test !(quad2 ⊆ multi) + + p1 = cart(-1.0, 0.0) + p2 = cart(0.0, 0.0) + p3 = cart(1.0, 0.0) + l1 = Line(p1, p3) + l2 = Line(p2, p3) + @test l1 ⊆ l2 + @test l2 ⊆ l1 + @test l1 ⊆ l1 + @test l2 ⊆ l2 + + outer = cart.([(14, 1), (18, 10), (10, 16), (2, 10), (6, 1)]) + inner = cart.([(15, 7), (10, 12), (5, 7)]) + pent = Pentagon(outer...) + tri = Triangle(inner...) + poly1 = PolyArea(outer) + poly2 = PolyArea([outer, reverse(inner)]) + multi = Multi([poly2, tri]) + @test tri ⊆ pent + @test tri ⊆ poly1 + @test tri ⊈ poly2 + @test tri ⊆ multi + @test pent ⊆ poly1 + @test pent ⊈ poly2 + @test pent ⊆ multi + + poly1 = PolyArea(cart.([(-2, 8), (-3, 4), (2, -2), (13, -2), (16, 1), (16, 8), (11, 11), (4, 12)])) + poly2 = PolyArea(cart.([(9, 0), (11, 4), (10, 7), (4, 7), (1, 6), (3, 4), (1, 2), (3, 0)])) + poly3 = PolyArea(cart.([(12, 1), (14, 4), (12, 8), (3, 8), (4, 4), (3, 2)])) + poly4 = PolyArea(cart.([(10, 4), (9, 6), (5, 6), (5, 4), (8, 2)])) + poly5 = PolyArea(cart.([(10, 9), (10, 10), (6, 11), (3, 9)])) + @test poly2 ⊆ poly1 + @test poly3 ⊆ poly1 + @test poly4 ⊆ poly1 + @test poly5 ⊆ poly1 + @test poly4 ⊆ poly2 + @test poly4 ⊆ poly3 + @test poly5 ⊈ poly2 + @test poly5 ⊈ poly3 +end - @testset "intersects" begin - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - q = Quadrangle(cart(1, 1), cart(2, 1), cart(2, 2), cart(1, 2)) - @test intersects(t, t) - @test intersects(q, q) - @test !intersects(t, q) - @test !intersects(q, t) - - t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) - q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) - @test intersects(t, t) - @test intersects(q, q) - @test intersects(t, q) - @test intersects(q, t) - - t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) - q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) - m = Multi([t, q]) - @test intersects(m, t) - @test intersects(t, m) - @test intersects(m, q) - @test intersects(q, m) - @test intersects(m, m) - - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - b = Ball(cart(0, 0), T(1)) - @test intersects(t, t) - @test intersects(b, b) - @test intersects(t, b) - @test intersects(b, t) - - t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) - b = Ball(cart(0, 0), T(1)) - @test intersects(t, t) - @test intersects(b, b) - @test intersects(t, b) - @test intersects(b, t) - - t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) - b = Ball(cart(-0.01, 0), T(1)) - @test intersects(t, t) - @test intersects(b, b) - @test !intersects(t, b) - @test !intersects(b, t) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/250 - t1 = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) - t2 = Triangle(cart(1, 0, 0), cart(3, 0, 0), cart(2, 2, 0)) - t3 = Triangle(cart(3, 0, 0), cart(5, 0, 0), cart(4, 2, 0)) - @test intersects(t1, t2) - @test intersects(t2, t3) - @test !intersects(t1, t3) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/639 - r = Ray(cart(0.41169768366272996, 0.8990554132423699), vector(0.47249211625247445, 0.2523149692768657)) - b = Box(cart(1.0, 1.0), cart(5.0, 2.0)) - @test intersects(r, b) - @test intersects(b, r) - - t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) - r1 = Ray(cart(1, 1, 1), vector(0, 0, -1)) - r2 = Ray(cart(1, 1, 1), vector(0, 0, 1)) - @test intersects(r1, t) - @test intersects(t, r1) - @test !intersects(r2, t) - @test !intersects(t, r2) - - r = Ray(cart(0, 0), vector(1, 0)) - s1 = Sphere(cart(3, 0), T(1)) - s2 = Sphere(cart(0, 3), T(1)) - @test intersects(r, s1) - @test !intersects(r, s2) - - # result doesn't change under translation - t1 = Translate(T(10), T(0)) - t2 = Translate(T(0), T(10)) - t3 = Translate(T(-10), T(0)) - t4 = Translate(T(0), T(-10)) - for t in [t1, t2, t3, t4] - @test intersects(t(r), t(s1)) - @test !intersects(t(r), t(s2)) - end - - # result doesn't change under rotation - r1 = Rotate(Angle2d(T(π / 2))) - r2 = Rotate(Angle2d(T(-π / 2))) - r3 = Rotate(Angle2d(T(π))) - r4 = Rotate(Angle2d(T(-π))) - for t in [r1, r2, r3, r4] - @test intersects(t(r), t(s1)) - @test !intersects(t(r), t(s2)) - end - - r = Ray(cart(0, 0), vector(1, 0)) - s = Sphere(cart(floatmax(Float32) / 2, 0), 1) - @test intersects(r, s) - - r = Ray(cart(0, 0, 0), vector(1, 0, 0)) - s1 = Sphere(cart(5, 0, 1 - eps(T(1))), T(1)) - s2 = Sphere(cart(5, 0, 1 + eps(T(1))), T(1)) - @test intersects(r, s1) - @test !intersects(r, s2) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/635 - q1 = Quadrangle(cart(4.0, 4.0, 0.0), cart(3.0, 3.0, 2.0), cart(3.0, 1.0, 2.0), cart(4.0, 0.0, 0.0)) - q2 = Quadrangle(cart(3.6, 3.0, 1.0), cart(5.6, 3.0, 1.0), cart(5.6, 1.0, 1.0), cart(3.6, 1.0, 1.0)) - q3 = Quadrangle(cart(3.6, 1.0, 1.0), cart(5.6, 1.0, 1.0), cart(5.6, -1.0, 1.0), cart(3.6, -1.0, 1.0)) - q4 = Quadrangle(cart(2.1, 1.0, 1.0), cart(4.1, 1.0, 1.0), cart(4.1, -1.0, 1.0), cart(2.1, -1.0, 1.0)) - @test !intersects(q1, q2) - @test !intersects(q1, q3) - @test intersects(q1, q1) - @test intersects(q1, q4) - - h1 = Tetrahedron(cart(1, 1, 0), cart(4, 4, 0), cart(2.5, 2.5, 1.5), cart(1, 3, 2)) - h2 = Tetrahedron(cart(-1.0, 2.0, 1.0), cart(2.0, 1.0, 1.0), cart(-1.0, 4.0, 0.0), cart(0.5, 2.5, 1.5)) - h3 = Tetrahedron(cart(-1.3, 2.0, 1.0), cart(1.7, 1.0, 1.0), cart(-1.3, 4.0, 0.0), cart(0.2, 2.5, 1.5)) - @test intersects(h1, h2) - @test !intersects(h1, h3) - - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly1 = PolyArea(outer) - poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) - ball1 = Ball(cart(0.5, 0.5), T(0.05)) - ball2 = Ball(cart(0.3, 0.3), T(0.05)) - ball3 = Ball(cart(0.7, 0.3), T(0.05)) - ball4 = Ball(cart(0.3, 0.3), T(0.15)) - @test intersects(poly1, poly1) - @test intersects(poly2, poly2) - @test intersects(poly1, poly2) - @test intersects(poly2, poly1) - @test intersects(poly1, ball1) - @test intersects(poly2, ball1) - @test intersects(poly1, ball2) - @test !intersects(poly2, ball2) - @test intersects(poly1, ball3) - @test !intersects(poly2, ball3) - @test intersects(poly1, ball4) - @test intersects(poly2, ball4) - mesh1 = discretize(poly1, DehnTriangulation()) - mesh2 = discretize(poly2, DehnTriangulation()) - @test intersects(mesh1, mesh1) - @test intersects(mesh2, mesh2) - @test intersects(mesh1, mesh2) - @test intersects(mesh2, mesh1) - - p = cart(0.5, 0.5) - ball = Ball(cart(0, 0), T(1)) - @test intersects(p, ball) - @test intersects(ball, p) - @test intersects(p, p) - @test !intersects(p, p + vector(1, 1)) - - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - box = Box(cart(0, 0), cart(2, 2)) - @test intersects(poly, box) - - b1 = Box(cart(0, 0), cart(2, 2)) - b2 = Box(cart(2, 0), cart(4, 2)) - p1 = cart(1, 1) - p2 = cart(3, 1) - m = Multi([b1, b2]) - @test intersects(p1, b1) - @test !intersects(p2, b1) - @test intersects(p2, b2) - @test !intersects(p1, b2) - @test intersects(m, p1) - @test intersects(p1, m) - @test intersects(m, p2) - @test intersects(p2, m) - - s1 = Segment(cart(0, 0), cart(4, 4)) - s2 = Segment(cart(4, 0), cart(0, 4)) - s3 = Segment(cart(2, 0), cart(4, 2)) - @test intersects(s1, s2) - @test intersects(s2, s3) - @test !intersects(s1, s3) - - s1 = Segment(cart(4, 0), cart(0, 4)) - s2 = Segment(cart(4, 0), cart(8, 4)) - s3 = Segment(cart(0, 8), cart(8, 8)) - r1 = Rope(cart.([(0, 0), (4, 4), (8, 0)])) - r2 = Ring(cart.([(0, 2), (4, 6), (8, 2)])) - @test intersects(s1, r1) - @test intersects(s2, r1) - @test !intersects(s3, r1) - @test intersects(s1, r2) - @test intersects(s2, r2) - @test !intersects(s3, r2) - @test intersects(r1, r2) - - r1 = Rope(cart.([(0, 0), (2, 2), (4, 0)])) - r2 = Rope(cart.([(3, 0), (5, 2), (7, 0)])) - r3 = Rope(cart.([(6, 0), (8, 2), (10, 0)])) - @test intersects(r1, r2) - @test intersects(r2, r3) - @test !intersects(r1, r3) - - r1 = Ring(cart.([(0, 0), (2, 2), (4, 0)])) - r2 = Ring(cart.([(3, 0), (5, 2), (7, 0)])) - r3 = Ring(cart.([(6, 0), (8, 2), (10, 0)])) - @test intersects(r1, r2) - @test intersects(r2, r3) - @test !intersects(r1, r3) - - t = Triangle(cart(3, 1), cart(7, 5), cart(11, 1)) - q = Quadrangle(cart(2, 0), cart(2, 7), cart(12, 7), cart(12, 0)) - b = Box(cart(2, 0), cart(12, 7)) - s1 = Segment(cart(5, 2), cart(9, 2)) - s2 = Segment(cart(0, 3), cart(5, 3)) - s3 = Segment(cart(4, 4), cart(10, 4)) - s4 = Segment(cart(1, 6), cart(13, 6)) - s5 = Segment(cart(0, 9), cart(14, 9)) - r1 = Ring(cart.([(1, 2), (7, 8), (13, 2)])) - r2 = Rope(cart.([(1, 2), (7, 8), (13, 2)])) - @test intersects(s1, t) - @test intersects(s2, t) - @test intersects(s3, t) - @test !intersects(s4, t) - @test !intersects(s5, t) - @test intersects(s1, q) - @test intersects(s2, q) - @test intersects(s3, q) - @test intersects(s4, q) - @test !intersects(s5, q) - @test intersects(s1, b) - @test intersects(s2, b) - @test intersects(s3, b) - @test intersects(s4, b) - @test !intersects(s5, b) - @test intersects(r1, t) - @test !intersects(r2, t) - @test intersects(r1, q) - @test intersects(r2, q) - @test intersects(r1, b) - @test intersects(r2, b) - - # performance test - b1 = Box(cart(0, 0), cart(3, 3)) - b2 = Box(cart(2, 2), cart(5, 5)) - @test intersects(b1, b2) - @test intersects(b2, b1) - @test @elapsed(intersects(b1, b2)) < 5e-5 - @test @allocated(intersects(b1, b2)) < 100 - - # partial application - points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - poly = PolyArea(points) - box = Box(cart(0, 0), cart(2, 2)) - @test intersects(box)(poly) - @test all(intersects(box), points) - - # method ambiguities - p = cart(3, 1) - ring = Ring(cart.([(0, 0), (2, 2), (4, 0)])) - rope = Rope(cart.([(2, 0), (4, 2), (6, 0)])) - seg = Segment(cart(0, 1), cart(6, 1)) - multi = Multi([ring]) - @test intersects(p, ring) - @test intersects(p, rope) - @test intersects(p, seg) - @test intersects(p, multi) - @test intersects(ring, multi) - @test intersects(rope, multi) - @test intersects(seg, multi) +@testitem "intersects" setup = [Setup] begin + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + q = Quadrangle(cart(1, 1), cart(2, 1), cart(2, 2), cart(1, 2)) + @test intersects(t, t) + @test intersects(q, q) + @test !intersects(t, q) + @test !intersects(q, t) + + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) + @test intersects(t, t) + @test intersects(q, q) + @test intersects(t, q) + @test intersects(q, t) + + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + q = Quadrangle(cart(1.3, 0.5), cart(2.3, 0.5), cart(2.3, 1.5), cart(1.3, 1.5)) + m = Multi([t, q]) + @test intersects(m, t) + @test intersects(t, m) + @test intersects(m, q) + @test intersects(q, m) + @test intersects(m, m) + + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + b = Ball(cart(0, 0), T(1)) + @test intersects(t, t) + @test intersects(b, b) + @test intersects(t, b) + @test intersects(b, t) + + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + b = Ball(cart(0, 0), T(1)) + @test intersects(t, t) + @test intersects(b, b) + @test intersects(t, b) + @test intersects(b, t) + + t = Triangle(cart(1, 0), cart(2, 0), cart(1, 1)) + b = Ball(cart(-0.01, 0), T(1)) + @test intersects(t, t) + @test intersects(b, b) + @test !intersects(t, b) + @test !intersects(b, t) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/250 + t1 = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) + t2 = Triangle(cart(1, 0, 0), cart(3, 0, 0), cart(2, 2, 0)) + t3 = Triangle(cart(3, 0, 0), cart(5, 0, 0), cart(4, 2, 0)) + @test intersects(t1, t2) + @test intersects(t2, t3) + @test !intersects(t1, t3) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/639 + r = Ray(cart(0.41169768366272996, 0.8990554132423699), vector(0.47249211625247445, 0.2523149692768657)) + b = Box(cart(1.0, 1.0), cart(5.0, 2.0)) + @test intersects(r, b) + @test intersects(b, r) + + t = Triangle(cart(0, 0, 0), cart(2, 0, 0), cart(1, 2, 0)) + r1 = Ray(cart(1, 1, 1), vector(0, 0, -1)) + r2 = Ray(cart(1, 1, 1), vector(0, 0, 1)) + @test intersects(r1, t) + @test intersects(t, r1) + @test !intersects(r2, t) + @test !intersects(t, r2) + + r = Ray(cart(0, 0), vector(1, 0)) + s1 = Sphere(cart(3, 0), T(1)) + s2 = Sphere(cart(0, 3), T(1)) + @test intersects(r, s1) + @test !intersects(r, s2) + + # result doesn't change under translation + t1 = Translate(T(10), T(0)) + t2 = Translate(T(0), T(10)) + t3 = Translate(T(-10), T(0)) + t4 = Translate(T(0), T(-10)) + for t in [t1, t2, t3, t4] + @test intersects(t(r), t(s1)) + @test !intersects(t(r), t(s2)) end - @testset "ordering" begin - # lexicographical order - @test cart(0, 0) < cart(1, 1) - @test cart(0, 0) < cart(0, 1) - @test cart(1, 0) < cart(1, 1) - @test !(cart(1, 0) < cart(1, 0)) - @test !(cart(1, 0) < cart(0, 0)) - @test cart(1, 1) > cart(0, 0) - @test cart(0, 1) > cart(0, 0) - @test cart(1, 1) > cart(1, 0) - @test cart(1, 0) ≥ cart(1, 0) - @test cart(1, 0) ≥ cart(0, 0) - @test cart(0, 0) ≤ cart(0, 0) - - # product order - @test cart(0, 0) ≺ cart(1, 1) - @test !(cart(0, 0) ≺ cart(0, 1)) - @test !(cart(1, 0) ≺ cart(1, 1)) - @test !(cart(1, 0) ≺ cart(1, 0)) - @test !(cart(1, 0) ≺ cart(0, 0)) - @test cart(1, 1) ≻ cart(0, 0) - @test !(cart(0, 1) ≻ cart(0, 0)) - @test !(cart(1, 1) ≻ cart(1, 0)) - @test cart(1, 0) ⪰ cart(1, 0) - @test cart(1, 0) ⪰ cart(0, 0) - @test cart(0, 0) ⪯ cart(0, 0) - - # product order - @test cart(1, 1) ⪯ cart(1, 1) - @test !(cart(1, 1) ≺ cart(1, 1)) - @test cart(1, 2) ⪯ cart(3, 4) - @test cart(1, 2) ≺ cart(3, 4) - @test cart(1, 1) ⪰ cart(1, 1) - @test !(cart(1, 1) ≻ cart(1, 1)) - @test cart(3, 4) ⪰ cart(1, 2) - @test cart(3, 4) ≻ cart(1, 2) + # result doesn't change under rotation + r1 = Rotate(Angle2d(T(π / 2))) + r2 = Rotate(Angle2d(T(-π / 2))) + r3 = Rotate(Angle2d(T(π))) + r4 = Rotate(Angle2d(T(-π))) + for t in [r1, r2, r3, r4] + @test intersects(t(r), t(s1)) + @test !intersects(t(r), t(s2)) end - @testset "iscollinear" begin - @test iscollinear(cart(0, 0), cart(1, 1), cart(2, 2)) - end + r = Ray(cart(0, 0), vector(1, 0)) + s = Sphere(cart(floatmax(Float32) / 2, 0), 1) + @test intersects(r, s) + + r = Ray(cart(0, 0, 0), vector(1, 0, 0)) + s1 = Sphere(cart(5, 0, 1 - eps(T(1))), T(1)) + s2 = Sphere(cart(5, 0, 1 + eps(T(1))), T(1)) + @test intersects(r, s1) + @test !intersects(r, s2) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/635 + q1 = Quadrangle(cart(4.0, 4.0, 0.0), cart(3.0, 3.0, 2.0), cart(3.0, 1.0, 2.0), cart(4.0, 0.0, 0.0)) + q2 = Quadrangle(cart(3.6, 3.0, 1.0), cart(5.6, 3.0, 1.0), cart(5.6, 1.0, 1.0), cart(3.6, 1.0, 1.0)) + q3 = Quadrangle(cart(3.6, 1.0, 1.0), cart(5.6, 1.0, 1.0), cart(5.6, -1.0, 1.0), cart(3.6, -1.0, 1.0)) + q4 = Quadrangle(cart(2.1, 1.0, 1.0), cart(4.1, 1.0, 1.0), cart(4.1, -1.0, 1.0), cart(2.1, -1.0, 1.0)) + @test !intersects(q1, q2) + @test !intersects(q1, q3) + @test intersects(q1, q1) + @test intersects(q1, q4) + + h1 = Tetrahedron(cart(1, 1, 0), cart(4, 4, 0), cart(2.5, 2.5, 1.5), cart(1, 3, 2)) + h2 = Tetrahedron(cart(-1.0, 2.0, 1.0), cart(2.0, 1.0, 1.0), cart(-1.0, 4.0, 0.0), cart(0.5, 2.5, 1.5)) + h3 = Tetrahedron(cart(-1.3, 2.0, 1.0), cart(1.7, 1.0, 1.0), cart(-1.3, 4.0, 0.0), cart(0.2, 2.5, 1.5)) + @test intersects(h1, h2) + @test !intersects(h1, h3) + + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly1 = PolyArea(outer) + poly2 = PolyArea([outer, reverse(hole1), reverse(hole2)]) + ball1 = Ball(cart(0.5, 0.5), T(0.05)) + ball2 = Ball(cart(0.3, 0.3), T(0.05)) + ball3 = Ball(cart(0.7, 0.3), T(0.05)) + ball4 = Ball(cart(0.3, 0.3), T(0.15)) + @test intersects(poly1, poly1) + @test intersects(poly2, poly2) + @test intersects(poly1, poly2) + @test intersects(poly2, poly1) + @test intersects(poly1, ball1) + @test intersects(poly2, ball1) + @test intersects(poly1, ball2) + @test !intersects(poly2, ball2) + @test intersects(poly1, ball3) + @test !intersects(poly2, ball3) + @test intersects(poly1, ball4) + @test intersects(poly2, ball4) + mesh1 = discretize(poly1, DehnTriangulation()) + mesh2 = discretize(poly2, DehnTriangulation()) + @test intersects(mesh1, mesh1) + @test intersects(mesh2, mesh2) + @test intersects(mesh1, mesh2) + @test intersects(mesh2, mesh1) + + p = cart(0.5, 0.5) + ball = Ball(cart(0, 0), T(1)) + @test intersects(p, ball) + @test intersects(ball, p) + @test intersects(p, p) + @test !intersects(p, p + vector(1, 1)) + + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + box = Box(cart(0, 0), cart(2, 2)) + @test intersects(poly, box) + + b1 = Box(cart(0, 0), cart(2, 2)) + b2 = Box(cart(2, 0), cart(4, 2)) + p1 = cart(1, 1) + p2 = cart(3, 1) + m = Multi([b1, b2]) + @test intersects(p1, b1) + @test !intersects(p2, b1) + @test intersects(p2, b2) + @test !intersects(p1, b2) + @test intersects(m, p1) + @test intersects(p1, m) + @test intersects(m, p2) + @test intersects(p2, m) + + s1 = Segment(cart(0, 0), cart(4, 4)) + s2 = Segment(cart(4, 0), cart(0, 4)) + s3 = Segment(cart(2, 0), cart(4, 2)) + @test intersects(s1, s2) + @test intersects(s2, s3) + @test !intersects(s1, s3) + + s1 = Segment(cart(4, 0), cart(0, 4)) + s2 = Segment(cart(4, 0), cart(8, 4)) + s3 = Segment(cart(0, 8), cart(8, 8)) + r1 = Rope(cart.([(0, 0), (4, 4), (8, 0)])) + r2 = Ring(cart.([(0, 2), (4, 6), (8, 2)])) + @test intersects(s1, r1) + @test intersects(s2, r1) + @test !intersects(s3, r1) + @test intersects(s1, r2) + @test intersects(s2, r2) + @test !intersects(s3, r2) + @test intersects(r1, r2) + + r1 = Rope(cart.([(0, 0), (2, 2), (4, 0)])) + r2 = Rope(cart.([(3, 0), (5, 2), (7, 0)])) + r3 = Rope(cart.([(6, 0), (8, 2), (10, 0)])) + @test intersects(r1, r2) + @test intersects(r2, r3) + @test !intersects(r1, r3) + + r1 = Ring(cart.([(0, 0), (2, 2), (4, 0)])) + r2 = Ring(cart.([(3, 0), (5, 2), (7, 0)])) + r3 = Ring(cart.([(6, 0), (8, 2), (10, 0)])) + @test intersects(r1, r2) + @test intersects(r2, r3) + @test !intersects(r1, r3) + + t = Triangle(cart(3, 1), cart(7, 5), cart(11, 1)) + q = Quadrangle(cart(2, 0), cart(2, 7), cart(12, 7), cart(12, 0)) + b = Box(cart(2, 0), cart(12, 7)) + s1 = Segment(cart(5, 2), cart(9, 2)) + s2 = Segment(cart(0, 3), cart(5, 3)) + s3 = Segment(cart(4, 4), cart(10, 4)) + s4 = Segment(cart(1, 6), cart(13, 6)) + s5 = Segment(cart(0, 9), cart(14, 9)) + r1 = Ring(cart.([(1, 2), (7, 8), (13, 2)])) + r2 = Rope(cart.([(1, 2), (7, 8), (13, 2)])) + @test intersects(s1, t) + @test intersects(s2, t) + @test intersects(s3, t) + @test !intersects(s4, t) + @test !intersects(s5, t) + @test intersects(s1, q) + @test intersects(s2, q) + @test intersects(s3, q) + @test intersects(s4, q) + @test !intersects(s5, q) + @test intersects(s1, b) + @test intersects(s2, b) + @test intersects(s3, b) + @test intersects(s4, b) + @test !intersects(s5, b) + @test intersects(r1, t) + @test !intersects(r2, t) + @test intersects(r1, q) + @test intersects(r2, q) + @test intersects(r1, b) + @test intersects(r2, b) + + # performance test + b1 = Box(cart(0, 0), cart(3, 3)) + b2 = Box(cart(2, 2), cart(5, 5)) + @test intersects(b1, b2) + @test intersects(b2, b1) + @test @elapsed(intersects(b1, b2)) < 5e-5 + @test @allocated(intersects(b1, b2)) < 100 + + # partial application + points = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + poly = PolyArea(points) + box = Box(cart(0, 0), cart(2, 2)) + @test intersects(box)(poly) + @test all(intersects(box), points) + + # method ambiguities + p = cart(3, 1) + ring = Ring(cart.([(0, 0), (2, 2), (4, 0)])) + rope = Rope(cart.([(2, 0), (4, 2), (6, 0)])) + seg = Segment(cart(0, 1), cart(6, 1)) + multi = Multi([ring]) + @test intersects(p, ring) + @test intersects(p, rope) + @test intersects(p, seg) + @test intersects(p, multi) + @test intersects(ring, multi) + @test intersects(rope, multi) + @test intersects(seg, multi) +end - @testset "iscoplanar" begin - @test iscoplanar(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) - end +@testitem "ordering" setup = [Setup] begin + # lexicographical order + @test cart(0, 0) < cart(1, 1) + @test cart(0, 0) < cart(0, 1) + @test cart(1, 0) < cart(1, 1) + @test !(cart(1, 0) < cart(1, 0)) + @test !(cart(1, 0) < cart(0, 0)) + @test cart(1, 1) > cart(0, 0) + @test cart(0, 1) > cart(0, 0) + @test cart(1, 1) > cart(1, 0) + @test cart(1, 0) ≥ cart(1, 0) + @test cart(1, 0) ≥ cart(0, 0) + @test cart(0, 0) ≤ cart(0, 0) + + # product order + @test cart(0, 0) ≺ cart(1, 1) + @test !(cart(0, 0) ≺ cart(0, 1)) + @test !(cart(1, 0) ≺ cart(1, 1)) + @test !(cart(1, 0) ≺ cart(1, 0)) + @test !(cart(1, 0) ≺ cart(0, 0)) + @test cart(1, 1) ≻ cart(0, 0) + @test !(cart(0, 1) ≻ cart(0, 0)) + @test !(cart(1, 1) ≻ cart(1, 0)) + @test cart(1, 0) ⪰ cart(1, 0) + @test cart(1, 0) ⪰ cart(0, 0) + @test cart(0, 0) ⪯ cart(0, 0) + + # product order + @test cart(1, 1) ⪯ cart(1, 1) + @test !(cart(1, 1) ≺ cart(1, 1)) + @test cart(1, 2) ⪯ cart(3, 4) + @test cart(1, 2) ≺ cart(3, 4) + @test cart(1, 1) ⪰ cart(1, 1) + @test !(cart(1, 1) ≻ cart(1, 1)) + @test cart(3, 4) ⪰ cart(1, 2) + @test cart(3, 4) ≻ cart(1, 2) +end + +@testitem "iscollinear" setup = [Setup] begin + @test iscollinear(cart(0, 0), cart(1, 1), cart(2, 2)) +end + +@testitem "iscoplanar" setup = [Setup] begin + @test iscoplanar(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) end diff --git a/test/primitives.jl b/test/primitives.jl index 1ea39c72b..adea51d38 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1,1292 +1,1289 @@ -@testset "Primitives" begin - @testset "Point" begin - @test embeddim(Point(1)) == 1 - @test embeddim(Point(1, 2)) == 2 - @test embeddim(Point(1, 2, 3)) == 3 - @test crs(cart(1, 1)) <: Cartesian{NoDatum} - @test crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} - @test crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} - @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} - @test Meshes.lentype(Point(1.0, 1.0)) == Meshes.Met{Float64} - @test Meshes.lentype(Point(1.0f0, 1.0f0)) == Meshes.Met{Float32} - @test Meshes.lentype(Point((T(1), T(1)))) == ℳ - @test Meshes.lentype(Point(T(1), T(1))) == ℳ - - equaltest(cart(1)) - equaltest(cart(1, 2)) - equaltest(cart(1, 2, 3)) - isapproxtest(cart(1)) - isapproxtest(cart(1, 2)) - isapproxtest(cart(1, 2, 3)) - - @test to(cart(1)) == vector(1) - @test to(cart(1, 2)) == vector(1, 2) - @test to(cart(1, 2, 3)) == vector(1, 2, 3) - @test to(Point(Polar(T(√2), T(π / 4)))) ≈ vector(1, 1) - @test to(Point(Cylindrical(T(√2), T(π / 4), T(1)))) ≈ vector(1, 1, 1) - - @test cart(1) - cart(1) == vector(0) - @test cart(1, 2) - cart(1, 1) == vector(0, 1) - @test cart(1, 2, 3) - cart(1, 1, 1) == vector(0, 1, 2) - @test_throws DimensionMismatch cart(1, 2) - cart(1, 2, 3) - - @test cart(1) + vector(0) == cart(1) - @test cart(2) + vector(2) == cart(4) - @test cart(1, 2) + vector(0, 0) == cart(1, 2) - @test cart(2, 3) + vector(2, 1) == cart(4, 4) - @test cart(1, 2, 3) + vector(0, 0, 0) == cart(1, 2, 3) - @test cart(2, 3, 4) + vector(2, 1, 0) == cart(4, 4, 4) - @test_throws DimensionMismatch cart(1, 2) + vector(1, 2, 3) - - @test cart(1) - vector(0) == cart(1) - @test cart(2) - vector(2) == cart(0) - @test cart(1, 2) - vector(0, 0) == cart(1, 2) - @test cart(2, 3) - vector(2, 1) == cart(0, 2) - @test cart(1, 2, 3) - vector(0, 0, 0) == cart(1, 2, 3) - @test cart(2, 3, 4) - vector(2, 1, 0) == cart(0, 2, 4) - - @test cart(1) ≈ cart(1 + eps(T)) - @test cart(1, 2) ≈ cart(1 + eps(T), T(2)) - @test cart(1, 2, 3) ≈ cart(1 + eps(T), T(2), T(3)) - - @test embeddim(Point((1,))) == 1 - @test Meshes.lentype(Point((1,))) == Meshes.Met{Float64} - @test Meshes.lentype(Point((1.0,))) == Meshes.Met{Float64} - - @test embeddim(Point((1, 2))) == 2 - @test Meshes.lentype(Point((1, 2))) == Meshes.Met{Float64} - @test Meshes.lentype(Point((1.0, 2.0))) == Meshes.Met{Float64} - - @test embeddim(Point((1, 2, 3))) == 3 - @test Meshes.lentype(Point((1, 2, 3))) == Meshes.Met{Float64} - @test Meshes.lentype(Point((1.0, 2.0, 3.0))) == Meshes.Met{Float64} - - # check all 1D Point constructors, because those tend to make trouble - @test Point(1) == Point((1,)) - @test Point(T(-2)) == Point((T(-2),)) - @test Point(T(0)) == Point((T(0),)) - - # check that input of mixed coordinate types is allowed and works as expected - @test Point(1, 0.2) == Point(1.0, 0.2) - @test Point((3.0, 4)) == Point(3.0, 4.0) - @test Point((5.0, 6.0, 7)) == Point(5.0, 6.0, 7.0) - @test Point(8, T(9.0)) == Point((T(8.0), T(9.0))) - @test Point((T(-1.0), -2)) == Point((T(-1.0), T(-2.0))) - @test Point((0, T(-1.0), +2, T(-4.0))) == Point((T(0.0), T(-1.0), T(+2.0), T(-4.0))) - - # Integer coordinates converted to Float64 - @test Meshes.lentype(Point(1)) == Meshes.Met{Float64} - @test Meshes.lentype(Point(1, 2)) == Meshes.Met{Float64} - @test Meshes.lentype(Point(1, 2, 3)) == Meshes.Met{Float64} - - # Unitful coordinates - p = Point(1u"m", 1u"m") - @test unit(Meshes.lentype(p)) == u"m" - @test Unitful.numtype(Meshes.lentype(p)) === Float64 - p = Point(1.0u"m", 1.0u"m") - @test unit(Meshes.lentype(p)) == u"m" - @test Unitful.numtype(Meshes.lentype(p)) === Float64 - p = Point(1.0f0u"m", 1.0f0u"m") - @test unit(Meshes.lentype(p)) == u"m" - @test Unitful.numtype(Meshes.lentype(p)) === Float32 - - # conversions - P = typeof(cart(1, 1)) - p1 = Point(1.0, 1.0) - p2 = convert(P, p1) - @test p2 isa P - p1 = Point(1.0f0, 1.0f0) - p2 = convert(P, p1) - @test p2 isa P - - # centroid - @test centroid(cart(1, 1)) == cart(1, 1) - - # measure of points is zero - @test measure(cart(1, 2)) == zero(ℳ) - @test measure(cart(1, 2, 3)) == zero(ℳ) - - # boundary of points is nothing - @test isnothing(boundary(cart(1))) - @test isnothing(boundary(cart(1, 2))) - @test isnothing(boundary(cart(1, 2, 3))) - - # check broadcasting works as expected - @test cart(2, 2) .- [cart(2, 3), cart(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] - @test cart(2, 2, 2) .- [cart(2, 3, 1), cart(3, 1, 4)] == [vector(0.0, -1.0, 1.0), vector(-1.0, 1.0, -2.0)] - - # angles between 2D points - @test ∠(cart(0, 1), cart(0, 0), cart(1, 0)) ≈ T(-π / 2) - @test ∠(cart(1, 0), cart(0, 0), cart(0, 1)) ≈ T(π / 2) - @test ∠(cart(-1, 0), cart(0, 0), cart(0, 1)) ≈ T(-π / 2) - @test ∠(cart(0, 1), cart(0, 0), cart(-1, 0)) ≈ T(π / 2) - @test ∠(cart(0, -1), cart(0, 0), cart(1, 0)) ≈ T(π / 2) - @test ∠(cart(1, 0), cart(0, 0), cart(0, -1)) ≈ T(-π / 2) - @test ∠(cart(0, -1), cart(0, 0), cart(-1, 0)) ≈ T(-π / 2) - @test ∠(cart(-1, 0), cart(0, 0), cart(0, -1)) ≈ T(π / 2) - - # angles between 3D points - @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) - @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) - @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) - @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) - @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) - @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) - - # a point pertains to itself - p = cart(0, 0) - q = cart(1, 1) - @test p ∈ p - @test q ∈ q - @test p ∉ q - @test q ∉ p - p = cart(0, 0, 0) - q = cart(1, 1, 1) - @test p ∈ p - @test q ∈ q - @test p ∉ q - @test q ∉ p - - # CRS propagation - p = merc(1, 1) - @test crs(p + vector(1, 1)) === crs(p) - @test crs(p - vector(1, 1)) === crs(p) - - p = cart(0, 1) - @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" - if T === Float32 - @test sprint(show, p) == "Point(x: 0.0f0 m, y: 1.0f0 m)" - @test sprint(show, MIME("text/plain"), p) == """ - Point with Cartesian{NoDatum} coordinates - ├─ x: 0.0f0 m - └─ y: 1.0f0 m""" - else - @test sprint(show, p) == "Point(x: 0.0 m, y: 1.0 m)" - @test sprint(show, MIME("text/plain"), p) == """ - Point with Cartesian{NoDatum} coordinates - ├─ x: 0.0 m - └─ y: 1.0 m""" - end +@testitem "Point" setup = [Setup] begin + @test embeddim(Point(1)) == 1 + @test embeddim(Point(1, 2)) == 2 + @test embeddim(Point(1, 2, 3)) == 3 + @test crs(cart(1, 1)) <: Cartesian{NoDatum} + @test crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} + @test crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} + @test Meshes.lentype(Point(1, 1)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1.0, 1.0)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1.0f0, 1.0f0)) == Meshes.Met{Float32} + @test Meshes.lentype(Point((T(1), T(1)))) == ℳ + @test Meshes.lentype(Point(T(1), T(1))) == ℳ + + equaltest(cart(1)) + equaltest(cart(1, 2)) + equaltest(cart(1, 2, 3)) + isapproxtest(cart(1)) + isapproxtest(cart(1, 2)) + isapproxtest(cart(1, 2, 3)) + + @test to(cart(1)) == vector(1) + @test to(cart(1, 2)) == vector(1, 2) + @test to(cart(1, 2, 3)) == vector(1, 2, 3) + @test to(Point(Polar(T(√2), T(π / 4)))) ≈ vector(1, 1) + @test to(Point(Cylindrical(T(√2), T(π / 4), T(1)))) ≈ vector(1, 1, 1) + + @test cart(1) - cart(1) == vector(0) + @test cart(1, 2) - cart(1, 1) == vector(0, 1) + @test cart(1, 2, 3) - cart(1, 1, 1) == vector(0, 1, 2) + @test_throws DimensionMismatch cart(1, 2) - cart(1, 2, 3) + + @test cart(1) + vector(0) == cart(1) + @test cart(2) + vector(2) == cart(4) + @test cart(1, 2) + vector(0, 0) == cart(1, 2) + @test cart(2, 3) + vector(2, 1) == cart(4, 4) + @test cart(1, 2, 3) + vector(0, 0, 0) == cart(1, 2, 3) + @test cart(2, 3, 4) + vector(2, 1, 0) == cart(4, 4, 4) + @test_throws DimensionMismatch cart(1, 2) + vector(1, 2, 3) + + @test cart(1) - vector(0) == cart(1) + @test cart(2) - vector(2) == cart(0) + @test cart(1, 2) - vector(0, 0) == cart(1, 2) + @test cart(2, 3) - vector(2, 1) == cart(0, 2) + @test cart(1, 2, 3) - vector(0, 0, 0) == cart(1, 2, 3) + @test cart(2, 3, 4) - vector(2, 1, 0) == cart(0, 2, 4) + + @test cart(1) ≈ cart(1 + eps(T)) + @test cart(1, 2) ≈ cart(1 + eps(T), T(2)) + @test cart(1, 2, 3) ≈ cart(1 + eps(T), T(2), T(3)) + + @test embeddim(Point((1,))) == 1 + @test Meshes.lentype(Point((1,))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0,))) == Meshes.Met{Float64} + + @test embeddim(Point((1, 2))) == 2 + @test Meshes.lentype(Point((1, 2))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0, 2.0))) == Meshes.Met{Float64} + + @test embeddim(Point((1, 2, 3))) == 3 + @test Meshes.lentype(Point((1, 2, 3))) == Meshes.Met{Float64} + @test Meshes.lentype(Point((1.0, 2.0, 3.0))) == Meshes.Met{Float64} + + # check all 1D Point constructors, because those tend to make trouble + @test Point(1) == Point((1,)) + @test Point(T(-2)) == Point((T(-2),)) + @test Point(T(0)) == Point((T(0),)) + + # check that input of mixed coordinate types is allowed and works as expected + @test Point(1, 0.2) == Point(1.0, 0.2) + @test Point((3.0, 4)) == Point(3.0, 4.0) + @test Point((5.0, 6.0, 7)) == Point(5.0, 6.0, 7.0) + @test Point(8, T(9.0)) == Point((T(8.0), T(9.0))) + @test Point((T(-1.0), -2)) == Point((T(-1.0), T(-2.0))) + @test Point((0, T(-1.0), +2, T(-4.0))) == Point((T(0.0), T(-1.0), T(+2.0), T(-4.0))) + + # Integer coordinates converted to Float64 + @test Meshes.lentype(Point(1)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1, 2)) == Meshes.Met{Float64} + @test Meshes.lentype(Point(1, 2, 3)) == Meshes.Met{Float64} + + # Unitful coordinates + p = Point(1u"m", 1u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float64 + p = Point(1.0u"m", 1.0u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float64 + p = Point(1.0f0u"m", 1.0f0u"m") + @test unit(Meshes.lentype(p)) == u"m" + @test Unitful.numtype(Meshes.lentype(p)) === Float32 + + # conversions + P = typeof(cart(1, 1)) + p1 = Point(1.0, 1.0) + p2 = convert(P, p1) + @test p2 isa P + p1 = Point(1.0f0, 1.0f0) + p2 = convert(P, p1) + @test p2 isa P + + # centroid + @test centroid(cart(1, 1)) == cart(1, 1) + + # measure of points is zero + @test measure(cart(1, 2)) == zero(ℳ) + @test measure(cart(1, 2, 3)) == zero(ℳ) + + # boundary of points is nothing + @test isnothing(boundary(cart(1))) + @test isnothing(boundary(cart(1, 2))) + @test isnothing(boundary(cart(1, 2, 3))) + + # check broadcasting works as expected + @test cart(2, 2) .- [cart(2, 3), cart(3, 1)] == [vector(0.0, -1.0), vector(-1.0, 1.0)] + @test cart(2, 2, 2) .- [cart(2, 3, 1), cart(3, 1, 4)] == [vector(0.0, -1.0, 1.0), vector(-1.0, 1.0, -2.0)] + + # angles between 2D points + @test ∠(cart(0, 1), cart(0, 0), cart(1, 0)) ≈ T(-π / 2) + @test ∠(cart(1, 0), cart(0, 0), cart(0, 1)) ≈ T(π / 2) + @test ∠(cart(-1, 0), cart(0, 0), cart(0, 1)) ≈ T(-π / 2) + @test ∠(cart(0, 1), cart(0, 0), cart(-1, 0)) ≈ T(π / 2) + @test ∠(cart(0, -1), cart(0, 0), cart(1, 0)) ≈ T(π / 2) + @test ∠(cart(1, 0), cart(0, 0), cart(0, -1)) ≈ T(-π / 2) + @test ∠(cart(0, -1), cart(0, 0), cart(-1, 0)) ≈ T(-π / 2) + @test ∠(cart(-1, 0), cart(0, 0), cart(0, -1)) ≈ T(π / 2) + + # angles between 3D points + @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) + @test ∠(cart(1, 0, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) + @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) + @test ∠(cart(0, 1, 0), cart(0, 0, 0), cart(0, 0, 1)) ≈ T(π / 2) + @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(1, 0, 0)) ≈ T(π / 2) + @test ∠(cart(0, 0, 1), cart(0, 0, 0), cart(0, 1, 0)) ≈ T(π / 2) + + # a point pertains to itself + p = cart(0, 0) + q = cart(1, 1) + @test p ∈ p + @test q ∈ q + @test p ∉ q + @test q ∉ p + p = cart(0, 0, 0) + q = cart(1, 1, 1) + @test p ∈ p + @test q ∈ q + @test p ∉ q + @test q ∉ p + + # CRS propagation + p = merc(1, 1) + @test crs(p + vector(1, 1)) === crs(p) + @test crs(p - vector(1, 1)) === crs(p) + + p = cart(0, 1) + @test sprint(show, p, context=:compact => true) == "(x: 0.0 m, y: 1.0 m)" + if T === Float32 + @test sprint(show, p) == "Point(x: 0.0f0 m, y: 1.0f0 m)" + @test sprint(show, MIME("text/plain"), p) == """ + Point with Cartesian{NoDatum} coordinates + ├─ x: 0.0f0 m + └─ y: 1.0f0 m""" + else + @test sprint(show, p) == "Point(x: 0.0 m, y: 1.0 m)" + @test sprint(show, MIME("text/plain"), p) == """ + Point with Cartesian{NoDatum} coordinates + ├─ x: 0.0 m + └─ y: 1.0 m""" end +end - @testset "Ray" begin - r = Ray(cart(0, 0), vector(1, 1)) - @test paramdim(r) == 1 - @test crs(r) <: Cartesian{NoDatum} - @test Meshes.lentype(r) == ℳ - @test measure(r) == typemax(ℳ) - @test length(r) == typemax(ℳ) - @test boundary(r) == cart(0, 0) - @test perimeter(r) == zero(ℳ) - - r = Ray(cart(0, 0), vector(1, 1)) - equaltest(r) - isapproxtest(r) - - r = Ray(cart(0, 0), vector(1, 1)) - @test r(T(0.0)) == cart(0, 0) - @test r(T(1.0)) == cart(1, 1) - @test r(T(Inf)) == cart(Inf, Inf) - @test r(T(1.0)) - r(T(0.0)) == vector(1, 1) - @test_throws DomainError(T(-1), "r(t) is not defined for t < 0.") r(T(-1)) - - p₁ = cart(3, 3, 3) - p₂ = cart(-3, -3, -3) - p₃ = cart(1, 0, 0) - r = Ray(cart(0, 0, 0), vector(1, 1, 1)) - @test p₁ ∈ r - @test p₂ ∉ r - @test p₃ ∉ r - - r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(cart(1, 1, 1), vector(1, 2, 1)) - @test r1 != r2 - - r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(cart(1, 0, 0), vector(-1, 0, 0)) - @test r1 != r2 - - r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) - r2 = Ray(cart(1, 0, 0), vector(1, 0, 0)) - @test r1 != r2 - - r1 = Ray(cart(0, 0, 0), vector(2, 0, 0)) - r2 = Ray(cart(0, 0, 0), vector(1, 0, 0)) - @test r1 == r2 - - r = Ray(cart(0, 0), vector(1, 1)) - @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), r) == """ - Ray - ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m) - └─ v: Vec(1.0f0 m, 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), r) == """ - Ray - ├─ p: Point(x: 0.0 m, y: 0.0 m) - └─ v: Vec(1.0 m, 1.0 m)""" - end +@testitem "Ray" setup = [Setup] begin + r = Ray(cart(0, 0), vector(1, 1)) + @test paramdim(r) == 1 + @test crs(r) <: Cartesian{NoDatum} + @test Meshes.lentype(r) == ℳ + @test measure(r) == typemax(ℳ) + @test length(r) == typemax(ℳ) + @test boundary(r) == cart(0, 0) + @test perimeter(r) == zero(ℳ) + + r = Ray(cart(0, 0), vector(1, 1)) + equaltest(r) + isapproxtest(r) + + r = Ray(cart(0, 0), vector(1, 1)) + @test r(T(0.0)) == cart(0, 0) + @test r(T(1.0)) == cart(1, 1) + @test r(T(Inf)) == cart(Inf, Inf) + @test r(T(1.0)) - r(T(0.0)) == vector(1, 1) + @test_throws DomainError(T(-1), "r(t) is not defined for t < 0.") r(T(-1)) + + p₁ = cart(3, 3, 3) + p₂ = cart(-3, -3, -3) + p₃ = cart(1, 0, 0) + r = Ray(cart(0, 0, 0), vector(1, 1, 1)) + @test p₁ ∈ r + @test p₂ ∉ r + @test p₃ ∉ r + + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 1, 1), vector(1, 2, 1)) + @test r1 != r2 + + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 0, 0), vector(-1, 0, 0)) + @test r1 != r2 + + r1 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + r2 = Ray(cart(1, 0, 0), vector(1, 0, 0)) + @test r1 != r2 + + r1 = Ray(cart(0, 0, 0), vector(2, 0, 0)) + r2 = Ray(cart(0, 0, 0), vector(1, 0, 0)) + @test r1 == r2 + + r = Ray(cart(0, 0), vector(1, 1)) + @test sprint(show, r) == "Ray(p: (x: 0.0 m, y: 0.0 m), v: (1.0 m, 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), r) == """ + Ray + ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ v: Vec(1.0f0 m, 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), r) == """ + Ray + ├─ p: Point(x: 0.0 m, y: 0.0 m) + └─ v: Vec(1.0 m, 1.0 m)""" end +end - @testset "Line" begin - l = Line(cart(0, 0), cart(1, 1)) - @test paramdim(l) == 1 - @test crs(l) <: Cartesian{NoDatum} - @test Meshes.lentype(l) == ℳ - @test measure(l) == typemax(ℳ) - @test length(l) == typemax(ℳ) - @test isnothing(boundary(l)) - @test perimeter(l) == zero(ℳ) - - l = Line(cart(0, 0), cart(1, 1)) - equaltest(l) - isapproxtest(l) - - l = Line(cart(0, 0), cart(1, 1)) - @test (l(0), l(1)) == (cart(0, 0), cart(1, 1)) - - l = Line(cart(0, 0), cart(1, 1)) - @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), l) == """ - Line - ├─ a: Point(x: 0.0f0 m, y: 0.0f0 m) - └─ b: Point(x: 1.0f0 m, y: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), l) == """ - Line - ├─ a: Point(x: 0.0 m, y: 0.0 m) - └─ b: Point(x: 1.0 m, y: 1.0 m)""" - end +@testitem "Line" setup = [Setup] begin + l = Line(cart(0, 0), cart(1, 1)) + @test paramdim(l) == 1 + @test crs(l) <: Cartesian{NoDatum} + @test Meshes.lentype(l) == ℳ + @test measure(l) == typemax(ℳ) + @test length(l) == typemax(ℳ) + @test isnothing(boundary(l)) + @test perimeter(l) == zero(ℳ) + + l = Line(cart(0, 0), cart(1, 1)) + equaltest(l) + isapproxtest(l) + + l = Line(cart(0, 0), cart(1, 1)) + @test (l(0), l(1)) == (cart(0, 0), cart(1, 1)) + + l = Line(cart(0, 0), cart(1, 1)) + @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), l) == """ + Line + ├─ a: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ b: Point(x: 1.0f0 m, y: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), l) == """ + Line + ├─ a: Point(x: 0.0 m, y: 0.0 m) + └─ b: Point(x: 1.0 m, y: 1.0 m)""" end +end - @testset "Plane" begin - p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - @test p(T(1), T(0)) == cart(1, 0, 0) - @test paramdim(p) == 2 - @test embeddim(p) == 3 - @test crs(p) <: Cartesian{NoDatum} - @test Meshes.lentype(p) == ℳ - @test measure(p) == typemax(ℳ)^2 - @test area(p) == typemax(ℳ)^2 - @test p(T(0), T(0)) == cart(0, 0, 0) - @test normal(p) == Vec(0, 0, 1) - @test isnothing(boundary(p)) - @test perimeter(p) == zero(ℳ) - - p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - equaltest(p) - isapproxtest(p) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - @test p(T(1), T(0)) == cart(1, 0, 0) - @test p(T(0), T(1)) == cart(0, 1, 0) - - p₁ = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - p₂ = Plane(cart(0, 0, 0), vector(0, 1, 0), vector(1, 0, 0)) - @test p₁ ≈ p₂ - p₁ = Plane(cart(0, 0, 0), vector(1, 1, 0)) - p₂ = Plane(cart(0, 0, 0), -vector(1, 1, 0)) - @test p₁ ≈ p₂ - - # https://github.com/JuliaGeometry/Meshes.jl/issues/624 - p₁ = Plane(cart(0, 0, 0), vector(0, 0, 1)) - p₂ = Plane(cart(0, 0, 10), vector(0, 0, 1)) - @test !(p₁ ≈ p₂) - - # normal to plane has norm one regardless of basis - p = Plane(cart(0, 0, 0), vector(2, 0, 0), vector(0, 3, 0)) - n = normal(p) - @test isapprox(norm(n), oneunit(ℳ), atol=atol(ℳ)) - - # plane passing through three points - p₁ = cart(0, 0, 0) - p₂ = cart(1, 2, 3) - p₃ = cart(3, 2, 1) - p = Plane(p₁, p₂, p₃) - @test p₁ ∈ p - @test p₂ ∈ p - @test p₃ ∈ p - - p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) - @test sprint(show, p) == - "Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, 0.0 m, 0.0 m), v: (0.0 m, 1.0 m, 0.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), p) == """ - Plane - ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ u: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) - └─ v: Vec(0.0f0 m, 1.0f0 m, 0.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), p) == """ - Plane - ├─ p: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ u: Vec(1.0 m, 0.0 m, 0.0 m) - └─ v: Vec(0.0 m, 1.0 m, 0.0 m)""" - end +@testitem "Plane" setup = [Setup] begin + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test p(T(1), T(0)) == cart(1, 0, 0) + @test paramdim(p) == 2 + @test embeddim(p) == 3 + @test crs(p) <: Cartesian{NoDatum} + @test Meshes.lentype(p) == ℳ + @test measure(p) == typemax(ℳ)^2 + @test area(p) == typemax(ℳ)^2 + @test p(T(0), T(0)) == cart(0, 0, 0) + @test normal(p) == Vec(0, 0, 1) + @test isnothing(boundary(p)) + @test perimeter(p) == zero(ℳ) + + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + equaltest(p) + isapproxtest(p) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test p(T(1), T(0)) == cart(1, 0, 0) + @test p(T(0), T(1)) == cart(0, 1, 0) + + p₁ = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + p₂ = Plane(cart(0, 0, 0), vector(0, 1, 0), vector(1, 0, 0)) + @test p₁ ≈ p₂ + p₁ = Plane(cart(0, 0, 0), vector(1, 1, 0)) + p₂ = Plane(cart(0, 0, 0), -vector(1, 1, 0)) + @test p₁ ≈ p₂ + + # https://github.com/JuliaGeometry/Meshes.jl/issues/624 + p₁ = Plane(cart(0, 0, 0), vector(0, 0, 1)) + p₂ = Plane(cart(0, 0, 10), vector(0, 0, 1)) + @test !(p₁ ≈ p₂) + + # normal to plane has norm one regardless of basis + p = Plane(cart(0, 0, 0), vector(2, 0, 0), vector(0, 3, 0)) + n = normal(p) + @test isapprox(norm(n), oneunit(ℳ), atol=atol(ℳ)) + + # plane passing through three points + p₁ = cart(0, 0, 0) + p₂ = cart(1, 2, 3) + p₃ = cart(3, 2, 1) + p = Plane(p₁, p₂, p₃) + @test p₁ ∈ p + @test p₂ ∈ p + @test p₃ ∈ p + + p = Plane(cart(0, 0, 0), vector(1, 0, 0), vector(0, 1, 0)) + @test sprint(show, p) == + "Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, 0.0 m, 0.0 m), v: (0.0 m, 1.0 m, 0.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), p) == """ + Plane + ├─ p: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ u: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) + └─ v: Vec(0.0f0 m, 1.0f0 m, 0.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), p) == """ + Plane + ├─ p: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ u: Vec(1.0 m, 0.0 m, 0.0 m) + └─ v: Vec(0.0 m, 1.0 m, 0.0 m)""" end +end - @testset "BezierCurve" begin - b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) - @test embeddim(b) == 2 - @test paramdim(b) == 1 - @test crs(b) <: Cartesian{NoDatum} - @test Meshes.lentype(b) == ℳ - - b = BezierCurve(cart(0, 0), cart(1, 1)) - equaltest(b) - isapproxtest(b) - - b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) - for method in [DeCasteljau(), Horner()] - @test b(T(0), method) == cart(0, 0) - @test b(T(1), method) == cart(1, 0) - @test b(T(0.5), method) == cart(0.5, 0.5) - @test b(T(0.5), method) == cart(0.5, 0.5) - @test_throws DomainError(T(-0.1), "b(t) is not defined for t outside [0, 1].") b(T(-0.1), method) - @test_throws DomainError(T(1.2), "b(t) is not defined for t outside [0, 1].") b(T(1.2), method) - end - - @test boundary(b) == Multi([cart(0, 0), cart(1, 0)]) - b = BezierCurve(cart(0, 0), cart(1, 1)) - @test boundary(b) == Multi([cart(0, 0), cart(1, 1)]) - @test perimeter(b) == zero(ℳ) - - # CRS propagation - b = BezierCurve(merc(0, 0), merc(0.5, 1), merc(1, 0)) - @test crs(b(T(0), Horner())) === crs(b) - - b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) - @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" - if T === Float32 - @test sprint(show, MIME("text/plain"), b) == """ - BezierCurve - └─ controls: [Point(x: 0.0f0 m, y: 0.0f0 m), Point(x: 0.5f0 m, y: 1.0f0 m), Point(x: 1.0f0 m, y: 0.0f0 m)]""" - else - @test sprint(show, MIME("text/plain"), b) == """ - BezierCurve - └─ controls: [Point(x: 0.0 m, y: 0.0 m), Point(x: 0.5 m, y: 1.0 m), Point(x: 1.0 m, y: 0.0 m)]""" - end +@testitem "BezierCurve" setup = [Setup] begin + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) + @test embeddim(b) == 2 + @test paramdim(b) == 1 + @test crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ + + b = BezierCurve(cart(0, 0), cart(1, 1)) + equaltest(b) + isapproxtest(b) + + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) + for method in [DeCasteljau(), Horner()] + @test b(T(0), method) == cart(0, 0) + @test b(T(1), method) == cart(1, 0) + @test b(T(0.5), method) == cart(0.5, 0.5) + @test b(T(0.5), method) == cart(0.5, 0.5) + @test_throws DomainError(T(-0.1), "b(t) is not defined for t outside [0, 1].") b(T(-0.1), method) + @test_throws DomainError(T(1.2), "b(t) is not defined for t outside [0, 1].") b(T(1.2), method) end - @testset "Box" begin - b = Box(cart(0), cart(1)) - @test embeddim(b) == 1 - @test paramdim(b) == 1 - @test crs(b) <: Cartesian{NoDatum} - @test Meshes.lentype(b) == ℳ - @test minimum(b) == cart(0) - @test maximum(b) == cart(1) - @test extrema(b) == (cart(0), cart(1)) - - b = Box(cart(0, 0), cart(1, 1)) - @test embeddim(b) == 2 - @test paramdim(b) == 2 - @test crs(b) <: Cartesian{NoDatum} - @test Meshes.lentype(b) == ℳ - @test minimum(b) == cart(0, 0) - @test maximum(b) == cart(1, 1) - @test extrema(b) == (cart(0, 0), cart(1, 1)) - - b = Box(cart(0, 0, 0), cart(1, 1, 1)) - @test embeddim(b) == 3 - @test paramdim(b) == 3 - @test crs(b) <: Cartesian{NoDatum} - @test Meshes.lentype(b) == ℳ - @test minimum(b) == cart(0, 0, 0) - @test maximum(b) == cart(1, 1, 1) - @test extrema(b) == (cart(0, 0, 0), cart(1, 1, 1)) - - b = Box(latlon(0, 0), latlon(45, 90)) - @test embeddim(b) == 3 - @test paramdim(b) == 2 - - b = Box(cart(0, 0), cart(1, 1)) - equaltest(b) - isapproxtest(b) - - b = Box(cart(0), cart(1)) - @test boundary(b) == Multi([cart(0), cart(1)]) - @test measure(b) == T(1) * u"m" - @test cart(0) ∈ b - @test cart(1) ∈ b - @test cart(0.5) ∈ b - @test cart(-0.5) ∉ b - @test cart(1.5) ∉ b - - b = Box(cart(0, 0), cart(1, 1)) - @test measure(b) == area(b) == T(1) * u"m^2" - @test cart(1, 1) ∈ b - @test perimeter(b) ≈ T(4) * u"m" - - b = Box(cart(1, 1), cart(2, 2)) - @test sides(b) == (T(1) * u"m", T(1) * u"m") - @test centroid(b) == cart(1.5, 1.5) - @test diagonal(b) == √T(2) * u"m" - - b = Box(cart(1, 2), cart(3, 4)) - v = cart.([(1, 2), (3, 2), (3, 4), (1, 4)]) - @test boundary(b) == Ring(v) - - b = Box(cart(1, 2, 3), cart(4, 5, 6)) - v = cart.([(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)]) - c = connect.([(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)]) - @test boundary(b) == SimpleMesh(v, c) - - b = Box(cart(0, 0), cart(1, 1)) - @test boundary(b) == Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - - b = Box(cart(0, 0, 0), cart(1, 1, 1)) - m = boundary(b) - @test m isa Mesh - @test nvertices(m) == 8 - @test nelements(m) == 6 - - # subsetting with boxes - b1 = Box(cart(0, 0), cart(0.5, 0.5)) - b2 = Box(cart(0.1, 0.1), cart(0.5, 0.5)) - b3 = Box(cart(0, 0), cart(1, 1)) - @test b1 ⊆ b3 - @test b2 ⊆ b3 - @test !(b1 ⊆ b2) - @test !(b3 ⊆ b1) - @test !(b3 ⊆ b1) - - b = Box(cart(0, 0), cart(10, 20)) - @test b(T(0.0), T(0.0)) == cart(0, 0) - @test b(T(0.5), T(0.0)) == cart(5, 0) - @test b(T(1.0), T(0.0)) == cart(10, 0) - @test b(T(0.0), T(0.5)) == cart(0, 10) - @test b(T(0.0), T(1.0)) == cart(0, 20) - - b = Box(cart(0, 0, 0), cart(10, 20, 30)) - @test b(T(0.0), T(0.0), T(0.0)) == cart(0, 0, 0) - @test b(T(1.0), T(1.0), T(1.0)) == cart(10, 20, 30) - - @test_throws AssertionError Box(cart(1), cart(0)) - @test_throws AssertionError Box(cart(1, 1), cart(0, 0)) - @test_throws AssertionError Box(cart(1, 1, 1), cart(0, 0, 0)) - - b = Box(cart(0, 0), cart(1, 1)) - q = convert(Quadrangle, b) - @test q isa Quadrangle - @test q == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - - b = Box(cart(0, 0, 0), cart(1, 1, 1)) - h = convert(Hexahedron, b) - @test h isa Hexahedron - @test h == Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - - # CRS propagation - b = Box(merc(0, 0), merc(1, 1)) - @test crs(centroid(b)) === crs(b) - - b = Box(cart(0, 0), cart(1, 1)) - @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), b) == """ - Box - ├─ min: Point(x: 0.0f0 m, y: 0.0f0 m) - └─ max: Point(x: 1.0f0 m, y: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), b) == """ - Box - ├─ min: Point(x: 0.0 m, y: 0.0 m) - └─ max: Point(x: 1.0 m, y: 1.0 m)""" - end + @test boundary(b) == Multi([cart(0, 0), cart(1, 0)]) + b = BezierCurve(cart(0, 0), cart(1, 1)) + @test boundary(b) == Multi([cart(0, 0), cart(1, 1)]) + @test perimeter(b) == zero(ℳ) + + # CRS propagation + b = BezierCurve(merc(0, 0), merc(0.5, 1), merc(1, 0)) + @test crs(b(T(0), Horner())) === crs(b) + + b = BezierCurve(cart(0, 0), cart(0.5, 1), cart(1, 0)) + @test sprint(show, b) == "BezierCurve(controls: [(x: 0.0 m, y: 0.0 m), (x: 0.5 m, y: 1.0 m), (x: 1.0 m, y: 0.0 m)])" + if T === Float32 + @test sprint(show, MIME("text/plain"), b) == """ + BezierCurve + └─ controls: [Point(x: 0.0f0 m, y: 0.0f0 m), Point(x: 0.5f0 m, y: 1.0f0 m), Point(x: 1.0f0 m, y: 0.0f0 m)]""" + else + @test sprint(show, MIME("text/plain"), b) == """ + BezierCurve + └─ controls: [Point(x: 0.0 m, y: 0.0 m), Point(x: 0.5 m, y: 1.0 m), Point(x: 1.0 m, y: 0.0 m)]""" end +end - @testset "Ball" begin - b = Ball(cart(1, 2, 3), T(5)) - @test embeddim(b) == 3 - @test paramdim(b) == 3 - @test crs(b) <: Cartesian{NoDatum} - @test Meshes.lentype(b) == ℳ - @test center(b) == cart(1, 2, 3) - @test radius(b) == T(5) * u"m" - - b = Ball(latlon(0, 0), T(5)) - @test embeddim(b) == 3 - @test paramdim(b) == 2 - - b = Ball(cart(0, 0), T(1)) - equaltest(b) - isapproxtest(b) - - b = Ball(cart(1, 2, 3), 4) - @test Meshes.lentype(b) == ℳ - - b1 = Ball(cart(0, 0), T(1)) - b2 = Ball(cart(0, 0)) - b3 = Ball(T.((0, 0))) - @test b1 == b2 == b3 - - b = Ball(cart(0, 0), T(2)) - @test measure(b) ≈ T(π) * (T(2)^2) * u"m^2" - b = Ball(cart(0, 0, 0), T(2)) - @test measure(b) ≈ T(4 / 3) * T(π) * (T(2)^3) * u"m^3" - @test_throws ArgumentError length(b) - @test_throws ArgumentError area(b) - - b = Ball(cart(0, 0), T(2)) - @test cart(1, 0) ∈ b - @test cart(0, 1) ∈ b - @test cart(2, 0) ∈ b - @test cart(0, 2) ∈ b - @test cart(3, 5) ∉ b - @test perimeter(b) ≈ T(4π) * u"m" - - b = Ball(cart(0, 0, 0), T(2)) - @test cart(1, 0, 0) ∈ b - @test cart(0, 0, 1) ∈ b - @test cart(2, 0, 0) ∈ b - @test cart(0, 0, 2) ∈ b - @test cart(3, 5, 2) ∉ b - - b = Ball(cart(0, 0), T(2)) - @test b(T(0), T(0)) ≈ cart(0, 0) - @test b(T(1), T(0)) ≈ cart(2, 0) - - b = Ball(cart(7, 7), T(1.5)) - ps = b.(1, rand(T, 100)) - all(∈(b), ps) - - b = Ball(cart(0, 0, 0), T(2)) - @test b(T(0), T(0), T(0)) ≈ cart(0, 0, 0) - @test b(T(1), T(0), T(0)) ≈ cart(0, 0, 2) - - b = Ball(cart(7, 7, 7), T(1.5)) - ps = b.(1, rand(T, 100), rand(T, 100)) - all(∈(b), ps) - - b = Ball(cart(0, 0), T(1)) - @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), b) == """ - Ball - ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m) - └─ radius: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), b) == """ - Ball - ├─ center: Point(x: 0.0 m, y: 0.0 m) - └─ radius: 1.0 m""" - end +@testitem "Box" setup = [Setup] begin + b = Box(cart(0), cart(1)) + @test embeddim(b) == 1 + @test paramdim(b) == 1 + @test crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ + @test minimum(b) == cart(0) + @test maximum(b) == cart(1) + @test extrema(b) == (cart(0), cart(1)) + + b = Box(cart(0, 0), cart(1, 1)) + @test embeddim(b) == 2 + @test paramdim(b) == 2 + @test crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ + @test minimum(b) == cart(0, 0) + @test maximum(b) == cart(1, 1) + @test extrema(b) == (cart(0, 0), cart(1, 1)) + + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + @test embeddim(b) == 3 + @test paramdim(b) == 3 + @test crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ + @test minimum(b) == cart(0, 0, 0) + @test maximum(b) == cart(1, 1, 1) + @test extrema(b) == (cart(0, 0, 0), cart(1, 1, 1)) + + b = Box(latlon(0, 0), latlon(45, 90)) + @test embeddim(b) == 3 + @test paramdim(b) == 2 + + b = Box(cart(0, 0), cart(1, 1)) + equaltest(b) + isapproxtest(b) + + b = Box(cart(0), cart(1)) + @test boundary(b) == Multi([cart(0), cart(1)]) + @test measure(b) == T(1) * u"m" + @test cart(0) ∈ b + @test cart(1) ∈ b + @test cart(0.5) ∈ b + @test cart(-0.5) ∉ b + @test cart(1.5) ∉ b + + b = Box(cart(0, 0), cart(1, 1)) + @test measure(b) == area(b) == T(1) * u"m^2" + @test cart(1, 1) ∈ b + @test perimeter(b) ≈ T(4) * u"m" + + b = Box(cart(1, 1), cart(2, 2)) + @test sides(b) == (T(1) * u"m", T(1) * u"m") + @test centroid(b) == cart(1.5, 1.5) + @test diagonal(b) == √T(2) * u"m" + + b = Box(cart(1, 2), cart(3, 4)) + v = cart.([(1, 2), (3, 2), (3, 4), (1, 4)]) + @test boundary(b) == Ring(v) + + b = Box(cart(1, 2, 3), cart(4, 5, 6)) + v = cart.([(1, 2, 3), (4, 2, 3), (4, 5, 3), (1, 5, 3), (1, 2, 6), (4, 2, 6), (4, 5, 6), (1, 5, 6)]) + c = connect.([(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)]) + @test boundary(b) == SimpleMesh(v, c) + + b = Box(cart(0, 0), cart(1, 1)) + @test boundary(b) == Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + m = boundary(b) + @test m isa Mesh + @test nvertices(m) == 8 + @test nelements(m) == 6 + + # subsetting with boxes + b1 = Box(cart(0, 0), cart(0.5, 0.5)) + b2 = Box(cart(0.1, 0.1), cart(0.5, 0.5)) + b3 = Box(cart(0, 0), cart(1, 1)) + @test b1 ⊆ b3 + @test b2 ⊆ b3 + @test !(b1 ⊆ b2) + @test !(b3 ⊆ b1) + @test !(b3 ⊆ b1) + + b = Box(cart(0, 0), cart(10, 20)) + @test b(T(0.0), T(0.0)) == cart(0, 0) + @test b(T(0.5), T(0.0)) == cart(5, 0) + @test b(T(1.0), T(0.0)) == cart(10, 0) + @test b(T(0.0), T(0.5)) == cart(0, 10) + @test b(T(0.0), T(1.0)) == cart(0, 20) + + b = Box(cart(0, 0, 0), cart(10, 20, 30)) + @test b(T(0.0), T(0.0), T(0.0)) == cart(0, 0, 0) + @test b(T(1.0), T(1.0), T(1.0)) == cart(10, 20, 30) + + @test_throws AssertionError Box(cart(1), cart(0)) + @test_throws AssertionError Box(cart(1, 1), cart(0, 0)) + @test_throws AssertionError Box(cart(1, 1, 1), cart(0, 0, 0)) + + b = Box(cart(0, 0), cart(1, 1)) + q = convert(Quadrangle, b) + @test q isa Quadrangle + @test q == Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + + b = Box(cart(0, 0, 0), cart(1, 1, 1)) + h = convert(Hexahedron, b) + @test h isa Hexahedron + @test h == Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + + # CRS propagation + b = Box(merc(0, 0), merc(1, 1)) + @test crs(centroid(b)) === crs(b) + + b = Box(cart(0, 0), cart(1, 1)) + @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), b) == """ + Box + ├─ min: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ max: Point(x: 1.0f0 m, y: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), b) == """ + Box + ├─ min: Point(x: 0.0 m, y: 0.0 m) + └─ max: Point(x: 1.0 m, y: 1.0 m)""" end +end - @testset "Sphere" begin - s = Sphere(cart(0, 0, 0), T(1)) - @test embeddim(s) == 3 - @test paramdim(s) == 2 - @test crs(s) <: Cartesian{NoDatum} - @test Meshes.lentype(s) == ℳ - @test center(s) == cart(0, 0, 0) - @test radius(s) == T(1) * u"m" - @test extrema(s) == (cart(-1, -1, -1), cart(1, 1, 1)) - @test isnothing(boundary(s)) - @test perimeter(s) == zero(ℳ) - - s = Sphere(latlon(0, 0), T(1)) - @test embeddim(s) == 3 - @test paramdim(s) == 1 - - s = Sphere(cart(0, 0), T(1)) - equaltest(s) - isapproxtest(s) - - s = Sphere(cart(1, 2, 3), 4) - @test Meshes.lentype(s) == ℳ - - s = Sphere(cart(0, 0), T(1)) - @test embeddim(s) == 2 - @test paramdim(s) == 1 - @test Meshes.lentype(s) == ℳ - @test center(s) == cart(0, 0) - @test radius(s) == T(1) * u"m" - @test extrema(s) == (cart(-1, -1), cart(1, 1)) - @test isnothing(boundary(s)) - - s1 = Sphere(cart(0, 0), T(1)) - s2 = Sphere(cart(0, 0)) - s3 = Sphere(T.((0, 0))) - @test s1 == s2 == s3 - - s = Sphere(cart(0, 0), T(2)) - @test measure(s) ≈ T(2π) * 2 * u"m" - @test length(s) ≈ T(2π) * 2 * u"m" - @test extrema(s) == (cart(-2, -2), cart(2, 2)) - s = Sphere(cart(0, 0, 0), T(2)) - @test measure(s) ≈ T(4π) * (2^2) * u"m^2" - @test area(s) ≈ T(4π) * (2^2) * u"m^2" - - s = Sphere(cart(0, 0), T(2)) - @test cart(1, 0) ∉ s - @test cart(0, 1) ∉ s - @test cart(2, 0) ∈ s - @test cart(0, 2) ∈ s - @test cart(3, 5) ∉ s - - s = Sphere(cart(0, 0, 0), T(2)) - @test cart(1, 0, 0) ∉ s - @test cart(0, 0, 1) ∉ s - @test cart(2, 0, 0) ∈ s - @test cart(0, 0, 2) ∈ s - @test cart(3, 5, 2) ∉ s - - # 2D sphere passing through 3 points - s = Sphere(cart(0, 0), cart(0.5, 0), cart(1, 1)) - @test center(s) == cart(0.25, 0.75) - @test radius(s) == T(0.7905694150420949) * u"m" - s = Sphere(cart(0, 0), cart(1, 0), cart(0, 1)) - @test center(s) == cart(0.5, 0.5) - @test radius(s) == T(0.7071067811865476) * u"m" - s = Sphere(cart(0, 0), cart(1, 0), cart(1, 1)) - @test center(s) == cart(0.5, 0.5) - @test radius(s) == T(0.7071067811865476) * u"m" - - # 3D sphere passing through 4 points - s = Sphere(cart(0, 0, 0), cart(5, 0, 1), cart(1, 1, 1), cart(3, 2, 1)) - @test cart(0, 0, 0) ∈ s - @test cart(5, 0, 1) ∈ s - @test cart(1, 1, 1) ∈ s - @test cart(3, 2, 1) ∈ s - O = center(s) - r = radius(s) - @test isapprox(r, norm(cart(0, 0, 0) - O)) - - s = Sphere(cart(0, 0), T(2)) - @test s(T(0)) ≈ cart(2, 0) - @test s(T(0.5)) ≈ cart(-2, 0) - - s = Sphere(cart(0, 0, 0), T(2)) - @test s(T(0), T(0)) ≈ cart(0, 0, 2) - @test s(T(0.5), T(0.5)) ≈ cart(-2, 0, 0) - - s = Sphere(cart(0, 0, 0), T(1)) - @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), s) == """ - Sphere - ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - └─ radius: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), s) == """ - Sphere - ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - └─ radius: 1.0 m""" - end +@testitem "Ball" setup = [Setup] begin + b = Ball(cart(1, 2, 3), T(5)) + @test embeddim(b) == 3 + @test paramdim(b) == 3 + @test crs(b) <: Cartesian{NoDatum} + @test Meshes.lentype(b) == ℳ + @test center(b) == cart(1, 2, 3) + @test radius(b) == T(5) * u"m" + + b = Ball(latlon(0, 0), T(5)) + @test embeddim(b) == 3 + @test paramdim(b) == 2 + + b = Ball(cart(0, 0), T(1)) + equaltest(b) + isapproxtest(b) + + b = Ball(cart(1, 2, 3), 4) + @test Meshes.lentype(b) == ℳ + + b1 = Ball(cart(0, 0), T(1)) + b2 = Ball(cart(0, 0)) + b3 = Ball(T.((0, 0))) + @test b1 == b2 == b3 + + b = Ball(cart(0, 0), T(2)) + @test measure(b) ≈ T(π) * (T(2)^2) * u"m^2" + b = Ball(cart(0, 0, 0), T(2)) + @test measure(b) ≈ T(4 / 3) * T(π) * (T(2)^3) * u"m^3" + @test_throws ArgumentError length(b) + @test_throws ArgumentError area(b) + + b = Ball(cart(0, 0), T(2)) + @test cart(1, 0) ∈ b + @test cart(0, 1) ∈ b + @test cart(2, 0) ∈ b + @test cart(0, 2) ∈ b + @test cart(3, 5) ∉ b + @test perimeter(b) ≈ T(4π) * u"m" + + b = Ball(cart(0, 0, 0), T(2)) + @test cart(1, 0, 0) ∈ b + @test cart(0, 0, 1) ∈ b + @test cart(2, 0, 0) ∈ b + @test cart(0, 0, 2) ∈ b + @test cart(3, 5, 2) ∉ b + + b = Ball(cart(0, 0), T(2)) + @test b(T(0), T(0)) ≈ cart(0, 0) + @test b(T(1), T(0)) ≈ cart(2, 0) + + b = Ball(cart(7, 7), T(1.5)) + ps = b.(1, rand(T, 100)) + all(∈(b), ps) + + b = Ball(cart(0, 0, 0), T(2)) + @test b(T(0), T(0), T(0)) ≈ cart(0, 0, 0) + @test b(T(1), T(0), T(0)) ≈ cart(0, 0, 2) + + b = Ball(cart(7, 7, 7), T(1.5)) + ps = b.(1, rand(T, 100), rand(T, 100)) + all(∈(b), ps) + + b = Ball(cart(0, 0), T(1)) + @test sprint(show, b) == "Ball(center: (x: 0.0 m, y: 0.0 m), radius: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), b) == """ + Ball + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m) + └─ radius: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), b) == """ + Ball + ├─ center: Point(x: 0.0 m, y: 0.0 m) + └─ radius: 1.0 m""" end +end - @testset "Ellipsoid" begin - e = Ellipsoid((T(3), T(2), T(1))) - @test embeddim(e) == 3 - @test paramdim(e) == 2 - @test crs(e) <: Cartesian{NoDatum} - @test Meshes.lentype(e) == ℳ - @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") - @test center(e) == cart(0, 0, 0) - @test isnothing(boundary(e)) - @test perimeter(e) == zero(ℳ) - - e = Ellipsoid((T(3), T(2), T(1))) - equaltest(e) - isapproxtest(e) - - e = Ellipsoid((T(3), T(2), T(1))) - @test sprint(show, e) == - "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" - if T === Float32 - @test sprint(show, MIME("text/plain"), e) == """ - Ellipsoid - ├─ radii: (3.0f0 m, 2.0f0 m, 1.0f0 m) - ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - └─ rotation: UniformScaling{Bool}(true)""" - else - @test sprint(show, MIME("text/plain"), e) == """ - Ellipsoid - ├─ radii: (3.0 m, 2.0 m, 1.0 m) - ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - └─ rotation: UniformScaling{Bool}(true)""" - end +@testitem "Sphere" setup = [Setup] begin + s = Sphere(cart(0, 0, 0), T(1)) + @test embeddim(s) == 3 + @test paramdim(s) == 2 + @test crs(s) <: Cartesian{NoDatum} + @test Meshes.lentype(s) == ℳ + @test center(s) == cart(0, 0, 0) + @test radius(s) == T(1) * u"m" + @test extrema(s) == (cart(-1, -1, -1), cart(1, 1, 1)) + @test isnothing(boundary(s)) + @test perimeter(s) == zero(ℳ) + + s = Sphere(latlon(0, 0), T(1)) + @test embeddim(s) == 3 + @test paramdim(s) == 1 + + s = Sphere(cart(0, 0), T(1)) + equaltest(s) + isapproxtest(s) + + s = Sphere(cart(1, 2, 3), 4) + @test Meshes.lentype(s) == ℳ + + s = Sphere(cart(0, 0), T(1)) + @test embeddim(s) == 2 + @test paramdim(s) == 1 + @test Meshes.lentype(s) == ℳ + @test center(s) == cart(0, 0) + @test radius(s) == T(1) * u"m" + @test extrema(s) == (cart(-1, -1), cart(1, 1)) + @test isnothing(boundary(s)) + + s1 = Sphere(cart(0, 0), T(1)) + s2 = Sphere(cart(0, 0)) + s3 = Sphere(T.((0, 0))) + @test s1 == s2 == s3 + + s = Sphere(cart(0, 0), T(2)) + @test measure(s) ≈ T(2π) * 2 * u"m" + @test length(s) ≈ T(2π) * 2 * u"m" + @test extrema(s) == (cart(-2, -2), cart(2, 2)) + s = Sphere(cart(0, 0, 0), T(2)) + @test measure(s) ≈ T(4π) * (2^2) * u"m^2" + @test area(s) ≈ T(4π) * (2^2) * u"m^2" + + s = Sphere(cart(0, 0), T(2)) + @test cart(1, 0) ∉ s + @test cart(0, 1) ∉ s + @test cart(2, 0) ∈ s + @test cart(0, 2) ∈ s + @test cart(3, 5) ∉ s + + s = Sphere(cart(0, 0, 0), T(2)) + @test cart(1, 0, 0) ∉ s + @test cart(0, 0, 1) ∉ s + @test cart(2, 0, 0) ∈ s + @test cart(0, 0, 2) ∈ s + @test cart(3, 5, 2) ∉ s + + # 2D sphere passing through 3 points + s = Sphere(cart(0, 0), cart(0.5, 0), cart(1, 1)) + @test center(s) == cart(0.25, 0.75) + @test radius(s) == T(0.7905694150420949) * u"m" + s = Sphere(cart(0, 0), cart(1, 0), cart(0, 1)) + @test center(s) == cart(0.5, 0.5) + @test radius(s) == T(0.7071067811865476) * u"m" + s = Sphere(cart(0, 0), cart(1, 0), cart(1, 1)) + @test center(s) == cart(0.5, 0.5) + @test radius(s) == T(0.7071067811865476) * u"m" + + # 3D sphere passing through 4 points + s = Sphere(cart(0, 0, 0), cart(5, 0, 1), cart(1, 1, 1), cart(3, 2, 1)) + @test cart(0, 0, 0) ∈ s + @test cart(5, 0, 1) ∈ s + @test cart(1, 1, 1) ∈ s + @test cart(3, 2, 1) ∈ s + O = center(s) + r = radius(s) + @test isapprox(r, norm(cart(0, 0, 0) - O)) + + s = Sphere(cart(0, 0), T(2)) + @test s(T(0)) ≈ cart(2, 0) + @test s(T(0.5)) ≈ cart(-2, 0) + + s = Sphere(cart(0, 0, 0), T(2)) + @test s(T(0), T(0)) ≈ cart(0, 0, 2) + @test s(T(0.5), T(0.5)) ≈ cart(-2, 0, 0) + + s = Sphere(cart(0, 0, 0), T(1)) + @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), s) == """ + Sphere + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + └─ radius: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), s) == """ + Sphere + ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + └─ radius: 1.0 m""" end +end - @testset "Disk" begin - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - @test embeddim(d) == 3 - @test paramdim(d) == 2 - @test crs(d) <: Cartesian{NoDatum} - @test Meshes.lentype(d) == ℳ - @test plane(d) == p - @test center(d) == cart(0, 0, 0) - @test radius(d) == T(2) * u"m" - @test normal(d) == vector(0, 0, 1) - @test measure(d) == T(π) * T(2)^2 * u"m^2" - @test area(d) == measure(d) - @test cart(0, 0, 0) ∈ d - @test cart(0, 0, 1) ∉ d - @test boundary(d) == Circle(p, T(2)) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - equaltest(d) - isapproxtest(d) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - @test sprint(show, d) == - "Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), d) == """ - Disk - ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 2.0f0 m""" - else - @test sprint(show, MIME("text/plain"), d) == """ - Disk - ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 2.0 m""" - end +@testitem "Ellipsoid" setup = [Setup] begin + e = Ellipsoid((T(3), T(2), T(1))) + @test embeddim(e) == 3 + @test paramdim(e) == 2 + @test crs(e) <: Cartesian{NoDatum} + @test Meshes.lentype(e) == ℳ + @test radii(e) == (T(3) * u"m", T(2) * u"m", T(1) * u"m") + @test center(e) == cart(0, 0, 0) + @test isnothing(boundary(e)) + @test perimeter(e) == zero(ℳ) + + e = Ellipsoid((T(3), T(2), T(1))) + equaltest(e) + isapproxtest(e) + + e = Ellipsoid((T(3), T(2), T(1))) + @test sprint(show, e) == + "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" + if T === Float32 + @test sprint(show, MIME("text/plain"), e) == """ + Ellipsoid + ├─ radii: (3.0f0 m, 2.0f0 m, 1.0f0 m) + ├─ center: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + └─ rotation: LinearAlgebra.UniformScaling{Bool}(true)""" + else + @test sprint(show, MIME("text/plain"), e) == """ + Ellipsoid + ├─ radii: (3.0 m, 2.0 m, 1.0 m) + ├─ center: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + └─ rotation: LinearAlgebra.UniformScaling{Bool}(true)""" end +end - @testset "Circle" begin - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - c = Circle(p, T(2)) - @test embeddim(c) == 3 - @test paramdim(c) == 1 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test plane(c) == p - @test center(c) == cart(0, 0, 0) - @test radius(c) == T(2) * u"m" - @test measure(c) == 2 * T(π) * T(2) * u"m" - @test length(c) == measure(c) - @test cart(2, 0, 0) ∈ c - @test cart(0, 2, 0) ∈ c - @test cart(0, 0, 0) ∉ c - @test isnothing(boundary(c)) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - c = Circle(p, T(2)) - equaltest(c) - isapproxtest(c) - - # 3D circumcircle - p1 = cart(0, 4, 0) - p2 = cart(0, -4, 0) - p3 = cart(0, 0, 4) - c = Circle(p1, p2, p3) - @test p1 ∈ c - @test p2 ∈ c - @test p3 ∈ c - - # circle parametrization - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - c = Circle(p, T(2)) - @test c(T(0)) ≈ cart(2, 0, 0) - @test c(T(0.25)) ≈ cart(0, 2, 0) - @test c(T(0.5)) ≈ cart(-2, 0, 0) - @test c(T(0.75)) ≈ cart(0, -2, 0) - @test c(T(1)) ≈ cart(2, 0, 0) - - # CRS propagation - c1 = Cartesian{WGS84Latest}(T(0), T(4), T(0)) - c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) - c3 = Cartesian{WGS84Latest}(T(0), T(0), T(4)) - c = Circle(Point(c1), Point(c2), Point(c3)) - @test crs(c) === typeof(c1) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - c = Circle(p, T(2)) - @test sprint(show, c) == - "Circle(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), c) == """ - Circle - ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 2.0f0 m""" - else - @test sprint(show, MIME("text/plain"), c) == """ - Circle - ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 2.0 m""" - end +@testitem "Disk" setup = [Setup] begin + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + @test embeddim(d) == 3 + @test paramdim(d) == 2 + @test crs(d) <: Cartesian{NoDatum} + @test Meshes.lentype(d) == ℳ + @test plane(d) == p + @test center(d) == cart(0, 0, 0) + @test radius(d) == T(2) * u"m" + @test normal(d) == vector(0, 0, 1) + @test measure(d) == T(π) * T(2)^2 * u"m^2" + @test area(d) == measure(d) + @test cart(0, 0, 0) ∈ d + @test cart(0, 0, 1) ∉ d + @test boundary(d) == Circle(p, T(2)) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + equaltest(d) + isapproxtest(d) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + @test sprint(show, d) == + "Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), d) == """ + Disk + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0f0 m""" + else + @test sprint(show, MIME("text/plain"), d) == """ + Disk + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0 m""" end +end - @testset "Cylinder" begin - c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) - @test embeddim(c) == 3 - @test paramdim(c) == 3 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test radius(c) == T(5) * u"m" - @test bottom(c) == Plane(cart(1, 2, 3), vector(0, 0, 1)) - @test top(c) == Plane(cart(4, 5, 6), vector(0, 0, 1)) - @test axis(c) == Line(cart(1, 2, 3), cart(4, 5, 6)) - @test !isright(c) - @test measure(c) == volume(c) ≈ T(5)^2 * pi * T(3) * sqrt(T(3)) * u"m^3" - @test cart(1, 2, 3) ∈ c - @test cart(4, 5, 6) ∈ c - @test cart(0.99, 1.99, 2.99) ∉ c - @test cart(4.01, 5.01, 6.01) ∉ c - @test !Meshes.hasintersectingplanes(c) - @test c(0, 0, 0) ≈ bottom(c)(0, 0) - @test c(0, 0, 1) ≈ top(c)(0, 0) - @test c(1, 0.25, 0.5) ≈ Point(T(4.330127018922193), T(10.330127018922191), T(4.5)) - @test_throws DomainError c(1.1, 0, 0) - - c = Cylinder(T(1)) - equaltest(c) - isapproxtest(c) - - c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) - @test Meshes.hasintersectingplanes(c) - - c1 = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) - c2 = Cylinder(cart(0, 0, 0), cart(0, 0, 1)) - c3 = Cylinder(T(1)) - @test c1 == c2 == c3 - @test c1 ≈ c2 ≈ c3 - - c = Cylinder(T(1)) - @test Meshes.lentype(c) == ℳ - c = Cylinder(1) - @test Meshes.lentype(c) == Meshes.Met{Float64} - - c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) - @test radius(c) == T(1) * u"m" - @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) - @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test centroid(c) == cart(0.0, 0.0, 0.5) - @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) - @test isright(c) - @test boundary(c) == CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) - @test measure(c) == volume(c) ≈ T(π) * u"m^3" - @test cart(0, 0, 0) ∈ c - @test cart(0, 0, 1) ∈ c - @test cart(1, 0, 0) ∈ c - @test cart(0, 1, 0) ∈ c - @test cart(cosd(60), sind(60), 0.5) ∈ c - @test cart(0, 0, -0.001) ∉ c - @test cart(0, 0, 1.001) ∉ c - @test cart(1, 1, 1) ∉ c - - c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) - @test sprint(show, c) == - "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), c) == """ - Cylinder - ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), c) == """ - Cylinder - ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 1.0 m""" - end +@testitem "Circle" setup = [Setup] begin + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + c = Circle(p, T(2)) + @test embeddim(c) == 3 + @test paramdim(c) == 1 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test plane(c) == p + @test center(c) == cart(0, 0, 0) + @test radius(c) == T(2) * u"m" + @test measure(c) == 2 * T(π) * T(2) * u"m" + @test length(c) == measure(c) + @test cart(2, 0, 0) ∈ c + @test cart(0, 2, 0) ∈ c + @test cart(0, 0, 0) ∉ c + @test isnothing(boundary(c)) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + c = Circle(p, T(2)) + equaltest(c) + isapproxtest(c) + + # 3D circumcircle + p1 = cart(0, 4, 0) + p2 = cart(0, -4, 0) + p3 = cart(0, 0, 4) + c = Circle(p1, p2, p3) + @test p1 ∈ c + @test p2 ∈ c + @test p3 ∈ c + + # circle parametrization + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + c = Circle(p, T(2)) + @test c(T(0)) ≈ cart(2, 0, 0) + @test c(T(0.25)) ≈ cart(0, 2, 0) + @test c(T(0.5)) ≈ cart(-2, 0, 0) + @test c(T(0.75)) ≈ cart(0, -2, 0) + @test c(T(1)) ≈ cart(2, 0, 0) + + # CRS propagation + c1 = Cartesian{WGS84Latest}(T(0), T(4), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(-4), T(0)) + c3 = Cartesian{WGS84Latest}(T(0), T(0), T(4)) + c = Circle(Point(c1), Point(c2), Point(c3)) + @test crs(c) === typeof(c1) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + c = Circle(p, T(2)) + @test sprint(show, c) == + "Circle(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), c) == """ + Circle + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0f0 m""" + else + @test sprint(show, MIME("text/plain"), c) == """ + Circle + ├─ plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 2.0 m""" end +end - @testset "CylinderSurface" begin - c = CylinderSurface(T(2)) - @test embeddim(c) == 3 - @test paramdim(c) == 2 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test radius(c) == T(2) * u"m" - @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) - @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test centroid(c) == cart(0.0, 0.0, 0.5) - @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) - @test isright(c) - @test isnothing(boundary(c)) - @test measure(c) == area(c) ≈ (2 * T(2)^2 * pi + 2 * T(2) * pi) * u"m^2" - @test !Meshes.hasintersectingplanes(c) - - c = CylinderSurface(T(1)) - equaltest(c) - isapproxtest(c) - - c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) - @test Meshes.hasintersectingplanes(c) - - c1 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) - c2 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1)) - c3 = CylinderSurface(T(1)) - @test c1 == c2 == c3 - @test c1 ≈ c2 ≈ c3 - - c = CylinderSurface(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) - @test measure(c) == area(c) ≈ (2 * T(5)^2 * pi + 2 * T(5) * pi * sqrt(3 * T(3)^2)) * u"m^2" - - c = CylinderSurface(T(1)) - @test c(T(0), T(0)) ≈ cart(1, 0, 0) - @test c(T(0.5), T(0)) ≈ cart(-1, 0, 0) - @test c(T(0), T(1)) ≈ cart(1, 0, 1) - @test c(T(0.5), T(1)) ≈ cart(-1, 0, 1) - - c = CylinderSurface(1.0) - @test Meshes.lentype(c) == Meshes.Met{Float64} - c = CylinderSurface(1.0f0) - @test Meshes.lentype(c) == Meshes.Met{Float32} - c = CylinderSurface(1) - @test Meshes.lentype(c) == Meshes.Met{Float64} - - # CRS propagation - c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) - c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) - c = CylinderSurface(Point(c1), Point(c2), T(1)) - @test crs(centroid(c)) === crs(c) - - c = CylinderSurface(T(1)) - @test sprint(show, c) == - "CylinderSurface(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), c) == """ - CylinderSurface - ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), c) == """ - CylinderSurface - ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) - └─ radius: 1.0 m""" - end +@testitem "Cylinder" setup = [Setup] begin + c = Cylinder(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) + @test embeddim(c) == 3 + @test paramdim(c) == 3 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test radius(c) == T(5) * u"m" + @test bottom(c) == Plane(cart(1, 2, 3), vector(0, 0, 1)) + @test top(c) == Plane(cart(4, 5, 6), vector(0, 0, 1)) + @test axis(c) == Line(cart(1, 2, 3), cart(4, 5, 6)) + @test !isright(c) + @test measure(c) == volume(c) ≈ T(5)^2 * pi * T(3) * sqrt(T(3)) * u"m^3" + @test cart(1, 2, 3) ∈ c + @test cart(4, 5, 6) ∈ c + @test cart(0.99, 1.99, 2.99) ∉ c + @test cart(4.01, 5.01, 6.01) ∉ c + @test !Meshes.hasintersectingplanes(c) + @test c(0, 0, 0) ≈ bottom(c)(0, 0) + @test c(0, 0, 1) ≈ top(c)(0, 0) + @test c(1, 0.25, 0.5) ≈ Point(T(4.330127018922193), T(10.330127018922191), T(4.5)) + @test_throws DomainError c(1.1, 0, 0) + + c = Cylinder(T(1)) + equaltest(c) + isapproxtest(c) + + c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) + @test Meshes.hasintersectingplanes(c) + + c1 = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) + c2 = Cylinder(cart(0, 0, 0), cart(0, 0, 1)) + c3 = Cylinder(T(1)) + @test c1 == c2 == c3 + @test c1 ≈ c2 ≈ c3 + + c = Cylinder(T(1)) + @test Meshes.lentype(c) == ℳ + c = Cylinder(1) + @test Meshes.lentype(c) == Meshes.Met{Float64} + + c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) + @test radius(c) == T(1) * u"m" + @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test centroid(c) == cart(0.0, 0.0, 0.5) + @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) + @test isright(c) + @test boundary(c) == CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) + @test measure(c) == volume(c) ≈ T(π) * u"m^3" + @test cart(0, 0, 0) ∈ c + @test cart(0, 0, 1) ∈ c + @test cart(1, 0, 0) ∈ c + @test cart(0, 1, 0) ∈ c + @test cart(cosd(60), sind(60), 0.5) ∈ c + @test cart(0, 0, -0.001) ∉ c + @test cart(0, 0, 1.001) ∉ c + @test cart(1, 1, 1) ∉ c + + c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) + @test sprint(show, c) == + "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), c) == """ + Cylinder + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), c) == """ + Cylinder + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0 m""" end +end - @testset "ParaboloidSurface" begin - p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) - @test embeddim(p) == 3 - @test paramdim(p) == 2 - @test crs(p) <: Cartesian{NoDatum} - @test Meshes.lentype(p) == ℳ - @test focallength(p) == T(2) * u"m" - @test radius(p) == T(1) * u"m" - @test axis(p) == Line(cart(0, 0, 0), cart(0, 0, T(2))) - @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" - @test centroid(p) == cart(0, 0, 1 / 16) - - p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) - equaltest(p) - isapproxtest(p) - - p1 = ParaboloidSurface(cart(1, 2, 3), T(1), T(1)) - p2 = ParaboloidSurface(cart(1, 2, 3), T(1)) - p3 = ParaboloidSurface(cart(1, 2, 3)) - @test p1 == p2 == p3 - @test p1 ≈ p2 ≈ p3 - - p1 = ParaboloidSurface((1, 2, 3), 1.0, 1.0) - p2 = ParaboloidSurface((1, 2, 3), 1.0) - p3 = ParaboloidSurface((1, 2, 3)) - @test p1 == p2 == p3 - @test p1 ≈ p2 ≈ p3 - - p = ParaboloidSurface((1.0, 2.0, 3.0), 4.0, 5.0) - @test Meshes.lentype(p) == Meshes.Met{Float64} - @test radius(p) == 4.0 * u"m" - @test focallength(p) == 5.0 * u"m" - - p = ParaboloidSurface(cart(1, 5, 2), T(3), T(4)) - @test measure(p) == area(p) ≈ T(128π / 3 * (73√73 / 512 - 1)) * u"m^2" - @test p(T(0), T(0)) ≈ cart(1, 5, 2) - @test p(T(1), T(0)) ≈ cart(4, 5, 2 + 3^2 / (4 * 4)) - @test_throws DomainError p(T(-0.1), T(0)) - @test_throws DomainError p(T(1.1), T(0)) - - p = ParaboloidSurface() - @test Meshes.lentype(p) == Meshes.Met{Float64} - @test p(0.0, 0.0) ≈ Point(0, 0, 0) - @test p(0.5, 0.0) ≈ Point(0.5, 0, 0.5^2 / 4) - @test p(0.0, 0.5) ≈ Point(0, 0, 0) - @test p(0.5, 0.5) ≈ Point(-0.5, 0, 0.5^2 / 4) - - p = ParaboloidSurface(Point(0.0, 0.0, 0.0)) - @test Meshes.lentype(p) == Meshes.Met{Float64} - p = ParaboloidSurface(Point(0.0f0, 0.0f0, 0.0f0)) - @test Meshes.lentype(p) == Meshes.Met{Float32} - - p = ParaboloidSurface(cart(0, 0, 0), T(1), T(1)) - @test sprint(show, p) == - "ParaboloidSurface(apex: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m, focallength: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), p) == """ - ParaboloidSurface - ├─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) - ├─ radius: 1.0f0 m - └─ focallength: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), p) == """ - ParaboloidSurface - ├─ apex: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) - ├─ radius: 1.0 m - └─ focallength: 1.0 m""" - end +@testitem "CylinderSurface" setup = [Setup] begin + c = CylinderSurface(T(2)) + @test embeddim(c) == 3 + @test paramdim(c) == 2 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test radius(c) == T(2) * u"m" + @test bottom(c) == Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test top(c) == Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test centroid(c) == cart(0.0, 0.0, 0.5) + @test axis(c) == Line(cart(0, 0, 0), cart(0, 0, 1)) + @test isright(c) + @test isnothing(boundary(c)) + @test measure(c) == area(c) ≈ (2 * T(2)^2 * pi + 2 * T(2) * pi) * u"m^2" + @test !Meshes.hasintersectingplanes(c) + + c = CylinderSurface(T(1)) + equaltest(c) + isapproxtest(c) + + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(1, 0, 1)), T(5)) + @test Meshes.hasintersectingplanes(c) + + c1 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1), T(1)) + c2 = CylinderSurface(cart(0, 0, 0), cart(0, 0, 1)) + c3 = CylinderSurface(T(1)) + @test c1 == c2 == c3 + @test c1 ≈ c2 ≈ c3 + + c = CylinderSurface(Plane(cart(1, 2, 3), vector(0, 0, 1)), Plane(cart(4, 5, 6), vector(0, 0, 1)), T(5)) + @test measure(c) == area(c) ≈ (2 * T(5)^2 * pi + 2 * T(5) * pi * sqrt(3 * T(3)^2)) * u"m^2" + + c = CylinderSurface(T(1)) + @test c(T(0), T(0)) ≈ cart(1, 0, 0) + @test c(T(0.5), T(0)) ≈ cart(-1, 0, 0) + @test c(T(0), T(1)) ≈ cart(1, 0, 1) + @test c(T(0.5), T(1)) ≈ cart(-1, 0, 1) + + c = CylinderSurface(1.0) + @test Meshes.lentype(c) == Meshes.Met{Float64} + c = CylinderSurface(1.0f0) + @test Meshes.lentype(c) == Meshes.Met{Float32} + c = CylinderSurface(1) + @test Meshes.lentype(c) == Meshes.Met{Float64} + + # CRS propagation + c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) + c2 = Cartesian{WGS84Latest}(T(0), T(0), T(1)) + c = CylinderSurface(Point(c1), Point(c2), T(1)) + @test crs(centroid(c)) === crs(c) + + c = CylinderSurface(T(1)) + @test sprint(show, c) == + "CylinderSurface(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), c) == """ + CylinderSurface + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), c) == """ + CylinderSurface + ├─ bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + ├─ top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)) + └─ radius: 1.0 m""" end +end - @testset "Cone" begin - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - c = Cone(d, a) - @test embeddim(c) == 3 - @test paramdim(c) == 3 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - @test boundary(c) == ConeSurface(d, a) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = T.((0, 0, 1)) - c = Cone(d, a) - @test embeddim(c) == 3 - @test paramdim(c) == 3 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - c = Cone(d, a) - equaltest(c) - isapproxtest(c) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - c = Cone(d, a) - @test sprint(show, c) == - "Cone(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), c) == """ - Cone - ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) - └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), c) == """ - Cone - ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) - └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" - end - - # cone: apex at (5,4,3); base center at (5,1,3) - # halfangle: 30° -> radius: sqrt(3) - # axis of the cone is parallel to y axis - p = Plane(cart(5, 1, 3), vector(0, 1, 0)) - d = Disk(p, sqrt(T(3))) - a = cart(5, 4, 3) - c = Cone(d, a) - - @test rad2deg(Meshes.halfangle(c)) ≈ T(30) - @test Meshes.height(c) ≈ T(3) * u"m" - - @test cart(5, 1, 3) ∈ c - @test cart(5, 4, 3) ∈ c - @test cart(5, 1, 3 - sqrt(3)) ∈ c - @test cart(5, 1, 3 + sqrt(3)) ∈ c - @test cart(5 - sqrt(3), 1, 3) ∈ c - @test cart(5 + sqrt(3), 1, 3) ∈ c - @test cart(5, 2.5, 3) ∈ c - @test cart(5 + sqrt(3) / 2, 2.5, 3) ∈ c - @test cart(5 - sqrt(3) / 2, 2.5, 3) ∈ c - - @test cart(5, 0.9, 3) ∉ c - @test cart(5, 4.1, 3) ∉ c - @test cart(5, 1, 1) ∉ c - @test cart(5 + sqrt(3) + 0.01, 1, 3) ∉ c - @test cart(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c - @test cart(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c +@testitem "ParaboloidSurface" setup = [Setup] begin + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) + @test embeddim(p) == 3 + @test paramdim(p) == 2 + @test crs(p) <: Cartesian{NoDatum} + @test Meshes.lentype(p) == ℳ + @test focallength(p) == T(2) * u"m" + @test radius(p) == T(1) * u"m" + @test axis(p) == Line(cart(0, 0, 0), cart(0, 0, T(2))) + @test measure(p) == area(p) ≈ T(32π / 3 * (17√17 / 64 - 1)) * u"m^2" + @test centroid(p) == cart(0, 0, 1 / 16) + + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) + equaltest(p) + isapproxtest(p) + + p1 = ParaboloidSurface(cart(1, 2, 3), T(1), T(1)) + p2 = ParaboloidSurface(cart(1, 2, 3), T(1)) + p3 = ParaboloidSurface(cart(1, 2, 3)) + @test p1 == p2 == p3 + @test p1 ≈ p2 ≈ p3 + + p1 = ParaboloidSurface((1, 2, 3), 1.0, 1.0) + p2 = ParaboloidSurface((1, 2, 3), 1.0) + p3 = ParaboloidSurface((1, 2, 3)) + @test p1 == p2 == p3 + @test p1 ≈ p2 ≈ p3 + + p = ParaboloidSurface((1.0, 2.0, 3.0), 4.0, 5.0) + @test Meshes.lentype(p) == Meshes.Met{Float64} + @test radius(p) == 4.0 * u"m" + @test focallength(p) == 5.0 * u"m" + + p = ParaboloidSurface(cart(1, 5, 2), T(3), T(4)) + @test measure(p) == area(p) ≈ T(128π / 3 * (73√73 / 512 - 1)) * u"m^2" + @test p(T(0), T(0)) ≈ cart(1, 5, 2) + @test p(T(1), T(0)) ≈ cart(4, 5, 2 + 3^2 / (4 * 4)) + @test_throws DomainError p(T(-0.1), T(0)) + @test_throws DomainError p(T(1.1), T(0)) + + p = ParaboloidSurface() + @test Meshes.lentype(p) == Meshes.Met{Float64} + @test p(0.0, 0.0) ≈ Point(0, 0, 0) + @test p(0.5, 0.0) ≈ Point(0.5, 0, 0.5^2 / 4) + @test p(0.0, 0.5) ≈ Point(0, 0, 0) + @test p(0.5, 0.5) ≈ Point(-0.5, 0, 0.5^2 / 4) + + p = ParaboloidSurface(Point(0.0, 0.0, 0.0)) + @test Meshes.lentype(p) == Meshes.Met{Float64} + p = ParaboloidSurface(Point(0.0f0, 0.0f0, 0.0f0)) + @test Meshes.lentype(p) == Meshes.Met{Float32} + + p = ParaboloidSurface(cart(0, 0, 0), T(1), T(1)) + @test sprint(show, p) == "ParaboloidSurface(apex: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m, focallength: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), p) == """ + ParaboloidSurface + ├─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 0.0f0 m) + ├─ radius: 1.0f0 m + └─ focallength: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), p) == """ + ParaboloidSurface + ├─ apex: Point(x: 0.0 m, y: 0.0 m, z: 0.0 m) + ├─ radius: 1.0 m + └─ focallength: 1.0 m""" end +end - @testset "ConeSurface" begin - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - s = ConeSurface(d, a) - @test embeddim(s) == 3 - @test paramdim(s) == 2 - @test crs(s) <: Cartesian{NoDatum} - @test Meshes.lentype(s) == ℳ - @test isnothing(boundary(s)) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = T.((0, 0, 1)) - c = ConeSurface(d, a) - @test embeddim(c) == 3 - @test paramdim(c) == 2 - @test crs(c) <: Cartesian{NoDatum} - @test Meshes.lentype(c) == ℳ - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - c = ConeSurface(d, a) - equaltest(c) - isapproxtest(c) - - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - d = Disk(p, T(2)) - a = cart(0, 0, 1) - s = ConeSurface(d, a) - @test sprint(show, s) == - "ConeSurface(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" - if T === Float32 - @test sprint(show, MIME("text/plain"), s) == """ - ConeSurface - ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) - └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" - else - @test sprint(show, MIME("text/plain"), s) == """ - ConeSurface - ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) - └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" - end +@testitem "Cone" setup = [Setup] begin + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + c = Cone(d, a) + @test embeddim(c) == 3 + @test paramdim(c) == 3 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + @test boundary(c) == ConeSurface(d, a) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = T.((0, 0, 1)) + c = Cone(d, a) + @test embeddim(c) == 3 + @test paramdim(c) == 3 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + c = Cone(d, a) + equaltest(c) + isapproxtest(c) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + c = Cone(d, a) + @test sprint(show, c) == + "Cone(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), c) == """ + Cone + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), c) == """ + Cone + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end - @testset "Frustum" begin - pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) - db = Disk(pb, T(1)) - pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) - dt = Disk(pt, T(2)) - f = Frustum(db, dt) - @test embeddim(f) == 3 - @test crs(f) <: Cartesian{NoDatum} - @test Meshes.lentype(f) == ℳ - @test boundary(f) == FrustumSurface(db, dt) - - @test_throws AssertionError Frustum(db, db) - - pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) - db = Disk(pb, T(1)) - pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) - dt = Disk(pt, T(2)) - f = Frustum(db, dt) - equaltest(f) - isapproxtest(f) - - f = Frustum(db, dt) - @test cart(0, 0, 0) ∈ f - @test cart(0, 0, 10) ∈ f - @test cart(1, 0, 0) ∈ f - @test cart(2, 0, 10) ∈ f - @test cart(1, 0, 5) ∈ f - - @test cart(1, 1, 0) ∉ f - @test cart(2, 2, 10) ∉ f - @test cart(0, 0, -0.01) ∉ f - @test cart(0, 0, 10.01) ∉ f - - # reverse order, when top is larger than bottom - # the frustum is the same geometry - f = Frustum(dt, db) - @test cart(0, 0, 0) ∈ f - @test cart(0, 0, 10) ∈ f - @test cart(1, 0, 0) ∈ f - @test cart(2, 0, 10) ∈ f - @test cart(1, 0, 5) ∈ f - - @test cart(1, 1, 0) ∉ f - @test cart(2, 2, 10) ∉ f - @test cart(0, 0, -0.01) ∉ f - @test cart(0, 0, 10.01) ∉ f - end + # cone: apex at (5,4,3); base center at (5,1,3) + # halfangle: 30° -> radius: sqrt(3) + # axis of the cone is parallel to y axis + p = Plane(cart(5, 1, 3), vector(0, 1, 0)) + d = Disk(p, sqrt(T(3))) + a = cart(5, 4, 3) + c = Cone(d, a) + + @test rad2deg(Meshes.halfangle(c)) ≈ T(30) + @test Meshes.height(c) ≈ T(3) * u"m" + + @test cart(5, 1, 3) ∈ c + @test cart(5, 4, 3) ∈ c + @test cart(5, 1, 3 - sqrt(3)) ∈ c + @test cart(5, 1, 3 + sqrt(3)) ∈ c + @test cart(5 - sqrt(3), 1, 3) ∈ c + @test cart(5 + sqrt(3), 1, 3) ∈ c + @test cart(5, 2.5, 3) ∈ c + @test cart(5 + sqrt(3) / 2, 2.5, 3) ∈ c + @test cart(5 - sqrt(3) / 2, 2.5, 3) ∈ c + + @test cart(5, 0.9, 3) ∉ c + @test cart(5, 4.1, 3) ∉ c + @test cart(5, 1, 1) ∉ c + @test cart(5 + sqrt(3) + 0.01, 1, 3) ∉ c + @test cart(5 + sqrt(3) / 2 + 0.01, 2.5, 3) ∉ c + @test cart(5 - sqrt(3) / 2 - 0.01, 2.5, 3) ∉ c +end - @testset "FrustumSurface" begin - pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) - db = Disk(pb, T(1)) - pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) - dt = Disk(pt, T(2)) - f = FrustumSurface(db, dt) - @test embeddim(f) == 3 - @test paramdim(f) == 2 - @test crs(f) <: Cartesian{NoDatum} - @test Meshes.lentype(f) == ℳ - @test isnothing(boundary(f)) - - @test_throws AssertionError FrustumSurface(db, db) - - pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) - db = Disk(pb, T(1)) - pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) - dt = Disk(pt, T(2)) - f = FrustumSurface(db, dt) - equaltest(f) - isapproxtest(f) +@testitem "ConeSurface" setup = [Setup] begin + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + s = ConeSurface(d, a) + @test embeddim(s) == 3 + @test paramdim(s) == 2 + @test crs(s) <: Cartesian{NoDatum} + @test Meshes.lentype(s) == ℳ + @test isnothing(boundary(s)) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = T.((0, 0, 1)) + c = ConeSurface(d, a) + @test embeddim(c) == 3 + @test paramdim(c) == 2 + @test crs(c) <: Cartesian{NoDatum} + @test Meshes.lentype(c) == ℳ + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + c = ConeSurface(d, a) + equaltest(c) + isapproxtest(c) + + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + d = Disk(p, T(2)) + a = cart(0, 0, 1) + s = ConeSurface(d, a) + @test sprint(show, s) == + "ConeSurface(base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m), apex: (x: 0.0 m, y: 0.0 m, z: 1.0 m))" + if T === Float32 + @test sprint(show, MIME("text/plain"), s) == """ + ConeSurface + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0f0 m, y: 0.0f0 m, z: 1.0f0 m)""" + else + @test sprint(show, MIME("text/plain"), s) == """ + ConeSurface + ├─ base: Disk(plane: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 2.0 m) + └─ apex: Point(x: 0.0 m, y: 0.0 m, z: 1.0 m)""" end +end + +@testitem "Frustum" setup = [Setup] begin + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = Frustum(db, dt) + @test embeddim(f) == 3 + @test crs(f) <: Cartesian{NoDatum} + @test Meshes.lentype(f) == ℳ + @test boundary(f) == FrustumSurface(db, dt) + + @test_throws AssertionError Frustum(db, db) + + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = Frustum(db, dt) + equaltest(f) + isapproxtest(f) + + f = Frustum(db, dt) + @test cart(0, 0, 0) ∈ f + @test cart(0, 0, 10) ∈ f + @test cart(1, 0, 0) ∈ f + @test cart(2, 0, 10) ∈ f + @test cart(1, 0, 5) ∈ f + + @test cart(1, 1, 0) ∉ f + @test cart(2, 2, 10) ∉ f + @test cart(0, 0, -0.01) ∉ f + @test cart(0, 0, 10.01) ∉ f + + # reverse order, when top is larger than bottom + # the frustum is the same geometry + f = Frustum(dt, db) + @test cart(0, 0, 0) ∈ f + @test cart(0, 0, 10) ∈ f + @test cart(1, 0, 0) ∈ f + @test cart(2, 0, 10) ∈ f + @test cart(1, 0, 5) ∈ f + + @test cart(1, 1, 0) ∉ f + @test cart(2, 2, 10) ∉ f + @test cart(0, 0, -0.01) ∉ f + @test cart(0, 0, 10.01) ∉ f +end + +@testitem "FrustumSurface" setup = [Setup] begin + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = FrustumSurface(db, dt) + @test embeddim(f) == 3 + @test paramdim(f) == 2 + @test crs(f) <: Cartesian{NoDatum} + @test Meshes.lentype(f) == ℳ + @test isnothing(boundary(f)) + + @test_throws AssertionError FrustumSurface(db, db) + + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + db = Disk(pb, T(1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) + dt = Disk(pt, T(2)) + f = FrustumSurface(db, dt) + equaltest(f) + isapproxtest(f) +end - @testset "Torus" begin - t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) - @test cart(1, 1, -1) ∈ t - @test cart(1, 1, 1) ∉ t - @test paramdim(t) == 2 - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test center(t) == cart(1, 1, 1) - @test normal(t) == vector(1, 0, 0) - @test radii(t) == (T(2) * u"m", T(1) * u"m") - @test axis(t) == Line(cart(1, 1, 1), cart(2, 1, 1)) - @test measure(t) ≈ 8 * T(π)^2 * u"m^2" - @test_throws ArgumentError length(t) - @test_throws ArgumentError volume(t) - - t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) - equaltest(t) - isapproxtest(t) - - # torus passing through three points - p₁ = cart(0, 0, 0) - p₂ = cart(1, 2, 3) - p₃ = cart(3, 2, 1) - t = Torus(p₁, p₂, p₃, T(1)) - c = center(t) - R, r = radii(t) - @test r == T(1) * u"m" - @test norm(p₁ - c) ≈ R - @test norm(p₂ - c) ≈ R - @test norm(p₃ - c) ≈ R - @test p₁ ∈ t - @test p₂ ∈ t - @test p₃ ∈ t - - # constructor with tuples - c₁ = T.((0, 0, 0)) - c₂ = T.((1, 2, 3)) - c₃ = T.((3, 2, 1)) - q = Torus(c₁, c₂, c₃, 1) - @test q == t - - t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) - @test sprint(show, t) == - "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" - if T === Float32 - @test sprint(show, MIME("text/plain"), t) == """ - Torus - ├─ center: Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) - ├─ normal: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) - ├─ major: 2.0f0 m - └─ minor: 1.0f0 m""" - else - @test sprint(show, MIME("text/plain"), t) == """ - Torus - ├─ center: Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) - ├─ normal: Vec(1.0 m, 0.0 m, 0.0 m) - ├─ major: 2.0 m - └─ minor: 1.0 m""" - end +@testitem "Torus" setup = [Setup] begin + t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) + @test cart(1, 1, -1) ∈ t + @test cart(1, 1, 1) ∉ t + @test paramdim(t) == 2 + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test center(t) == cart(1, 1, 1) + @test normal(t) == vector(1, 0, 0) + @test radii(t) == (T(2) * u"m", T(1) * u"m") + @test axis(t) == Line(cart(1, 1, 1), cart(2, 1, 1)) + @test measure(t) ≈ 8 * T(π)^2 * u"m^2" + @test_throws ArgumentError length(t) + @test_throws ArgumentError volume(t) + + t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + equaltest(t) + isapproxtest(t) + + # torus passing through three points + p₁ = cart(0, 0, 0) + p₂ = cart(1, 2, 3) + p₃ = cart(3, 2, 1) + t = Torus(p₁, p₂, p₃, T(1)) + c = center(t) + R, r = radii(t) + @test r == T(1) * u"m" + @test norm(p₁ - c) ≈ R + @test norm(p₂ - c) ≈ R + @test norm(p₃ - c) ≈ R + @test p₁ ∈ t + @test p₂ ∈ t + @test p₃ ∈ t + + # constructor with tuples + c₁ = T.((0, 0, 0)) + c₂ = T.((1, 2, 3)) + c₃ = T.((3, 2, 1)) + q = Torus(c₁, c₂, c₃, 1) + @test q == t + + t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + @test sprint(show, t) == + "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" + if T === Float32 + @test sprint(show, MIME("text/plain"), t) == """ + Torus + ├─ center: Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) + ├─ normal: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) + ├─ major: 2.0f0 m + └─ minor: 1.0f0 m""" + else + @test sprint(show, MIME("text/plain"), t) == """ + Torus + ├─ center: Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) + ├─ normal: Vec(1.0 m, 0.0 m, 0.0 m) + ├─ major: 2.0 m + └─ minor: 1.0 m""" end end diff --git a/test/rand.jl b/test/rand.jl index 1be23217d..d746ab2f2 100644 --- a/test/rand.jl +++ b/test/rand.jl @@ -1,4 +1,4 @@ -@testset "rand" begin +@testitem "rand" setup = [Setup] begin p = rand(Point) @test p isa Point @test crs(p) <: Cartesian3D diff --git a/test/refinement.jl b/test/refinement.jl index 211b13f65..6b4a85944 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -1,143 +1,141 @@ -@testset "Refinement" begin - @testset "TriRefinement" begin - grid = cartgrid(3, 3) - ref1 = refine(grid, TriRefinement()) - ref2 = refine(ref1, TriRefinement()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], grid, showsegments=true) - viz(fig[1, 2], ref1, showsegments=true) - viz(fig[1, 3], ref2, showsegments=true) - @test_reference "data/trirefine-$T.png" fig - end - - # CRS propagation - grid = CartesianGrid((3, 3), merc(0, 0), (T(1), T(1))) - ref = refine(grid, TriRefinement()) - @test crs(ref) === crs(grid) +@testitem "TriRefinement" setup = [Setup] begin + grid = cartgrid(3, 3) + ref1 = refine(grid, TriRefinement()) + ref2 = refine(ref1, TriRefinement()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], grid, showsegments=true) + viz(fig[1, 2], ref1, showsegments=true) + viz(fig[1, 3], ref2, showsegments=true) + @test_reference "data/trirefine-$T.png" fig end - @testset "QuadRefinement" begin - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) - connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) - mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, QuadRefinement()) - ref2 = refine(ref1, QuadRefinement()) - ref3 = refine(ref2, QuadRefinement()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showsegments=true) - viz(fig[1, 2], ref2, showsegments=true) - viz(fig[1, 3], ref3, showsegments=true) - @test_reference "data/quadrefine-$T.png" fig - end - - # CRS propagation - points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) - connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) - mesh = SimpleMesh(points, connec) - ref = refine(mesh, QuadRefinement()) - @test crs(ref) === crs(mesh) + # CRS propagation + grid = CartesianGrid((3, 3), merc(0, 0), (T(1), T(1))) + ref = refine(grid, TriRefinement()) + @test crs(ref) === crs(grid) +end + +@testitem "QuadRefinement" setup = [Setup] begin + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) + connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) + mesh = SimpleMesh(points, connec) + ref1 = refine(mesh, QuadRefinement()) + ref2 = refine(ref1, QuadRefinement()) + ref3 = refine(ref2, QuadRefinement()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) + @test_reference "data/quadrefine-$T.png" fig + end + + # CRS propagation + points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) + connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, QuadRefinement()) + @test crs(ref) === crs(mesh) +end + +@testitem "RegularRefinement" setup = [Setup] begin + # 2D grids + grid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(10, 10)) + tgrid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(20, 20)) + @test refine(grid, RegularRefinement(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test refine(rgrid, RegularRefinement(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test refine(sgrid, RegularRefinement(2)) == tsgrid + tfgrid = TransformedGrid(grid, Identity()) + @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) + + # 3D grids + grid = cartgrid(3, 3, 3) + tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(6, 6, 6)) + @test refine(grid, RegularRefinement(2)) == tgrid + rgrid = convert(RectilinearGrid, grid) + trgrid = convert(RectilinearGrid, tgrid) + @test refine(rgrid, RegularRefinement(2)) == trgrid + sgrid = convert(StructuredGrid, grid) + tsgrid = convert(StructuredGrid, tgrid) + @test refine(sgrid, RegularRefinement(2)) == tsgrid + tfgrid = TransformedGrid(grid, Identity()) + @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) +end + +@testitem "CatmullClark" setup = [Setup] begin + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) + mesh = SimpleMesh(points, connec) + ref1 = refine(mesh, CatmullClark()) + ref2 = refine(ref1, CatmullClark()) + ref3 = refine(ref2, CatmullClark()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) + @test_reference "data/catmullclark-1-$T.png" fig end - @testset "RegularRefinement" begin - # 2D grids - grid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(10, 10)) - tgrid = CartesianGrid(cart(0, 0), cart(10, 10), dims=(20, 20)) - @test refine(grid, RegularRefinement(2)) == tgrid - rgrid = convert(RectilinearGrid, grid) - trgrid = convert(RectilinearGrid, tgrid) - @test refine(rgrid, RegularRefinement(2)) == trgrid - sgrid = convert(StructuredGrid, grid) - tsgrid = convert(StructuredGrid, tgrid) - @test refine(sgrid, RegularRefinement(2)) == tsgrid - tfgrid = TransformedGrid(grid, Identity()) - @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) - - # 3D grids - grid = cartgrid(3, 3, 3) - tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(6, 6, 6)) - @test refine(grid, RegularRefinement(2)) == tgrid - rgrid = convert(RectilinearGrid, grid) - trgrid = convert(RectilinearGrid, tgrid) - @test refine(rgrid, RegularRefinement(2)) == trgrid - sgrid = convert(StructuredGrid, grid) - tsgrid = convert(StructuredGrid, tgrid) - @test refine(sgrid, RegularRefinement(2)) == tsgrid - tfgrid = TransformedGrid(grid, Identity()) - @test refine(tfgrid, RegularRefinement(2)) == refine(grid, RegularRefinement(2)) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) + connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) + mesh = SimpleMesh(points, connec) + ref1 = refine(mesh, CatmullClark()) + ref2 = refine(ref1, CatmullClark()) + ref3 = refine(ref2, CatmullClark()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) + @test_reference "data/catmullclark-2-$T.png" fig end - @testset "CatmullClark" begin - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) - mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showsegments=true) - viz(fig[1, 2], ref2, showsegments=true) - viz(fig[1, 3], ref3, showsegments=true) - @test_reference "data/catmullclark-1-$T.png" fig - end - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) - connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) - mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showsegments=true) - viz(fig[1, 2], ref2, showsegments=true) - viz(fig[1, 3], ref3, showsegments=true) - @test_reference "data/catmullclark-2-$T.png" fig - end - - points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) - connec = connect.([(1, 4, 3, 2), (5, 6, 7, 8), (1, 2, 6, 5), (3, 4, 8, 7), (1, 5, 8, 4), (2, 3, 7, 6)]) - mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showsegments=true) - viz(fig[1, 2], ref2, showsegments=true) - viz(fig[1, 3], ref3, showsegments=true) - @test_reference "data/catmullclark-3-$T.png" fig - end - - # CRS propagation - points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) - mesh = SimpleMesh(points, connec) - ref = refine(mesh, CatmullClark()) - @test crs(ref) === crs(mesh) + points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) + connec = connect.([(1, 4, 3, 2), (5, 6, 7, 8), (1, 2, 6, 5), (3, 4, 8, 7), (1, 5, 8, 4), (2, 3, 7, 6)]) + mesh = SimpleMesh(points, connec) + ref1 = refine(mesh, CatmullClark()) + ref2 = refine(ref1, CatmullClark()) + ref3 = refine(ref2, CatmullClark()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) + @test_reference "data/catmullclark-3-$T.png" fig end - @testset "TriSubdivision" begin - points = cart.([(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)]) - connec = connect.([(1, 2, 3), (3, 2, 4), (4, 2, 1), (1, 3, 4)]) - mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, TriSubdivision()) - ref2 = refine(ref1, TriSubdivision()) - ref3 = refine(ref2, TriSubdivision()) - - if visualtests - fig = Mke.Figure(size=(900, 300)) - viz(fig[1, 1], ref1, showsegments=true) - viz(fig[1, 2], ref2, showsegments=true) - viz(fig[1, 3], ref3, showsegments=true) - @test_reference "data/trisubdivision-$T.png" fig - end + # CRS propagation + points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, CatmullClark()) + @test crs(ref) === crs(mesh) +end + +@testitem "TriSubdivision" setup = [Setup] begin + points = cart.([(-1, -1, -1), (1, 1, -1), (1, -1, 1), (-1, 1, 1)]) + connec = connect.([(1, 2, 3), (3, 2, 4), (4, 2, 1), (1, 3, 4)]) + mesh = SimpleMesh(points, connec) + ref1 = refine(mesh, TriSubdivision()) + ref2 = refine(ref1, TriSubdivision()) + ref3 = refine(ref2, TriSubdivision()) + + if visualtests + fig = Mke.Figure(size=(900, 300)) + viz(fig[1, 1], ref1, showsegments=true) + viz(fig[1, 2], ref2, showsegments=true) + viz(fig[1, 3], ref3, showsegments=true) + @test_reference "data/trisubdivision-$T.png" fig end end diff --git a/test/runtests.jl b/test/runtests.jl index 2d222e0e2..aec6b958d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,119 +1,45 @@ -using Meshes -using Tables -using Distances -using Statistics -using LinearAlgebra -using CoordRefSystems -using CategoricalArrays -using CircularArrays -using StaticArrays -using SparseArrays -using PlyIO -using Unitful -using Rotations -using Test, StableRNGs -using ReferenceTests, ImageIO - -using TransformsBase: Identity, → - -import TransformsBase as TB -import CairoMakie as Mke - -# environment settings -isCI = "CI" ∈ keys(ENV) -islinux = Sys.islinux() -visualtests = !isCI || (isCI && islinux) -datadir = joinpath(@__DIR__, "data") - -# dummy definitions -include("dummy.jl") - -# helper functions -include("testutils.jl") - -cart(args...) = cart(T, args...) - -merc(args...) = merc(T, args...) - -latlon(args...) = latlon(T, args...) - -vector(args...) = vector(T, args...) - -cartgrid(args...) = cartgrid(T, args...) - -randpoint1(n) = randcart(T, 1, n) -randpoint2(n) = randcart(T, 2, n) -randpoint3(n) = randcart(T, 3, n) - -# list of tests -testfiles = [ - "vectors.jl", - "primitives.jl", - "polytopes.jl", - "multigeoms.jl", - "transformedgeoms.jl", - "connectivities.jl", - "topologies.jl", - "toporelations.jl", - "domains.jl", - "subdomains.jl", - "sets.jl", - "meshes.jl", - "trajecs.jl", - "crs.jl", - "utils.jl", - "viewing.jl", - "partitioning.jl", - "sorting.jl", - "traversing.jl", - "neighborhoods.jl", - "neighborsearch.jl", - "predicates.jl", - "winding.jl", - "sideof.jl", - "orientation.jl", - "merging.jl", - "clipping.jl", - "clamping.jl", - "intersections.jl", - "complement.jl", - "simplification.jl", - "boundingboxes.jl", - "hulls.jl", - "sampling.jl", - "pointification.jl", - "tesselation.jl", - "discretization.jl", - "refinement.jl", - "coarsening.jl", - "transforms.jl", - "rand.jl", - "distances.jl", - "supportfun.jl", - "matrices.jl", - "tolerances.jl" -] - -# -------------------------------- -# RUN TESTS WITH SINGLE PRECISION -# -------------------------------- -T = Float32 -ℳ = Meshes.Met{T} -@testset "Meshes.jl ($T)" begin - for testfile in testfiles - println("Testing $testfile...") - include(testfile) +using TestItems +using TestItemRunner + +@run_package_tests + +@testsnippet Setup begin + using Tables + using Distances + using Statistics + using LinearAlgebra + using CoordRefSystems + using CategoricalArrays + using CircularArrays + using StaticArrays + using SparseArrays + using PlyIO + using Unitful + using Rotations + using StableRNGs + using ReferenceTests, ImageIO + + using TransformsBase: Identity, → + + import TransformsBase as TB + import CairoMakie as Mke + + # environment settings + isCI = "CI" ∈ keys(ENV) + islinux = Sys.islinux() + visualtests = !isCI || (isCI && islinux) + datadir = joinpath(@__DIR__, "data") + + # float settings + T = if isCI + if ENV["FLOAT_TYPE"] == "Float32" + Float32 + elseif ENV["FLOAT_TYPE"] == "Float64" + Float64 + end + else + Float64 end -end -# -------------------------------- -# RUN TESTS WITH DOUBLE PRECISION -# -------------------------------- -T = Float64 -ℳ = Meshes.Met{T} -@testset "Meshes.jl ($T)" begin - for testfile in testfiles - println("Testing $testfile...") - include(testfile) - end + include("testutils.jl") end diff --git a/test/sampling.jl b/test/sampling.jl index d46bb3c25..5d498f0c3 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -1,443 +1,436 @@ -@testset "Sampling" begin - @testset "UniformSampling" begin - rng = StableRNG(123) - d = cartgrid(100, 100) - s = sample(rng, d, UniformSampling(100)) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - - # availability of option ordered - s = sample(rng, d, UniformSampling(100, ordered=true)) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - end - - @testset "WeightedSampling" begin - # uniform weights => uniform sampler - rng = StableRNG(123) - d = cartgrid(100, 100) - s = sample(rng, d, WeightedSampling(100)) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - - # availability of option ordered - s = sample(rng, d, WeightedSampling(100, ordered=true)) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - - # utility method - s = sample(rng, d, 100, ordered=true) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - s = sample(rng, d, 100, fill(1, 10000), ordered=true) - μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) - @test nelements(s) == 100 - @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") - end - - @testset "BallSampling" begin - d = cartgrid(100, 100) - s = sample(d, BallSampling(T(10))) - n = nelements(s) - x = to(centroid(s, 1)) - y = to(centroid(s, 17)) - @test n < 100 - @test sqrt(sum((x - y) .^ 2)) ≥ T(10) * u"m" - - d = cartgrid(100, 100) - s = sample(d, BallSampling(T(20))) - n = nelements(s) - x = to(centroid(s, 1)) - y = to(centroid(s, 17)) - @test n < 50 - @test sqrt(sum((x - y) .^ 2)) ≥ T(20) * u"m" - end +@testitem "UniformSampling" setup = [Setup] begin + rng = StableRNG(123) + d = cartgrid(100, 100) + s = sample(rng, d, UniformSampling(100)) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") + + # availability of option ordered + s = sample(rng, d, UniformSampling(100, ordered=true)) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") +end - @testset "BlockSampling" begin - g = cartgrid(100, 100) - s = sample(g, BlockSampling(T(10))) - @test nelements(s) == 100 - x = to.(centroid.(s)) - D = pairwise(Euclidean(), x) - d = [D[i, j] for i in 1:length(x) for j in 1:(i - 1)] - @test all(≥(T(10) * u"m"), d) - end +@testitem "WeightedSampling" setup = [Setup] begin + # uniform weights => uniform sampler + rng = StableRNG(123) + d = cartgrid(100, 100) + s = sample(rng, d, WeightedSampling(100)) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") + + # availability of option ordered + s = sample(rng, d, WeightedSampling(100, ordered=true)) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") + + # utility method + s = sample(rng, d, 100, ordered=true) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") + s = sample(rng, d, 100, fill(1, 10000), ordered=true) + μ = mean(to.([centroid(s, i) for i in 1:nelements(s)])) + @test nelements(s) == 100 + @test isapprox(μ, vector(50.0, 50.0), atol=T(10) * u"m") +end - @testset "RegularSampling" begin - b = Box(cart(0, 0), cart(2, 2)) - ps = sample(b, RegularSampling(3)) - @test collect(ps) == cart.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]) - ps = sample(b, RegularSampling(2, 3)) - @test collect(ps) == cart.([(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)]) - - b = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) - ps = sample(b, RegularSampling(4)) - ts = - cart.([ - (0.0, 0.0), - (0.5555555555555556, 0.1111111111111111), - (0.8888888888888888, 0.4444444444444444), - (1.0, 1.0) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end +@testitem "BallSampling" setup = [Setup] begin + d = cartgrid(100, 100) + s = sample(d, BallSampling(T(10))) + n = nelements(s) + x = to(centroid(s, 1)) + y = to(centroid(s, 17)) + @test n < 100 + @test sqrt(sum((x - y) .^ 2)) ≥ T(10) * u"m" + + d = cartgrid(100, 100) + s = sample(d, BallSampling(T(20))) + n = nelements(s) + x = to(centroid(s, 1)) + y = to(centroid(s, 17)) + @test n < 50 + @test sqrt(sum((x - y) .^ 2)) ≥ T(20) * u"m" +end - s = Sphere(cart(0, 0), T(2)) - ps = sample(s, RegularSampling(4)) - ts = cart.([(2, 0), (0, 2), (-2, 0), (0, -2)]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end +@testitem "BlockSampling" setup = [Setup] begin + g = cartgrid(100, 100) + s = sample(g, BlockSampling(T(10))) + @test nelements(s) == 100 + x = to.(centroid.(s)) + D = pairwise(Euclidean(), x) + d = [D[i, j] for i in 1:length(x) for j in 1:(i - 1)] + @test all(≥(T(10) * u"m"), d) +end - s = Sphere(cart(0, 0, 0), T(2)) - ps = sample(s, RegularSampling(2, 2)) - ts = - cart.([ - (1.7320508075688772, 0.0, 1.0), - (1.7320508075688772, 0.0, -1.0), - (-1.7320508075688772, 0.0, 1.0), - (-1.7320508075688772, 0.0, -1.0), - (0.0, 0.0, 2.0), - (0.0, 0.0, -2.0) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end +@testitem "RegularSampling" setup = [Setup] begin + b = Box(cart(0, 0), cart(2, 2)) + ps = sample(b, RegularSampling(3)) + @test collect(ps) == cart.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]) + ps = sample(b, RegularSampling(2, 3)) + @test collect(ps) == cart.([(0, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)]) + + b = BezierCurve([cart(0, 0), cart(1, 0), cart(1, 1)]) + ps = sample(b, RegularSampling(4)) + ts = + cart.([(0.0, 0.0), (0.5555555555555556, 0.1111111111111111), (0.8888888888888888, 0.4444444444444444), (1.0, 1.0)]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - e = Ellipsoid((T(3), T(2), T(1)), cart(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) - ps = sample(e, RegularSampling(2, 2)) - ts = - cart.([ - (2.725814800973295, 2.225814800973295, -0.5871173070873834), - (1.872261410380021, 2.372261410380021, -1.0871173070873832), - (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), - (-0.725814800973295, -0.22581480097329454, 2.587117307087383), - (1.8535533905932737, 0.8535533905932737, 1.5), - (0.14644660940672627, 1.1464466094067263, 0.4999999999999999) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end + s = Sphere(cart(0, 0), T(2)) + ps = sample(s, RegularSampling(4)) + ts = cart.([(2, 0), (0, 2), (-2, 0), (0, -2)]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - b = Ball(cart(0, 0), T(2)) - ps = sample(b, RegularSampling(3, 4)) - @test all(∈(b), ps) - ts = - cart.([ - (0.6666666666666666, 0.0), - (1.3333333333333333, 0.0), - (2.0, 0.0), - (0.0, 0.6666666666666666), - (0.0, 1.3333333333333333), - (0.0, 2.0), - (-0.6666666666666666, 0.0), - (-1.3333333333333333, 0.0), - (-2.0, 0.0), - (0.0, -0.6666666666666666), - (0.0, -1.3333333333333333), - (0.0, -2.0), - (0.0, 0.0) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end + s = Sphere(cart(0, 0, 0), T(2)) + ps = sample(s, RegularSampling(2, 2)) + ts = + cart.([ + (1.7320508075688772, 0.0, 1.0), + (1.7320508075688772, 0.0, -1.0), + (-1.7320508075688772, 0.0, 1.0), + (-1.7320508075688772, 0.0, -1.0), + (0.0, 0.0, 2.0), + (0.0, 0.0, -2.0) + ]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - b = Ball(cart(10, 10), T(2)) - ps = sample(b, RegularSampling(4, 3)) - @test all(∈(b), ps) - ts = - cart.([ - (10.5, 10.0), - (11.0, 10.0), - (11.5, 10.0), - (12.0, 10.0), - (9.75, 10.433012701892219), - (9.5, 10.86602540378444), - (9.25, 11.299038105676658), - (9.0, 11.732050807568877), - (9.75, 9.566987298107781), - (9.5, 9.13397459621556), - (9.25, 8.700961894323342), - (9.0, 8.267949192431121), - (10.0, 10.0) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end + e = Ellipsoid((T(3), T(2), T(1)), cart(1, 1, 1), RotZYX(T(π / 4), T(π / 4), T(π / 4))) + ps = sample(e, RegularSampling(2, 2)) + ts = + cart.([ + (2.725814800973295, 2.225814800973295, -0.5871173070873834), + (1.872261410380021, 2.372261410380021, -1.0871173070873832), + (0.12773858961997864, -0.37226141038002103, 3.0871173070873836), + (-0.725814800973295, -0.22581480097329454, 2.587117307087383), + (1.8535533905932737, 0.8535533905932737, 1.5), + (0.14644660940672627, 1.1464466094067263, 0.4999999999999999) + ]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - b = Ball(cart(0, 0, 0), T(2)) - ps = sample(b, RegularSampling(3, 2, 3)) - @test all(∈(b), ps) - ts = - cart.([ - (0.5773502691896257, 0.0, 0.3333333333333333), - (1.1547005383792515, 0.0, 0.6666666666666666), - (1.7320508075688772, 0.0, 1.0), - (0.5773502691896256, 0.0, -0.3333333333333335), - (1.1547005383792512, 0.0, -0.666666666666667), - (1.732050807568877, 0.0, -1.0000000000000004), - (-0.288675134594813, 0.4999999999999999, 0.3333333333333333), - (-0.577350269189626, 0.9999999999999998, 0.6666666666666666), - (-0.8660254037844389, 1.4999999999999996, 1.0), - (-0.2886751345948129, 0.4999999999999998, -0.3333333333333335), - (-0.5773502691896258, 0.9999999999999996, -0.666666666666667), - (-0.8660254037844388, 1.4999999999999993, -1.0000000000000004), - (-0.28867513459481264, -0.5000000000000001, 0.3333333333333333), - (-0.5773502691896253, -1.0000000000000002, 0.6666666666666666), - (-0.8660254037844379, -1.5000000000000004, 1.0), - (-0.2886751345948126, -0.5, -0.3333333333333335), - (-0.5773502691896252, -1.0, -0.666666666666667), - (-0.8660254037844378, -1.5000000000000002, -1.0000000000000004), - (0.0, 0.0, 0.0) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end + b = Ball(cart(0, 0), T(2)) + ps = sample(b, RegularSampling(3, 4)) + @test all(∈(b), ps) + ts = + cart.([ + (0.6666666666666666, 0.0), + (1.3333333333333333, 0.0), + (2.0, 0.0), + (0.0, 0.6666666666666666), + (0.0, 1.3333333333333333), + (0.0, 2.0), + (-0.6666666666666666, 0.0), + (-1.3333333333333333, 0.0), + (-2.0, 0.0), + (0.0, -0.6666666666666666), + (0.0, -1.3333333333333333), + (0.0, -2.0), + (0.0, 0.0) + ]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - b = Ball(cart(10, 10, 10), T(2)) - ps = sample(b, RegularSampling(3, 2, 3)) - @test all(∈(b), ps) - - # cylinder with parallel planes - c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) - ps = sample(c, RegularSampling(2, 20, 10)) - cs = to.(ps) - xs = getindex.(cs, 1) - ys = getindex.(cs, 2) - zs = getindex.(cs, 3) - @test length(cs) == 200 + 200 + 10 - @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) - @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) - @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) - - # cylinder surface with parallel planes - c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) - ps = sample(c, RegularSampling(20, 10)) - cs = to.(ps) - xs = getindex.(cs, 1) - ys = getindex.(cs, 2) - zs = getindex.(cs, 3) - @test length(cs) == 200 + 2 - @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) - @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) - @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) - - # cylinder surface with parallel shifted planes - c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) - ps = sample(c, RegularSampling(20, 10)) - cs = to.(ps) - xs = getindex.(cs, 1) - ys = getindex.(cs, 2) - zs = getindex.(cs, 3) - @test length(cs) == 200 + 2 - - # cylinder surface with non-parallel planes - c = CylinderSurface(Plane(cart(0, 0, 0), vector(1, 0, 1)), Plane(cart(1, 1, 1), vector(0, 1, 1)), T(1)) - ps = sample(c, RegularSampling(20, 10)) - cs = to.(ps) - @test length(cs) == 200 + 2 - - s = Segment(cart(0, 0), cart(1, 1)) - ps = sample(s, RegularSampling(2)) - @test collect(ps) == cart.([(0, 0), (1, 1)]) - ps = sample(s, RegularSampling(3)) - @test collect(ps) == cart.([(0, 0), (0.5, 0.5), (1, 1)]) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - ps = sample(q, RegularSampling(2, 2)) - @test collect(ps) == cart.([(0, 0), (1, 0), (0, 1), (1, 1)]) - ps = sample(q, RegularSampling(3, 3)) - @test collect(ps) == cart.([(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)]) - - h = Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - ps = sample(h, RegularSampling(2, 2, 2)) - @test collect(ps) == cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) - ps = sample(h, RegularSampling(3, 2, 2)) - @test collect(ps) == - cart.([ - (0, 0, 0), - (0.5, 0, 0), - (1, 0, 0), - (0, 1, 0), - (0.5, 1, 0), - (1, 1, 0), - (0, 0, 1), - (0.5, 0, 1), - (1, 0, 1), - (0, 1, 1), - (0.5, 1, 1), - (1, 1, 1) + b = Ball(cart(10, 10), T(2)) + ps = sample(b, RegularSampling(4, 3)) + @test all(∈(b), ps) + ts = + cart.([ + (10.5, 10.0), + (11.0, 10.0), + (11.5, 10.0), + (12.0, 10.0), + (9.75, 10.433012701892219), + (9.5, 10.86602540378444), + (9.25, 11.299038105676658), + (9.0, 11.732050807568877), + (9.75, 9.566987298107781), + (9.5, 9.13397459621556), + (9.25, 8.700961894323342), + (9.0, 8.267949192431121), + (10.0, 10.0) ]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - torus = Torus(cart(0, 0, 0), vector(1, 0, 0), T(2), T(1)) - ps = sample(torus, RegularSampling(3, 3)) - ts = - cart.([ - (0, 0, -3), - (-sqrt(3) / 2, 0, -1.5), - (sqrt(3) / 2, 0, -1.5), - (0, 3sqrt(3) / 2, 1.5), - (-sqrt(3) / 2, 3sqrt(3) / 4, 0.75), - (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), - (0, -3sqrt(3) / 2, 1.5), - (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75), - (sqrt(3) / 2, -3sqrt(3) / 4, 0.75) - ]) - for (p, t) in zip(ps, ts) - @test p ≈ t - end + b = Ball(cart(0, 0, 0), T(2)) + ps = sample(b, RegularSampling(3, 2, 3)) + @test all(∈(b), ps) + ts = + cart.([ + (0.5773502691896257, 0.0, 0.3333333333333333), + (1.1547005383792515, 0.0, 0.6666666666666666), + (1.7320508075688772, 0.0, 1.0), + (0.5773502691896256, 0.0, -0.3333333333333335), + (1.1547005383792512, 0.0, -0.666666666666667), + (1.732050807568877, 0.0, -1.0000000000000004), + (-0.288675134594813, 0.4999999999999999, 0.3333333333333333), + (-0.577350269189626, 0.9999999999999998, 0.6666666666666666), + (-0.8660254037844389, 1.4999999999999996, 1.0), + (-0.2886751345948129, 0.4999999999999998, -0.3333333333333335), + (-0.5773502691896258, 0.9999999999999996, -0.666666666666667), + (-0.8660254037844388, 1.4999999999999993, -1.0000000000000004), + (-0.28867513459481264, -0.5000000000000001, 0.3333333333333333), + (-0.5773502691896253, -1.0000000000000002, 0.6666666666666666), + (-0.8660254037844379, -1.5000000000000004, 1.0), + (-0.2886751345948126, -0.5, -0.3333333333333335), + (-0.5773502691896252, -1.0, -0.666666666666667), + (-0.8660254037844378, -1.5000000000000002, -1.0000000000000004), + (0.0, 0.0, 0.0) + ]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end - grid = cartgrid(10, 10) - points = sample(grid, RegularSampling(100, 200)) - @test length(collect(points)) == 20000 + b = Ball(cart(10, 10, 10), T(2)) + ps = sample(b, RegularSampling(3, 2, 3)) + @test all(∈(b), ps) + + # cylinder with parallel planes + c = Cylinder(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) + ps = sample(c, RegularSampling(2, 20, 10)) + cs = to.(ps) + xs = getindex.(cs, 1) + ys = getindex.(cs, 2) + zs = getindex.(cs, 3) + @test length(cs) == 200 + 200 + 10 + @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) + @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) + @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) + + # cylinder surface with parallel planes + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(0, 0, 1), vector(0, 0, 1)), T(1)) + ps = sample(c, RegularSampling(20, 10)) + cs = to.(ps) + xs = getindex.(cs, 1) + ys = getindex.(cs, 2) + zs = getindex.(cs, 3) + @test length(cs) == 200 + 2 + @test all(-oneunit(ℳ) ≤ x ≤ oneunit(ℳ) for x in xs) + @test all(-oneunit(ℳ) ≤ y ≤ oneunit(ℳ) for y in ys) + @test all(zero(ℳ) ≤ z ≤ oneunit(ℳ) for z in zs) + + # cylinder surface with parallel shifted planes + c = CylinderSurface(Plane(cart(0, 0, 0), vector(0, 0, 1)), Plane(cart(1, 1, 1), vector(0, 0, 1)), T(1)) + ps = sample(c, RegularSampling(20, 10)) + cs = to.(ps) + xs = getindex.(cs, 1) + ys = getindex.(cs, 2) + zs = getindex.(cs, 3) + @test length(cs) == 200 + 2 + + # cylinder surface with non-parallel planes + c = CylinderSurface(Plane(cart(0, 0, 0), vector(1, 0, 1)), Plane(cart(1, 1, 1), vector(0, 1, 1)), T(1)) + ps = sample(c, RegularSampling(20, 10)) + cs = to.(ps) + @test length(cs) == 200 + 2 + + s = Segment(cart(0, 0), cart(1, 1)) + ps = sample(s, RegularSampling(2)) + @test collect(ps) == cart.([(0, 0), (1, 1)]) + ps = sample(s, RegularSampling(3)) + @test collect(ps) == cart.([(0, 0), (0.5, 0.5), (1, 1)]) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + ps = sample(q, RegularSampling(2, 2)) + @test collect(ps) == cart.([(0, 0), (1, 0), (0, 1), (1, 1)]) + ps = sample(q, RegularSampling(3, 3)) + @test collect(ps) == cart.([(0, 0), (0.5, 0), (1, 0), (0, 0.5), (0.5, 0.5), (1, 0.5), (0, 1), (0.5, 1), (1, 1)]) + + h = Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + ps = sample(h, RegularSampling(2, 2, 2)) + @test collect(ps) == cart.([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]) + ps = sample(h, RegularSampling(3, 2, 2)) + @test collect(ps) == + cart.([ + (0, 0, 0), + (0.5, 0, 0), + (1, 0, 0), + (0, 1, 0), + (0.5, 1, 0), + (1, 1, 0), + (0, 0, 1), + (0.5, 0, 1), + (1, 0, 1), + (0, 1, 1), + (0.5, 1, 1), + (1, 1, 1) + ]) + + torus = Torus(cart(0, 0, 0), vector(1, 0, 0), T(2), T(1)) + ps = sample(torus, RegularSampling(3, 3)) + ts = + cart.([ + (0, 0, -3), + (-sqrt(3) / 2, 0, -1.5), + (sqrt(3) / 2, 0, -1.5), + (0, 3sqrt(3) / 2, 1.5), + (-sqrt(3) / 2, 3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, 3sqrt(3) / 4, 0.75), + (0, -3sqrt(3) / 2, 1.5), + (-sqrt(3) / 2, -3sqrt(3) / 4, 0.75), + (sqrt(3) / 2, -3sqrt(3) / 4, 0.75) + ]) + for (p, t) in zip(ps, ts) + @test p ≈ t end - @testset "HomogeneousSampling" begin - s = Segment(cart(0, 0), cart(1, 0)) - ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in to.(ps)) - @test all(coords[2] == zero(ℳ) for coords in to.(ps)) - - s = Segment(cart(0, 0), cart(0, 1)) - ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(coords[1] == zero(ℳ) for coords in to.(ps)) - @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) - - s = Segment(cart(0, 0), cart(1, 1)) - ps = sample(s, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) - - c = Rope(cart(0, 0), cart(1, 0), cart(0, 1), cart(1, 1)) - ps = sample(c, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all( - coords[1] + coords[2] == oneunit(ℳ) || (zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) && coords[2] ∈ [zero(ℳ), oneunit(ℳ)]) for - coords in to.(ps) - ) + grid = cartgrid(10, 10) + points = sample(grid, RegularSampling(100, 200)) + @test length(collect(points)) == 20000 +end - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - ps = sample(t, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(∈(t), ps) - - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - ps = sample(q, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(∈(q), ps) - - b = Ball(cart(10, 10), T(3)) - ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(∈(b), ps) - - b = Ball(cart(10, 10, 10), T(10)) - ps = sample(b, HomogeneousSampling(100)) - @test first(ps) isa Point - @test all(∈(b), ps) - - poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) - multi = Multi([poly1, poly2]) - ps = sample(multi, HomogeneousSampling(100)) - @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) - connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) - mesh = SimpleMesh(points, connec) - ps = sample(mesh, HomogeneousSampling(400)) - @test first(ps) isa Point - @test all(∈(mesh), ps) - ps = sample(mesh, HomogeneousSampling(400, 1:nelements(mesh))) - @test first(ps) isa Point - @test all(∈(mesh), ps) - end +@testitem "HomogeneousSampling" setup = [Setup] begin + s = Segment(cart(0, 0), cart(1, 0)) + ps = sample(s, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) for coords in to.(ps)) + @test all(coords[2] == zero(ℳ) for coords in to.(ps)) + + s = Segment(cart(0, 0), cart(0, 1)) + ps = sample(s, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(coords[1] == zero(ℳ) for coords in to.(ps)) + @test all(zero(ℳ) ≤ coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) + + s = Segment(cart(0, 0), cart(1, 1)) + ps = sample(s, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(zero(ℳ) ≤ coords[1] == coords[2] ≤ oneunit(ℳ) for coords in to.(ps)) + + c = Rope(cart(0, 0), cart(1, 0), cart(0, 1), cart(1, 1)) + ps = sample(c, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all( + coords[1] + coords[2] == oneunit(ℳ) || (zero(ℳ) ≤ coords[1] ≤ oneunit(ℳ) && coords[2] ∈ [zero(ℳ), oneunit(ℳ)]) for + coords in to.(ps) + ) + + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + ps = sample(t, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(∈(t), ps) + + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + ps = sample(q, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(∈(q), ps) + + b = Ball(cart(10, 10), T(3)) + ps = sample(b, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(∈(b), ps) + + b = Ball(cart(10, 10, 10), T(10)) + ps = sample(b, HomogeneousSampling(100)) + @test first(ps) isa Point + @test all(∈(b), ps) + + poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) + multi = Multi([poly1, poly2]) + ps = sample(multi, HomogeneousSampling(100)) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) + mesh = SimpleMesh(points, connec) + ps = sample(mesh, HomogeneousSampling(400)) + @test first(ps) isa Point + @test all(∈(mesh), ps) + ps = sample(mesh, HomogeneousSampling(400, 1:nelements(mesh))) + @test first(ps) isa Point + @test all(∈(mesh), ps) +end - @testset "MinDistanceSampling" begin - poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) - multi = Multi([poly1, poly2]) - ps = sample(multi, MinDistanceSampling(0.1)) - @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) - - points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) - connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) - mesh = SimpleMesh(points, connec) - ps = sample(mesh, MinDistanceSampling(0.2)) - n = length(ps) - @test first(ps) isa Point - @test all(∈(mesh), ps) - @test all(norm(ps[i] - ps[j]) ≥ T(0.2) * u"m" for i in 1:n for j in (i + 1):n) - - # geometries with almost zero measure - # can still be sampled (at least one point) - poly = PolyArea(cart.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) - ps = sample(poly, MinDistanceSampling(3.2423333333753135e-5)) - @test length(ps) > 0 +@testitem "MinDistanceSampling" setup = [Setup] begin + poly1 = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + poly2 = PolyArea(cart.([(1, 1), (2, 1), (2, 2), (1, 2)])) + multi = Multi([poly1, poly2]) + ps = sample(multi, MinDistanceSampling(0.1)) + @test all(p -> (cart(0, 0) ⪯ p ⪯ cart(1, 1)) || (cart(1, 1) ⪯ p ⪯ cart(2, 2)), ps) + + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.5), (0.75, 0.5)]) + connec = connect.([(3, 1, 5), (4, 6, 2), (1, 2, 6, 5), (5, 6, 4, 3)]) + mesh = SimpleMesh(points, connec) + ps = sample(mesh, MinDistanceSampling(0.2)) + n = length(ps) + @test first(ps) isa Point + @test all(∈(mesh), ps) + @test all(norm(ps[i] - ps[j]) ≥ T(0.2) * u"m" for i in 1:n for j in (i + 1):n) + + # geometries with almost zero measure + # can still be sampled (at least one point) + poly = PolyArea(cart.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) + ps = sample(poly, MinDistanceSampling(3.2423333333753135e-5)) + @test length(ps) > 0 +end + +@testitem "RNGs" setup = [Setup] begin + dom = cartgrid(100, 100) + for method in [UniformSampling(100), WeightedSampling(100), BallSampling(T(10))] + rng = StableRNG(2021) + s1 = sample(rng, dom, method) + rng = StableRNG(2021) + s2 = sample(rng, dom, method) + @test collect(s1) == collect(s2) end - @testset "RNGs" begin - dom = cartgrid(100, 100) - for method in [UniformSampling(100), WeightedSampling(100), BallSampling(T(10))] + # cannot test some sampling methods with T = Float32 + # because of https://github.com/JuliaStats/StatsBase.jl/issues/695 + if T == Float64 + for method in [HomogeneousSampling(100), MinDistanceSampling(T(5))] rng = StableRNG(2021) s1 = sample(rng, dom, method) rng = StableRNG(2021) s2 = sample(rng, dom, method) @test collect(s1) == collect(s2) end + end - # cannot test some sampling methods with T = Float32 - # because of https://github.com/JuliaStats/StatsBase.jl/issues/695 - if T == Float64 - for method in [HomogeneousSampling(100), MinDistanceSampling(T(5))] - rng = StableRNG(2021) - s1 = sample(rng, dom, method) - rng = StableRNG(2021) - s2 = sample(rng, dom, method) - @test collect(s1) == collect(s2) - end - end - - method = RegularSampling(10) - for geom in [ - Box(cart(0, 0), cart(2, 2)) - Sphere(cart(0, 0), T(2)) - Ball(cart(0, 0), T(2)) - Segment(cart(0, 0), cart(1, 1)) - Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - Hexahedron( - cart(0, 0, 0), - cart(1, 0, 0), - cart(1, 1, 0), - cart(0, 1, 0), - cart(0, 0, 1), - cart(1, 0, 1), - cart(1, 1, 1), - cart(0, 1, 1) - ) - ] - rng = StableRNG(2021) - s1 = sample(rng, geom, method) - rng = StableRNG(2021) - s2 = sample(rng, geom, method) - @test collect(s1) == collect(s2) - end + method = RegularSampling(10) + for geom in [ + Box(cart(0, 0), cart(2, 2)) + Sphere(cart(0, 0), T(2)) + Ball(cart(0, 0), T(2)) + Segment(cart(0, 0), cart(1, 1)) + Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + Hexahedron( + cart(0, 0, 0), + cart(1, 0, 0), + cart(1, 1, 0), + cart(0, 1, 0), + cart(0, 0, 1), + cart(1, 0, 1), + cart(1, 1, 1), + cart(0, 1, 1) + ) + ] + rng = StableRNG(2021) + s1 = sample(rng, geom, method) + rng = StableRNG(2021) + s2 = sample(rng, geom, method) + @test collect(s1) == collect(s2) end end diff --git a/test/sets.jl b/test/sets.jl index f6e1d6304..a5d9853f5 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -1,106 +1,104 @@ -@testset "Sets" begin - @testset "GeometrySet" begin - s = Segment(cart(0, 0), cart(1, 1)) - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - p = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - gset = GeometrySet([s, t, p]) - @test crs(gset) <: Cartesian{NoDatum} - @test Meshes.lentype(gset) == ℳ - @test [centroid(gset, i) for i in 1:3] == cart.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) +@testitem "GeometrySet" setup = [Setup] begin + s = Segment(cart(0, 0), cart(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + p = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + gset = GeometrySet([s, t, p]) + @test crs(gset) <: Cartesian{NoDatum} + @test Meshes.lentype(gset) == ℳ + @test [centroid(gset, i) for i in 1:3] == cart.([(1 / 2, 1 / 2), (1 / 3, 1 / 3), (1 / 2, 1 / 2)]) - s = Segment(cart(0, 0), cart(1, 1)) - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - geoms = [s, t] - gset1 = GeometrySet(geoms) - gset2 = GeometrySet(g for g in geoms) - @test gset1 == gset2 - @test parent(gset1) === geoms + s = Segment(cart(0, 0), cart(1, 1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + geoms = [s, t] + gset1 = GeometrySet(geoms) + gset2 = GeometrySet(g for g in geoms) + @test gset1 == gset2 + @test parent(gset1) === geoms - # make sure that eltype is inferred properly - # https://github.com/JuliaGeometry/Meshes.jl/issues/643 - geoms = Vector{Segment}() - push!(geoms, Segment(cart(0, 0), cart(1, 0))) - push!(geoms, Segment(cart(1, 0), cart(1, 1))) - push!(geoms, Segment(cart(1, 1), cart(0, 0))) - gset = GeometrySet(geoms) - @test eltype(gset) <: Segment + # make sure that eltype is inferred properly + # https://github.com/JuliaGeometry/Meshes.jl/issues/643 + geoms = Vector{Segment}() + push!(geoms, Segment(cart(0, 0), cart(1, 0))) + push!(geoms, Segment(cart(1, 0), cart(1, 1))) + push!(geoms, Segment(cart(1, 1), cart(0, 0))) + gset = GeometrySet(geoms) + @test eltype(gset) <: Segment - # conversion - grid = cartgrid(10, 10) - gset = convert(GeometrySet, grid) - @test gset isa GeometrySet - @test nelements(gset) == 100 - @test eltype(gset) <: Quadrangle - end + # conversion + grid = cartgrid(10, 10) + gset = convert(GeometrySet, grid) + @test gset isa GeometrySet + @test nelements(gset) == 100 + @test eltype(gset) <: Quadrangle +end - @testset "PointSet" begin - pset = PointSet(randpoint1(100)) - @test embeddim(pset) == 1 - @test crs(pset) <: Cartesian{NoDatum} - @test Meshes.lentype(pset) === ℳ - @test nelements(pset) == 100 - @test eltype(pset) <: Point +@testitem "PointSet" setup = [Setup] begin + pset = PointSet(randpoint1(100)) + @test embeddim(pset) == 1 + @test crs(pset) <: Cartesian{NoDatum} + @test Meshes.lentype(pset) === ℳ + @test nelements(pset) == 100 + @test eltype(pset) <: Point - pset = PointSet(randpoint2(100)) - @test embeddim(pset) == 2 - @test crs(pset) <: Cartesian{NoDatum} - @test Meshes.lentype(pset) === ℳ - @test nelements(pset) == 100 - @test eltype(pset) <: Point + pset = PointSet(randpoint2(100)) + @test embeddim(pset) == 2 + @test crs(pset) <: Cartesian{NoDatum} + @test Meshes.lentype(pset) === ℳ + @test nelements(pset) == 100 + @test eltype(pset) <: Point + + pset = PointSet(randpoint3(100)) + @test embeddim(pset) == 3 + @test crs(pset) <: Cartesian{NoDatum} + @test Meshes.lentype(pset) === ℳ + @test nelements(pset) == 100 + @test eltype(pset) <: Point - pset = PointSet(randpoint3(100)) + pset1 = PointSet([cart(1, 2, 3), cart(4, 5, 6)]) + pset2 = PointSet(cart(1, 2, 3), cart(4, 5, 6)) + pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) + pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) + @test pset1 == pset2 == pset3 == pset4 + for pset in [pset1, pset2, pset3, pset4] @test embeddim(pset) == 3 - @test crs(pset) <: Cartesian{NoDatum} @test Meshes.lentype(pset) === ℳ - @test nelements(pset) == 100 - @test eltype(pset) <: Point - - pset1 = PointSet([cart(1, 2, 3), cart(4, 5, 6)]) - pset2 = PointSet(cart(1, 2, 3), cart(4, 5, 6)) - pset3 = PointSet([T.((1, 2, 3)), T.((4, 5, 6))]) - pset4 = PointSet(T.((1, 2, 3)), T.((4, 5, 6))) - @test pset1 == pset2 == pset3 == pset4 - for pset in [pset1, pset2, pset3, pset4] - @test embeddim(pset) == 3 - @test Meshes.lentype(pset) === ℳ - @test nelements(pset) == 2 - @test pset[1] == cart(1, 2, 3) - @test pset[2] == cart(4, 5, 6) - end + @test nelements(pset) == 2 + @test pset[1] == cart(1, 2, 3) + @test pset[2] == cart(4, 5, 6) + end - pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) - @test centroid(pset) == cart(1 / 3, 1 / 3) + pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) + @test centroid(pset) == cart(1 / 3, 1 / 3) - pset = PointSet(cart.([(1, 0), (0, 1)])) - @test nelements(pset) == 2 - @test centroid(pset, 1) == cart(1, 0) - @test centroid(pset, 2) == cart(0, 1) + pset = PointSet(cart.([(1, 0), (0, 1)])) + @test nelements(pset) == 2 + @test centroid(pset, 1) == cart(1, 0) + @test centroid(pset, 2) == cart(0, 1) - pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) - @test measure(pset) == zero(T) * u"m" + pset = PointSet(cart.([(0, 0), (1, 0), (0, 1)])) + @test measure(pset) == zero(T) * u"m" - # constructor with iterator - points = cart.([(1, 0), (0, 1)]) - pset1 = PointSet(points) - pset2 = PointSet(p for p in points) - @test pset1 == pset2 + # constructor with iterator + points = cart.([(1, 0), (0, 1)]) + pset1 = PointSet(points) + pset2 = PointSet(p for p in points) + @test pset1 == pset2 - # CRS propagation - pset = PointSet(merc.([(0, 0), (1, 0), (0, 1)])) - @test crs(centroid(pset)) === crs(pset) + # CRS propagation + pset = PointSet(merc.([(0, 0), (1, 0), (0, 1)])) + @test crs(centroid(pset)) === crs(pset) - pset = PointSet(cart.([(1, 0), (0, 1)])) - @test sprint(show, pset) == "2 PointSet" - if T == Float32 - @test sprint(show, MIME"text/plain"(), pset) == """ - 2 PointSet - ├─ Point(x: 1.0f0 m, y: 0.0f0 m) - └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" - elseif T == Float64 - @test sprint(show, MIME"text/plain"(), pset) == """ - 2 PointSet - ├─ Point(x: 1.0 m, y: 0.0 m) - └─ Point(x: 0.0 m, y: 1.0 m)""" - end + pset = PointSet(cart.([(1, 0), (0, 1)])) + @test sprint(show, pset) == "2 PointSet" + if T == Float32 + @test sprint(show, MIME"text/plain"(), pset) == """ + 2 PointSet + ├─ Point(x: 1.0f0 m, y: 0.0f0 m) + └─ Point(x: 0.0f0 m, y: 1.0f0 m)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), pset) == """ + 2 PointSet + ├─ Point(x: 1.0 m, y: 0.0 m) + └─ Point(x: 0.0 m, y: 1.0 m)""" end end diff --git a/test/sideof.jl b/test/sideof.jl index 7e5933ee4..5ea40bc59 100644 --- a/test/sideof.jl +++ b/test/sideof.jl @@ -1,4 +1,4 @@ -@testset "sideof" begin +@testitem "sideof" setup = [Setup] begin p1, p2, p3 = cart(0, 0), cart(1, 1), cart(0.25, 0.5) l = Line(cart(0.5, 0.0), cart(0.0, 1.0)) @test sideof(p1, l) == LEFT diff --git a/test/simplification.jl b/test/simplification.jl index 3a71acd2c..afba919ca 100644 --- a/test/simplification.jl +++ b/test/simplification.jl @@ -1,47 +1,45 @@ -@testset "Simplification" begin - @testset "Selinger" begin - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) - s1 = simplify(c, SelingerSimplification(T(0.1))) - s2 = simplify(c, SelingerSimplification(T(0.5))) - @test s1 == Ring(cart.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) - @test s2 == Ring(cart.([(1, 0), (2, 2), (0, 2), (0, 0)])) - end +@testitem "Selinger" setup = [Setup] begin + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) + s1 = simplify(c, SelingerSimplification(T(0.1))) + s2 = simplify(c, SelingerSimplification(T(0.5))) + @test s1 == Ring(cart.([(1, 0), (1, 1), (2, 1), (2, 2), (0, 2), (0, 0)])) + @test s2 == Ring(cart.([(1, 0), (2, 2), (0, 2), (0, 0)])) +end - @testset "DouglasPeucker" begin - c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) - s2 = simplify(c, DouglasPeuckerSimplification(T(0.5))) - @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) +@testitem "DouglasPeucker" setup = [Setup] begin + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) + s2 = simplify(c, DouglasPeuckerSimplification(T(0.5))) + @test s1 == Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + @test s2 == Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)])) - p = PolyArea(Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) - s1 = simplify(p, DouglasPeuckerSimplification(T(0.5))) - @test s1 == PolyArea(Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)]))) - m = Multi([p, p]) - s2 = simplify(m, DouglasPeuckerSimplification(T(0.5))) - @test s2 == Multi([s1, s1]) - d = GeometrySet([p, p]) - s3 = simplify(d, DouglasPeuckerSimplification(T(0.5))) - @test s3 == GeometrySet([s1, s1]) - end + p = PolyArea(Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)]))) + s1 = simplify(p, DouglasPeuckerSimplification(T(0.5))) + @test s1 == PolyArea(Ring(cart.([(0, 0), (1.5, 0.5), (0, 1)]))) + m = Multi([p, p]) + s2 = simplify(m, DouglasPeuckerSimplification(T(0.5))) + @test s2 == Multi([s1, s1]) + d = GeometrySet([p, p]) + s3 = simplify(d, DouglasPeuckerSimplification(T(0.5))) + @test s3 == GeometrySet([s1, s1]) +end - @testset "MinMax" begin - # Selinger - c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) - s1 = simplify(c, SelingerSimplification(T(0.1))) - s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=6)) - @test nvertices(s2) ≤ nvertices(s1) - s1 = simplify(c, SelingerSimplification(T(0.5))) - s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=4)) - @test nvertices(s2) ≤ nvertices(s1) +@testitem "MinMax" setup = [Setup] begin + # Selinger + c = Ring(cart.([(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)])) + s1 = simplify(c, SelingerSimplification(T(0.1))) + s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=6)) + @test nvertices(s2) ≤ nvertices(s1) + s1 = simplify(c, SelingerSimplification(T(0.5))) + s2 = simplify(c, MinMaxSimplification(SelingerSimplification, max=4)) + @test nvertices(s2) ≤ nvertices(s1) - # Douglas-Peucker - c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) - s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) - s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=6)) - @test s1 ≗ s2 - s1 = simplify(c, DouglasPeuckerSimplification(T(0.5))) - s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=4)) - @test s1 ≗ s2 - end + # Douglas-Peucker + c = Ring(cart.([(0, 0), (1, 0), (1.5, 0.5), (1, 1), (0, 1)])) + s1 = simplify(c, DouglasPeuckerSimplification(T(0.1))) + s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=6)) + @test s1 ≗ s2 + s1 = simplify(c, DouglasPeuckerSimplification(T(0.5))) + s2 = simplify(c, MinMaxSimplification(DouglasPeuckerSimplification, max=4)) + @test s1 ≗ s2 end diff --git a/test/sorting.jl b/test/sorting.jl index 33671c949..ad5aad24a 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -1,18 +1,16 @@ -@testset "Sorting" begin - @testset "DirectionSort" begin - g = cartgrid(3, 3) - s = sort(g, DirectionSort((T(1), T(1)))) - @test centroid.(s) == - cart.([ - (0.5, 0.5), - (1.5, 0.5), - (0.5, 1.5), - (2.5, 0.5), - (1.5, 1.5), - (0.5, 2.5), - (2.5, 1.5), - (1.5, 2.5), - (2.5, 2.5) - ]) - end +@testitem "DirectionSort" setup = [Setup] begin + g = cartgrid(3, 3) + s = sort(g, DirectionSort((T(1), T(1)))) + @test centroid.(s) == + cart.([ + (0.5, 0.5), + (1.5, 0.5), + (0.5, 1.5), + (2.5, 0.5), + (1.5, 1.5), + (0.5, 2.5), + (2.5, 1.5), + (1.5, 2.5), + (2.5, 2.5) + ]) end diff --git a/test/subdomains.jl b/test/subdomains.jl index c22ac5a57..9459f6e72 100644 --- a/test/subdomains.jl +++ b/test/subdomains.jl @@ -1,4 +1,4 @@ -@testset "SubDomains" begin +@testitem "SubDomains" setup = [Setup] begin pset = PointSet(randpoint3(100)) inds = rand(1:100, 3) v = view(pset, inds) diff --git a/test/supportfun.jl b/test/supportfun.jl index ffb075426..7d93c5261 100644 --- a/test/supportfun.jl +++ b/test/supportfun.jl @@ -1,4 +1,4 @@ -@testset "Support function" begin +@testitem "Support function" setup = [Setup] begin t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test supportfun(t, vector(1, 0)) == cart(1, 0) @test supportfun(t, vector(0, 1)) == cart(0, 1) diff --git a/test/tesselation.jl b/test/tesselation.jl index 64c8f3c95..93abc8638 100644 --- a/test/tesselation.jl +++ b/test/tesselation.jl @@ -1,49 +1,47 @@ -@testset "Tesselation" begin - @testset "Delaunay" begin - pts = randpoint2(10) - pset = PointSet(pts) - mesh1 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) - mesh2 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test mesh1 == mesh2 +@testitem "Delaunay" setup = [Setup] begin + pts = randpoint2(10) + pset = PointSet(pts) + mesh1 = tesselate(pts, DelaunayTesselation(StableRNG(2024))) + mesh2 = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test mesh1 == mesh2 - # CRS propagation - tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] - pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) - mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test crs(mesh) === crs(pset) + # CRS propagation + tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] + pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) + mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test crs(mesh) === crs(pset) - coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] - pset = PointSet(Point.(coords)) - mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) - @test crs(mesh) === crs(pset) + coords = [LatLon(rand(-90:T(0.1):90), rand(-180:T(0.1):180)) for _ in 1:10] + pset = PointSet(Point.(coords)) + mesh = tesselate(pset, DelaunayTesselation(StableRNG(2024))) + @test crs(mesh) === crs(pset) - # error: the number of coordinates of the points must be 2 - pts = randpoint3(10) - pset = PointSet(pts) - @test_throws AssertionError tesselate(pset, DelaunayTesselation(StableRNG(2024))) - end + # error: the number of coordinates of the points must be 2 + pts = randpoint3(10) + pset = PointSet(pts) + @test_throws AssertionError tesselate(pset, DelaunayTesselation(StableRNG(2024))) +end - @testset "Voronoi" begin - pts = randpoint2(10) - pset = PointSet(pts) - mesh1 = tesselate(pts, VoronoiTesselation(StableRNG(2024))) - mesh2 = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test mesh1 == mesh2 +@testitem "Voronoi" setup = [Setup] begin + pts = randpoint2(10) + pset = PointSet(pts) + mesh1 = tesselate(pts, VoronoiTesselation(StableRNG(2024))) + mesh2 = tesselate(pset, VoronoiTesselation(StableRNG(2024))) + @test mesh1 == mesh2 - # CRS propagation - tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] - pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) - mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) - @test crs(mesh) === crs(pset) + # CRS propagation + tuples = [(rand(T) * u"km", rand(T) * u"km") for _ in 1:10] + pset = PointSet(Point.(Cartesian{WGS84Latest}.(tuples))) + mesh = tesselate(pset, VoronoiTesselation(StableRNG(2024))) + @test crs(mesh) === crs(pset) - # error: the number of coordinates of the points must be 2 - pts = randpoint3(10) - pset = PointSet(pts) - @test_throws AssertionError tesselate(pset, VoronoiTesselation(StableRNG(2024))) + # error: the number of coordinates of the points must be 2 + pts = randpoint3(10) + pset = PointSet(pts) + @test_throws AssertionError tesselate(pset, VoronoiTesselation(StableRNG(2024))) - # Test polygon order is the same as input points order - pts = randpoint2(10) - mesh = tesselate(pts, VoronoiTesselation(StableRNG(2024))) - @test all(p ∈ poly for (p, poly) in zip(pts, mesh)) - end + # Test polygon order is the same as input points order + pts = randpoint2(10) + mesh = tesselate(pts, VoronoiTesselation(StableRNG(2024))) + @test all(p ∈ poly for (p, poly) in zip(pts, mesh)) end diff --git a/test/testutils.jl b/test/testutils.jl index a7027b7b4..d1f2f3530 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -1,3 +1,29 @@ +# ------------- +# HELPER TYPES +# ------------- + +# meter type +ℳ = Meshes.Met{T} + +# dummy type implementing the Domain trait +struct DummyDomain{M<:Meshes.Manifold,C<:CRS} <: Domain{M,C} + origin::Point{M,C} +end + +function Meshes.element(domain::DummyDomain, ind::Int) + ℒ = Meshes.lentype(domain) + T = Unitful.numtype(ℒ) + c = domain.origin + Vec(ntuple(i -> T(ind) * unit(ℒ), embeddim(domain))) + r = oneunit(ℒ) + Ball(c, r) +end + +Meshes.nelements(d::DummyDomain) = 3 + +# ------------- +# IO FUNCTIONS +# ------------- + # helper function to read *.line files containing polygons # generated with RPG (https://github.com/cgalab/genpoly-rpg) function readpoly(T, fname) @@ -41,6 +67,10 @@ function readply(T, fname) SimpleMesh(points, connec) end +# -------------- +# CRS FUNCTIONS +# -------------- + cart(T::Type, coords...) = cart(T, coords) cart(T::Type, coords::Tuple) = Point(T.(coords)) @@ -53,6 +83,7 @@ latlon(T::Type, coords::Tuple) = Point(LatLon(T.(coords)...)) vector(T::Type, coords...) = vector(T, coords) vector(T::Type, coords::Tuple) = Vec(T.(coords)) +cartgrid(args...) = cartgrid(T, args...) cartgrid(T::Type, dims...) = cartgrid(T, dims) function cartgrid(T::Type, dims::Dims{Dim}) where {Dim} origin = ntuple(i -> T(0.0), Dim) @@ -63,6 +94,19 @@ end randcart(T, Dim, n) = [Point(ntuple(i -> rand(T), Dim)) for _ in 1:n] +# methods with fixed T +cart(xs...) = cart(T, xs...) +merc(xs...) = merc(T, xs...) +latlon(xs...) = latlon(T, xs...) +vector(xs...) = vector(T, xs...) +randpoint1(n) = randcart(T, 1, n) +randpoint2(n) = randcart(T, 2, n) +randpoint3(n) = randcart(T, 3, n) + +# ---------------- +# OTHER FUNCTIONS +# ---------------- + numconvert(T, x::Quantity{S,D,U}) where {S,D,U} = convert(Quantity{T,D,U}, x) withprecision(_, x) = x @@ -80,6 +124,19 @@ withprecision(T, geoms::CircularVector{<:Geometry}) = CircularVector([withprecis :($ctor($(exprs...))) end +# helper function for type stability tests +function someornone(g1, g2) + intersection(g1, g2) do I + if type(I) == NotIntersecting + "None" + else + "Some" + end + end +end + +setify(lists) = Set(Set.(lists)) + function equaltest(g) @test g == withprecision(Float64, g) @test g == withprecision(Float32, g) diff --git a/test/tolerances.jl b/test/tolerances.jl index 34e67ea99..44de98111 100644 --- a/test/tolerances.jl +++ b/test/tolerances.jl @@ -1,4 +1,4 @@ -@testset "tolerances" begin +@testitem "Tolerances" setup = [Setup] begin ℒ = ℳ 𝒜 = typeof(zero(ℳ)^2) 𝒱 = typeof(zero(ℳ)^3) diff --git a/test/topologies.jl b/test/topologies.jl index ab8efdb45..fdcd51366 100644 --- a/test/topologies.jl +++ b/test/topologies.jl @@ -1,543 +1,541 @@ -@testset "Topology" begin - @testset "GridTopology" begin - t = GridTopology(3) - @test paramdim(t) == 1 - @test size(t) == (3,) - @test elementtype(t) == Segment - @test facettype(t) == Point - @test elem2cart(t, 1) == (1,) - @test elem2cart(t, 2) == (2,) - @test elem2cart(t, 3) == (3,) - @test cart2corner(t, 1) == 1 - @test cart2corner(t, 2) == 2 - @test cart2corner(t, 3) == 3 - @test elem2corner(t, 1) == 1 - @test elem2corner(t, 2) == 2 - @test elem2corner(t, 3) == 3 - @test corner2elem(t, 1) == 1 - @test corner2elem(t, 2) == 2 - @test corner2elem(t, 3) == 3 - @test nelements(t) == 3 - @test nfacets(t) == 4 - @test nvertices(t) == 4 - @test nfaces(t, 1) == 3 - @test nfaces(t, 0) == 4 - @test element(t, 1) == connect((1, 2)) - @test element(t, 2) == connect((2, 3)) - @test element(t, 3) == connect((3, 4)) - @test faces(t, 1) == elements(t) - @test faces(t, 0) == vertices(t) - @test vertices(t) == 1:4 - @test vertex(t, 1) == 1 - @test vertex(t, 4) == 4 +@testitem "GridTopology" setup = [Setup] begin + t = GridTopology(3) + @test paramdim(t) == 1 + @test size(t) == (3,) + @test elementtype(t) == Segment + @test facettype(t) == Point + @test elem2cart(t, 1) == (1,) + @test elem2cart(t, 2) == (2,) + @test elem2cart(t, 3) == (3,) + @test cart2corner(t, 1) == 1 + @test cart2corner(t, 2) == 2 + @test cart2corner(t, 3) == 3 + @test elem2corner(t, 1) == 1 + @test elem2corner(t, 2) == 2 + @test elem2corner(t, 3) == 3 + @test corner2elem(t, 1) == 1 + @test corner2elem(t, 2) == 2 + @test corner2elem(t, 3) == 3 + @test nelements(t) == 3 + @test nfacets(t) == 4 + @test nvertices(t) == 4 + @test nfaces(t, 1) == 3 + @test nfaces(t, 0) == 4 + @test element(t, 1) == connect((1, 2)) + @test element(t, 2) == connect((2, 3)) + @test element(t, 3) == connect((3, 4)) + @test faces(t, 1) == elements(t) + @test faces(t, 0) == vertices(t) + @test vertices(t) == 1:4 + @test vertex(t, 1) == 1 + @test vertex(t, 4) == 4 - t = GridTopology(3, 4) - @test paramdim(t) == 2 - @test size(t) == (3, 4) - @test elementtype(t) == Quadrangle - @test facettype(t) == Segment - @test elem2cart(t, 1) == (1, 1) - @test elem2cart(t, 2) == (2, 1) - @test elem2cart(t, 3) == (3, 1) - @test elem2cart(t, 4) == (1, 2) - @test elem2cart(t, 5) == (2, 2) - @test elem2cart(t, 6) == (3, 2) - @test elem2cart(t, 7) == (1, 3) - @test elem2cart(t, 8) == (2, 3) - @test elem2cart(t, 9) == (3, 3) - @test elem2cart(t, 10) == (1, 4) - @test elem2cart(t, 11) == (2, 4) - @test elem2cart(t, 12) == (3, 4) - @test cart2corner(t, 1, 1) == 1 - @test cart2corner(t, 2, 1) == 2 - @test cart2corner(t, 3, 1) == 3 - @test cart2corner(t, 1, 2) == 5 - @test cart2corner(t, 2, 2) == 6 - @test cart2corner(t, 3, 2) == 7 - @test cart2corner(t, 1, 3) == 9 - @test cart2corner(t, 2, 3) == 10 - @test cart2corner(t, 3, 3) == 11 - @test cart2corner(t, 1, 4) == 13 - @test cart2corner(t, 2, 4) == 14 - @test cart2corner(t, 3, 4) == 15 - @test elem2corner(t, 1) == 1 - @test elem2corner(t, 2) == 2 - @test elem2corner(t, 3) == 3 - @test elem2corner(t, 4) == 5 - @test elem2corner(t, 5) == 6 - @test elem2corner(t, 6) == 7 - @test elem2corner(t, 7) == 9 - @test elem2corner(t, 8) == 10 - @test elem2corner(t, 9) == 11 - @test elem2corner(t, 10) == 13 - @test elem2corner(t, 11) == 14 - @test elem2corner(t, 12) == 15 - @test corner2elem(t, 1) == 1 - @test corner2elem(t, 2) == 2 - @test corner2elem(t, 3) == 3 - @test corner2elem(t, 5) == 4 - @test corner2elem(t, 6) == 5 - @test corner2elem(t, 7) == 6 - @test corner2elem(t, 9) == 7 - @test corner2elem(t, 10) == 8 - @test corner2elem(t, 11) == 9 - @test corner2elem(t, 13) == 10 - @test corner2elem(t, 14) == 11 - @test corner2elem(t, 15) == 12 - @test nelements(t) == 12 - @test nfacets(t) == 31 - @test nvertices(t) == 20 - @test nfaces(t, 2) == 12 - @test nfaces(t, 1) == 31 - @test nfaces(t, 0) == 20 - @test element(t, 1) == connect((1, 2, 6, 5)) - @test element(t, 5) == connect((6, 7, 11, 10)) - @test faces(t, 2) == elements(t) - @test faces(t, 0) == vertices(t) - @test vertices(t) == 1:20 - @test vertex(t, 1) == 1 - @test vertex(t, 20) == 20 - @test facet.(Ref(t), 1:31) == - connect.([ - (1, 5), - (2, 6), - (3, 7), - (4, 8), - (5, 9), - (6, 10), - (7, 11), - (8, 12), - (9, 13), - (10, 14), - (11, 15), - (12, 16), - (13, 17), - (14, 18), - (15, 19), - (16, 20), - (1, 2), - (5, 6), - (9, 10), - (13, 14), - (17, 18), - (2, 3), - (6, 7), - (10, 11), - (14, 15), - (18, 19), - (3, 4), - (7, 8), - (11, 12), - (15, 16), - (19, 20) - ]) + t = GridTopology(3, 4) + @test paramdim(t) == 2 + @test size(t) == (3, 4) + @test elementtype(t) == Quadrangle + @test facettype(t) == Segment + @test elem2cart(t, 1) == (1, 1) + @test elem2cart(t, 2) == (2, 1) + @test elem2cart(t, 3) == (3, 1) + @test elem2cart(t, 4) == (1, 2) + @test elem2cart(t, 5) == (2, 2) + @test elem2cart(t, 6) == (3, 2) + @test elem2cart(t, 7) == (1, 3) + @test elem2cart(t, 8) == (2, 3) + @test elem2cart(t, 9) == (3, 3) + @test elem2cart(t, 10) == (1, 4) + @test elem2cart(t, 11) == (2, 4) + @test elem2cart(t, 12) == (3, 4) + @test cart2corner(t, 1, 1) == 1 + @test cart2corner(t, 2, 1) == 2 + @test cart2corner(t, 3, 1) == 3 + @test cart2corner(t, 1, 2) == 5 + @test cart2corner(t, 2, 2) == 6 + @test cart2corner(t, 3, 2) == 7 + @test cart2corner(t, 1, 3) == 9 + @test cart2corner(t, 2, 3) == 10 + @test cart2corner(t, 3, 3) == 11 + @test cart2corner(t, 1, 4) == 13 + @test cart2corner(t, 2, 4) == 14 + @test cart2corner(t, 3, 4) == 15 + @test elem2corner(t, 1) == 1 + @test elem2corner(t, 2) == 2 + @test elem2corner(t, 3) == 3 + @test elem2corner(t, 4) == 5 + @test elem2corner(t, 5) == 6 + @test elem2corner(t, 6) == 7 + @test elem2corner(t, 7) == 9 + @test elem2corner(t, 8) == 10 + @test elem2corner(t, 9) == 11 + @test elem2corner(t, 10) == 13 + @test elem2corner(t, 11) == 14 + @test elem2corner(t, 12) == 15 + @test corner2elem(t, 1) == 1 + @test corner2elem(t, 2) == 2 + @test corner2elem(t, 3) == 3 + @test corner2elem(t, 5) == 4 + @test corner2elem(t, 6) == 5 + @test corner2elem(t, 7) == 6 + @test corner2elem(t, 9) == 7 + @test corner2elem(t, 10) == 8 + @test corner2elem(t, 11) == 9 + @test corner2elem(t, 13) == 10 + @test corner2elem(t, 14) == 11 + @test corner2elem(t, 15) == 12 + @test nelements(t) == 12 + @test nfacets(t) == 31 + @test nvertices(t) == 20 + @test nfaces(t, 2) == 12 + @test nfaces(t, 1) == 31 + @test nfaces(t, 0) == 20 + @test element(t, 1) == connect((1, 2, 6, 5)) + @test element(t, 5) == connect((6, 7, 11, 10)) + @test faces(t, 2) == elements(t) + @test faces(t, 0) == vertices(t) + @test vertices(t) == 1:20 + @test vertex(t, 1) == 1 + @test vertex(t, 20) == 20 + @test facet.(Ref(t), 1:31) == + connect.([ + (1, 5), + (2, 6), + (3, 7), + (4, 8), + (5, 9), + (6, 10), + (7, 11), + (8, 12), + (9, 13), + (10, 14), + (11, 15), + (12, 16), + (13, 17), + (14, 18), + (15, 19), + (16, 20), + (1, 2), + (5, 6), + (9, 10), + (13, 14), + (17, 18), + (2, 3), + (6, 7), + (10, 11), + (14, 15), + (18, 19), + (3, 4), + (7, 8), + (11, 12), + (15, 16), + (19, 20) + ]) - t = GridTopology(3, 4, 2) - @test paramdim(t) == 3 - @test size(t) == (3, 4, 2) - @test elementtype(t) == Hexahedron - @test facettype(t) == Quadrangle - @test elem2cart(t, 1) == (1, 1, 1) - @test elem2cart(t, 2) == (2, 1, 1) - @test elem2cart(t, 3) == (3, 1, 1) - @test elem2cart(t, 4) == (1, 2, 1) - @test elem2cart(t, 5) == (2, 2, 1) - @test elem2cart(t, 6) == (3, 2, 1) - @test elem2cart(t, 7) == (1, 3, 1) - @test elem2cart(t, 8) == (2, 3, 1) - @test elem2cart(t, 9) == (3, 3, 1) - @test elem2cart(t, 10) == (1, 4, 1) - @test elem2cart(t, 11) == (2, 4, 1) - @test elem2cart(t, 12) == (3, 4, 1) - @test elem2cart(t, 13) == (1, 1, 2) - @test elem2cart(t, 14) == (2, 1, 2) - @test elem2cart(t, 15) == (3, 1, 2) - @test elem2cart(t, 16) == (1, 2, 2) - @test elem2cart(t, 17) == (2, 2, 2) - @test elem2cart(t, 18) == (3, 2, 2) - @test elem2cart(t, 19) == (1, 3, 2) - @test elem2cart(t, 20) == (2, 3, 2) - @test elem2cart(t, 21) == (3, 3, 2) - @test elem2cart(t, 22) == (1, 4, 2) - @test elem2cart(t, 23) == (2, 4, 2) - @test elem2cart(t, 24) == (3, 4, 2) - @test cart2corner(t, 1, 1, 1) == 1 - @test cart2corner(t, 2, 1, 1) == 2 - @test cart2corner(t, 3, 1, 1) == 3 - @test cart2corner(t, 1, 2, 1) == 5 - @test cart2corner(t, 2, 2, 1) == 6 - @test cart2corner(t, 3, 2, 1) == 7 - @test cart2corner(t, 1, 3, 1) == 9 - @test cart2corner(t, 2, 3, 1) == 10 - @test cart2corner(t, 3, 3, 1) == 11 - @test cart2corner(t, 1, 4, 1) == 13 - @test cart2corner(t, 2, 4, 1) == 14 - @test cart2corner(t, 3, 4, 1) == 15 - @test cart2corner(t, 1, 1, 2) == 21 - @test cart2corner(t, 2, 1, 2) == 22 - @test cart2corner(t, 3, 1, 2) == 23 - @test cart2corner(t, 1, 2, 2) == 25 - @test cart2corner(t, 2, 2, 2) == 26 - @test cart2corner(t, 3, 2, 2) == 27 - @test cart2corner(t, 1, 3, 2) == 29 - @test cart2corner(t, 2, 3, 2) == 30 - @test cart2corner(t, 3, 3, 2) == 31 - @test cart2corner(t, 1, 4, 2) == 33 - @test cart2corner(t, 2, 4, 2) == 34 - @test cart2corner(t, 3, 4, 2) == 35 - @test elem2corner(t, 1) == 1 - @test elem2corner(t, 2) == 2 - @test elem2corner(t, 3) == 3 - @test elem2corner(t, 4) == 5 - @test elem2corner(t, 5) == 6 - @test elem2corner(t, 6) == 7 - @test elem2corner(t, 7) == 9 - @test elem2corner(t, 8) == 10 - @test elem2corner(t, 9) == 11 - @test elem2corner(t, 10) == 13 - @test elem2corner(t, 11) == 14 - @test elem2corner(t, 12) == 15 - @test elem2corner(t, 13) == 21 - @test elem2corner(t, 14) == 22 - @test elem2corner(t, 15) == 23 - @test elem2corner(t, 16) == 25 - @test elem2corner(t, 17) == 26 - @test elem2corner(t, 18) == 27 - @test elem2corner(t, 19) == 29 - @test elem2corner(t, 20) == 30 - @test elem2corner(t, 21) == 31 - @test elem2corner(t, 22) == 33 - @test elem2corner(t, 23) == 34 - @test elem2corner(t, 24) == 35 - @test corner2elem(t, 1) == 1 - @test corner2elem(t, 2) == 2 - @test corner2elem(t, 3) == 3 - @test corner2elem(t, 5) == 4 - @test corner2elem(t, 6) == 5 - @test corner2elem(t, 7) == 6 - @test corner2elem(t, 9) == 7 - @test corner2elem(t, 10) == 8 - @test corner2elem(t, 11) == 9 - @test corner2elem(t, 13) == 10 - @test corner2elem(t, 14) == 11 - @test corner2elem(t, 15) == 12 - @test corner2elem(t, 21) == 13 - @test corner2elem(t, 22) == 14 - @test corner2elem(t, 23) == 15 - @test corner2elem(t, 25) == 16 - @test corner2elem(t, 26) == 17 - @test corner2elem(t, 27) == 18 - @test corner2elem(t, 29) == 19 - @test corner2elem(t, 30) == 20 - @test corner2elem(t, 31) == 21 - @test corner2elem(t, 33) == 22 - @test corner2elem(t, 34) == 23 - @test corner2elem(t, 35) == 24 - @test nelements(t) == 24 - @test nfacets(t) == 3 * 24 + 3 * 4 + 4 * 2 + 3 * 2 - @test nvertices(t) == 60 - @test nfaces(t, 3) == 24 - @test nfaces(t, 2) == 3 * 24 + 3 * 4 + 4 * 2 + 3 * 2 - @test nfaces(t, 0) == 60 - @test element(t, 1) == connect((1, 2, 6, 5, 21, 22, 26, 25), Hexahedron) - @test element(t, 5) == connect((6, 7, 11, 10, 26, 27, 31, 30), Hexahedron) - @test faces(t, 3) == elements(t) - @test faces(t, 0) == vertices(t) - @test vertices(t) == 1:60 - @test vertex(t, 1) == 1 - @test vertex(t, 60) == 60 + t = GridTopology(3, 4, 2) + @test paramdim(t) == 3 + @test size(t) == (3, 4, 2) + @test elementtype(t) == Hexahedron + @test facettype(t) == Quadrangle + @test elem2cart(t, 1) == (1, 1, 1) + @test elem2cart(t, 2) == (2, 1, 1) + @test elem2cart(t, 3) == (3, 1, 1) + @test elem2cart(t, 4) == (1, 2, 1) + @test elem2cart(t, 5) == (2, 2, 1) + @test elem2cart(t, 6) == (3, 2, 1) + @test elem2cart(t, 7) == (1, 3, 1) + @test elem2cart(t, 8) == (2, 3, 1) + @test elem2cart(t, 9) == (3, 3, 1) + @test elem2cart(t, 10) == (1, 4, 1) + @test elem2cart(t, 11) == (2, 4, 1) + @test elem2cart(t, 12) == (3, 4, 1) + @test elem2cart(t, 13) == (1, 1, 2) + @test elem2cart(t, 14) == (2, 1, 2) + @test elem2cart(t, 15) == (3, 1, 2) + @test elem2cart(t, 16) == (1, 2, 2) + @test elem2cart(t, 17) == (2, 2, 2) + @test elem2cart(t, 18) == (3, 2, 2) + @test elem2cart(t, 19) == (1, 3, 2) + @test elem2cart(t, 20) == (2, 3, 2) + @test elem2cart(t, 21) == (3, 3, 2) + @test elem2cart(t, 22) == (1, 4, 2) + @test elem2cart(t, 23) == (2, 4, 2) + @test elem2cart(t, 24) == (3, 4, 2) + @test cart2corner(t, 1, 1, 1) == 1 + @test cart2corner(t, 2, 1, 1) == 2 + @test cart2corner(t, 3, 1, 1) == 3 + @test cart2corner(t, 1, 2, 1) == 5 + @test cart2corner(t, 2, 2, 1) == 6 + @test cart2corner(t, 3, 2, 1) == 7 + @test cart2corner(t, 1, 3, 1) == 9 + @test cart2corner(t, 2, 3, 1) == 10 + @test cart2corner(t, 3, 3, 1) == 11 + @test cart2corner(t, 1, 4, 1) == 13 + @test cart2corner(t, 2, 4, 1) == 14 + @test cart2corner(t, 3, 4, 1) == 15 + @test cart2corner(t, 1, 1, 2) == 21 + @test cart2corner(t, 2, 1, 2) == 22 + @test cart2corner(t, 3, 1, 2) == 23 + @test cart2corner(t, 1, 2, 2) == 25 + @test cart2corner(t, 2, 2, 2) == 26 + @test cart2corner(t, 3, 2, 2) == 27 + @test cart2corner(t, 1, 3, 2) == 29 + @test cart2corner(t, 2, 3, 2) == 30 + @test cart2corner(t, 3, 3, 2) == 31 + @test cart2corner(t, 1, 4, 2) == 33 + @test cart2corner(t, 2, 4, 2) == 34 + @test cart2corner(t, 3, 4, 2) == 35 + @test elem2corner(t, 1) == 1 + @test elem2corner(t, 2) == 2 + @test elem2corner(t, 3) == 3 + @test elem2corner(t, 4) == 5 + @test elem2corner(t, 5) == 6 + @test elem2corner(t, 6) == 7 + @test elem2corner(t, 7) == 9 + @test elem2corner(t, 8) == 10 + @test elem2corner(t, 9) == 11 + @test elem2corner(t, 10) == 13 + @test elem2corner(t, 11) == 14 + @test elem2corner(t, 12) == 15 + @test elem2corner(t, 13) == 21 + @test elem2corner(t, 14) == 22 + @test elem2corner(t, 15) == 23 + @test elem2corner(t, 16) == 25 + @test elem2corner(t, 17) == 26 + @test elem2corner(t, 18) == 27 + @test elem2corner(t, 19) == 29 + @test elem2corner(t, 20) == 30 + @test elem2corner(t, 21) == 31 + @test elem2corner(t, 22) == 33 + @test elem2corner(t, 23) == 34 + @test elem2corner(t, 24) == 35 + @test corner2elem(t, 1) == 1 + @test corner2elem(t, 2) == 2 + @test corner2elem(t, 3) == 3 + @test corner2elem(t, 5) == 4 + @test corner2elem(t, 6) == 5 + @test corner2elem(t, 7) == 6 + @test corner2elem(t, 9) == 7 + @test corner2elem(t, 10) == 8 + @test corner2elem(t, 11) == 9 + @test corner2elem(t, 13) == 10 + @test corner2elem(t, 14) == 11 + @test corner2elem(t, 15) == 12 + @test corner2elem(t, 21) == 13 + @test corner2elem(t, 22) == 14 + @test corner2elem(t, 23) == 15 + @test corner2elem(t, 25) == 16 + @test corner2elem(t, 26) == 17 + @test corner2elem(t, 27) == 18 + @test corner2elem(t, 29) == 19 + @test corner2elem(t, 30) == 20 + @test corner2elem(t, 31) == 21 + @test corner2elem(t, 33) == 22 + @test corner2elem(t, 34) == 23 + @test corner2elem(t, 35) == 24 + @test nelements(t) == 24 + @test nfacets(t) == 3 * 24 + 3 * 4 + 4 * 2 + 3 * 2 + @test nvertices(t) == 60 + @test nfaces(t, 3) == 24 + @test nfaces(t, 2) == 3 * 24 + 3 * 4 + 4 * 2 + 3 * 2 + @test nfaces(t, 0) == 60 + @test element(t, 1) == connect((1, 2, 6, 5, 21, 22, 26, 25), Hexahedron) + @test element(t, 5) == connect((6, 7, 11, 10, 26, 27, 31, 30), Hexahedron) + @test faces(t, 3) == elements(t) + @test faces(t, 0) == vertices(t) + @test vertices(t) == 1:60 + @test vertex(t, 1) == 1 + @test vertex(t, 60) == 60 - t = GridTopology((3,), (true,)) - @test isperiodic(t) == (true,) - @test nvertices(t) == 3 - @test nelements(t) == 3 - @test nfacets(t) == 3 - @test element(t, 1) == connect((1, 2)) - @test element(t, 2) == connect((2, 3)) - @test element(t, 3) == connect((3, 1)) + t = GridTopology((3,), (true,)) + @test isperiodic(t) == (true,) + @test nvertices(t) == 3 + @test nelements(t) == 3 + @test nfacets(t) == 3 + @test element(t, 1) == connect((1, 2)) + @test element(t, 2) == connect((2, 3)) + @test element(t, 3) == connect((3, 1)) - t = GridTopology((2, 3), (true, true)) - @test isperiodic(t) == (true, true) - @test nvertices(t) == 2 * 3 - @test nelements(t) == 6 - @test nfacets(t) == 12 - @test element(t, 1) == connect((1, 2, 4, 3)) - @test element(t, 2) == connect((2, 1, 3, 4)) - @test element(t, 3) == connect((3, 4, 6, 5)) - @test element(t, 4) == connect((4, 3, 5, 6)) - @test element(t, 5) == connect((5, 6, 2, 1)) - @test element(t, 6) == connect((6, 5, 1, 2)) + t = GridTopology((2, 3), (true, true)) + @test isperiodic(t) == (true, true) + @test nvertices(t) == 2 * 3 + @test nelements(t) == 6 + @test nfacets(t) == 12 + @test element(t, 1) == connect((1, 2, 4, 3)) + @test element(t, 2) == connect((2, 1, 3, 4)) + @test element(t, 3) == connect((3, 4, 6, 5)) + @test element(t, 4) == connect((4, 3, 5, 6)) + @test element(t, 5) == connect((5, 6, 2, 1)) + @test element(t, 6) == connect((6, 5, 1, 2)) - t = GridTopology((2, 3), (false, true)) - @test isperiodic(t) == (false, true) - @test nvertices(t) == 3 * 3 - @test nelements(t) == 6 - @test nfacets(t) == 15 - @test element(t, 1) == connect((1, 2, 5, 4)) - @test element(t, 2) == connect((2, 3, 6, 5)) - @test element(t, 3) == connect((4, 5, 8, 7)) - @test element(t, 4) == connect((5, 6, 9, 8)) - @test element(t, 5) == connect((7, 8, 2, 1)) - @test element(t, 6) == connect((8, 9, 3, 2)) + t = GridTopology((2, 3), (false, true)) + @test isperiodic(t) == (false, true) + @test nvertices(t) == 3 * 3 + @test nelements(t) == 6 + @test nfacets(t) == 15 + @test element(t, 1) == connect((1, 2, 5, 4)) + @test element(t, 2) == connect((2, 3, 6, 5)) + @test element(t, 3) == connect((4, 5, 8, 7)) + @test element(t, 4) == connect((5, 6, 9, 8)) + @test element(t, 5) == connect((7, 8, 2, 1)) + @test element(t, 6) == connect((8, 9, 3, 2)) - t = GridTopology((2, 3), (true, false)) - @test isperiodic(t) == (true, false) - @test nvertices(t) == 2 * 4 - @test nelements(t) == 6 - @test nfacets(t) == 14 - @test element(t, 1) == connect((1, 2, 4, 3)) - @test element(t, 2) == connect((2, 1, 3, 4)) - @test element(t, 3) == connect((3, 4, 6, 5)) - @test element(t, 4) == connect((4, 3, 5, 6)) - @test element(t, 5) == connect((5, 6, 8, 7)) - @test element(t, 6) == connect((6, 5, 7, 8)) + t = GridTopology((2, 3), (true, false)) + @test isperiodic(t) == (true, false) + @test nvertices(t) == 2 * 4 + @test nelements(t) == 6 + @test nfacets(t) == 14 + @test element(t, 1) == connect((1, 2, 4, 3)) + @test element(t, 2) == connect((2, 1, 3, 4)) + @test element(t, 3) == connect((3, 4, 6, 5)) + @test element(t, 4) == connect((4, 3, 5, 6)) + @test element(t, 5) == connect((5, 6, 8, 7)) + @test element(t, 6) == connect((6, 5, 7, 8)) - t = GridTopology((2, 3, 4), (true, true, true)) - @test isperiodic(t) == (true, true, true) - @test nvertices(t) == 2 * 3 * 4 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) - @test element(t, 1) == connect((1, 2, 4, 3, 7, 8, 10, 9), Hexahedron) - @test element(t, 2) == connect((2, 1, 3, 4, 8, 7, 9, 10), Hexahedron) - @test element(t, 24) == connect((24, 23, 19, 20, 6, 5, 1, 2), Hexahedron) + t = GridTopology((2, 3, 4), (true, true, true)) + @test isperiodic(t) == (true, true, true) + @test nvertices(t) == 2 * 3 * 4 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + @test element(t, 1) == connect((1, 2, 4, 3, 7, 8, 10, 9), Hexahedron) + @test element(t, 2) == connect((2, 1, 3, 4, 8, 7, 9, 10), Hexahedron) + @test element(t, 24) == connect((24, 23, 19, 20, 6, 5, 1, 2), Hexahedron) - t = GridTopology((2, 3, 4), (false, true, true)) - @test isperiodic(t) == (false, true, true) - @test nvertices(t) == 3 * 3 * 4 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 - @test element(t, 1) == connect((1, 2, 5, 4, 10, 11, 14, 13), Hexahedron) - @test element(t, 2) == connect((2, 3, 6, 5, 11, 12, 15, 14), Hexahedron) - @test element(t, 24) == connect((35, 36, 30, 29, 8, 9, 3, 2), Hexahedron) + t = GridTopology((2, 3, 4), (false, true, true)) + @test isperiodic(t) == (false, true, true) + @test nvertices(t) == 3 * 3 * 4 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 + @test element(t, 1) == connect((1, 2, 5, 4, 10, 11, 14, 13), Hexahedron) + @test element(t, 2) == connect((2, 3, 6, 5, 11, 12, 15, 14), Hexahedron) + @test element(t, 24) == connect((35, 36, 30, 29, 8, 9, 3, 2), Hexahedron) - t = GridTopology((2, 3, 4), (true, false, true)) - @test isperiodic(t) == (true, false, true) - @test nvertices(t) == 2 * 4 * 4 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 4 - @test element(t, 1) == connect((1, 2, 4, 3, 9, 10, 12, 11), Hexahedron) - @test element(t, 2) == connect((2, 1, 3, 4, 10, 9, 11, 12), Hexahedron) - @test element(t, 24) == connect((30, 29, 31, 32, 6, 5, 7, 8), Hexahedron) + t = GridTopology((2, 3, 4), (true, false, true)) + @test isperiodic(t) == (true, false, true) + @test nvertices(t) == 2 * 4 * 4 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 4 + @test element(t, 1) == connect((1, 2, 4, 3, 9, 10, 12, 11), Hexahedron) + @test element(t, 2) == connect((2, 1, 3, 4, 10, 9, 11, 12), Hexahedron) + @test element(t, 24) == connect((30, 29, 31, 32, 6, 5, 7, 8), Hexahedron) - t = GridTopology((2, 3, 4), (true, true, false)) - @test isperiodic(t) == (true, true, false) - @test nvertices(t) == 2 * 3 * 5 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 3 - @test element(t, 1) == connect((1, 2, 4, 3, 7, 8, 10, 9), Hexahedron) - @test element(t, 2) == connect((2, 1, 3, 4, 8, 7, 9, 10), Hexahedron) - @test element(t, 24) == connect((24, 23, 19, 20, 30, 29, 25, 26), Hexahedron) + t = GridTopology((2, 3, 4), (true, true, false)) + @test isperiodic(t) == (true, true, false) + @test nvertices(t) == 2 * 3 * 5 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 3 + @test element(t, 1) == connect((1, 2, 4, 3, 7, 8, 10, 9), Hexahedron) + @test element(t, 2) == connect((2, 1, 3, 4, 8, 7, 9, 10), Hexahedron) + @test element(t, 24) == connect((24, 23, 19, 20, 30, 29, 25, 26), Hexahedron) - t = GridTopology((2, 3, 4), (true, false, false)) - @test isperiodic(t) == (true, false, false) - @test nvertices(t) == 2 * 4 * 5 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 4 + 2 * 3 - @test element(t, 1) == connect((1, 2, 4, 3, 9, 10, 12, 11), Hexahedron) - @test element(t, 2) == connect((2, 1, 3, 4, 10, 9, 11, 12), Hexahedron) - @test element(t, 24) == connect((30, 29, 31, 32, 38, 37, 39, 40), Hexahedron) + t = GridTopology((2, 3, 4), (true, false, false)) + @test isperiodic(t) == (true, false, false) + @test nvertices(t) == 2 * 4 * 5 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 2 * 4 + 2 * 3 + @test element(t, 1) == connect((1, 2, 4, 3, 9, 10, 12, 11), Hexahedron) + @test element(t, 2) == connect((2, 1, 3, 4, 10, 9, 11, 12), Hexahedron) + @test element(t, 24) == connect((30, 29, 31, 32, 38, 37, 39, 40), Hexahedron) - t = GridTopology((2, 3, 4), (false, true, false)) - @test isperiodic(t) == (false, true, false) - @test nvertices(t) == 3 * 3 * 5 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 + 2 * 3 - @test element(t, 1) == connect((1, 2, 5, 4, 10, 11, 14, 13), Hexahedron) - @test element(t, 2) == connect((2, 3, 6, 5, 11, 12, 15, 14), Hexahedron) - @test element(t, 24) == connect((35, 36, 30, 29, 44, 45, 39, 38), Hexahedron) + t = GridTopology((2, 3, 4), (false, true, false)) + @test isperiodic(t) == (false, true, false) + @test nvertices(t) == 3 * 3 * 5 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 + 2 * 3 + @test element(t, 1) == connect((1, 2, 5, 4, 10, 11, 14, 13), Hexahedron) + @test element(t, 2) == connect((2, 3, 6, 5, 11, 12, 15, 14), Hexahedron) + @test element(t, 24) == connect((35, 36, 30, 29, 44, 45, 39, 38), Hexahedron) - t = GridTopology((2, 3, 4), (false, false, true)) - @test isperiodic(t) == (false, false, true) - @test nvertices(t) == 3 * 4 * 4 - @test nelements(t) == 2 * 3 * 4 - @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 + 2 * 4 - @test element(t, 1) == connect((1, 2, 5, 4, 13, 14, 17, 16), Hexahedron) - @test element(t, 2) == connect((2, 3, 6, 5, 14, 15, 18, 17), Hexahedron) - @test element(t, 24) == connect((44, 45, 48, 47, 8, 9, 12, 11), Hexahedron) - end + t = GridTopology((2, 3, 4), (false, false, true)) + @test isperiodic(t) == (false, false, true) + @test nvertices(t) == 3 * 4 * 4 + @test nelements(t) == 2 * 3 * 4 + @test nfacets(t) == 3 * (2 * 3 * 4) + 3 * 4 + 2 * 4 + @test element(t, 1) == connect((1, 2, 5, 4, 13, 14, 17, 16), Hexahedron) + @test element(t, 2) == connect((2, 3, 6, 5, 14, 15, 18, 17), Hexahedron) + @test element(t, 24) == connect((44, 45, 48, 47, 8, 9, 12, 11), Hexahedron) +end - @testset "HalfEdgeTopology" begin - function test_halfedge(elems, topology) - @test nelements(topology) == length(elems) - for e in 1:nelements(topology) - he = half4elem(topology, e) - inds = indices(elems[e]) - @test he.elem == e - @test he.head ∈ inds - end +@testitem "HalfEdgeTopology" setup = [Setup] begin + function test_halfedge(elems, topology) + @test nelements(topology) == length(elems) + for e in 1:nelements(topology) + he = half4elem(topology, e) + inds = indices(elems[e]) + @test he.elem == e + @test he.head ∈ inds end + end - # 2 triangles as a list of half-edges - h1 = HalfEdge(1, 1) - h2 = HalfEdge(2, nothing) - h3 = HalfEdge(2, 1) - h4 = HalfEdge(3, 2) - h5 = HalfEdge(3, 1) - h6 = HalfEdge(1, nothing) - h7 = HalfEdge(2, 2) - h8 = HalfEdge(4, nothing) - h9 = HalfEdge(4, 2) - h10 = HalfEdge(3, nothing) - h1.half = h2 - h2.half = h1 - h3.half = h4 - h4.half = h3 - h5.half = h6 - h6.half = h5 - h7.half = h8 - h8.half = h7 - h9.half = h10 - h10.half = h9 - h1.prev = h5 - h1.next = h3 - h3.prev = h1 - h3.next = h5 - h4.prev = h9 - h4.next = h7 - h5.prev = h3 - h5.next = h1 - h7.prev = h4 - h7.next = h9 - h9.prev = h7 - h9.next = h4 - halves = [(h1, h2), (h3, h4), (h5, h6), (h7, h8), (h9, h10)] - struc = HalfEdgeTopology(halves) - @test half4elem(struc, 1) == h1 - @test half4elem(struc, 2) == h4 - @test half4vert(struc, 1) == h1 - @test half4vert(struc, 2) == h3 - @test half4vert(struc, 3) == h4 - @test half4vert(struc, 4) == h9 - @test edge4pair(struc, (1, 2)) == 1 - @test edge4pair(struc, (2, 1)) == 1 - @test edge4pair(struc, (2, 3)) == 2 - @test edge4pair(struc, (3, 2)) == 2 - @test edge4pair(struc, (3, 1)) == 3 - @test edge4pair(struc, (1, 3)) == 3 - @test edge4pair(struc, (2, 4)) == 4 - @test edge4pair(struc, (4, 2)) == 4 - @test edge4pair(struc, (4, 3)) == 5 - @test edge4pair(struc, (3, 4)) == 5 + # 2 triangles as a list of half-edges + h1 = HalfEdge(1, 1) + h2 = HalfEdge(2, nothing) + h3 = HalfEdge(2, 1) + h4 = HalfEdge(3, 2) + h5 = HalfEdge(3, 1) + h6 = HalfEdge(1, nothing) + h7 = HalfEdge(2, 2) + h8 = HalfEdge(4, nothing) + h9 = HalfEdge(4, 2) + h10 = HalfEdge(3, nothing) + h1.half = h2 + h2.half = h1 + h3.half = h4 + h4.half = h3 + h5.half = h6 + h6.half = h5 + h7.half = h8 + h8.half = h7 + h9.half = h10 + h10.half = h9 + h1.prev = h5 + h1.next = h3 + h3.prev = h1 + h3.next = h5 + h4.prev = h9 + h4.next = h7 + h5.prev = h3 + h5.next = h1 + h7.prev = h4 + h7.next = h9 + h9.prev = h7 + h9.next = h4 + halves = [(h1, h2), (h3, h4), (h5, h6), (h7, h8), (h9, h10)] + struc = HalfEdgeTopology(halves) + @test half4elem(struc, 1) == h1 + @test half4elem(struc, 2) == h4 + @test half4vert(struc, 1) == h1 + @test half4vert(struc, 2) == h3 + @test half4vert(struc, 3) == h4 + @test half4vert(struc, 4) == h9 + @test edge4pair(struc, (1, 2)) == 1 + @test edge4pair(struc, (2, 1)) == 1 + @test edge4pair(struc, (2, 3)) == 2 + @test edge4pair(struc, (3, 2)) == 2 + @test edge4pair(struc, (3, 1)) == 3 + @test edge4pair(struc, (1, 3)) == 3 + @test edge4pair(struc, (2, 4)) == 4 + @test edge4pair(struc, (4, 2)) == 4 + @test edge4pair(struc, (4, 3)) == 5 + @test edge4pair(struc, (3, 4)) == 5 - # 2 triangles - elems = connect.([(1, 2, 3), (4, 3, 2)]) - t = HalfEdgeTopology(elems) - @test paramdim(t) == 2 - @test nelements(t) == 2 - @test nfacets(t) == 5 - @test nvertices(t) == 4 - @test nfaces(t, 2) == 2 - @test nfaces(t, 1) == 5 - @test nfaces(t, 0) == 4 - test_halfedge(elems, t) + # 2 triangles + elems = connect.([(1, 2, 3), (4, 3, 2)]) + t = HalfEdgeTopology(elems) + @test paramdim(t) == 2 + @test nelements(t) == 2 + @test nfacets(t) == 5 + @test nvertices(t) == 4 + @test nfaces(t, 2) == 2 + @test nfaces(t, 1) == 5 + @test nfaces(t, 0) == 4 + test_halfedge(elems, t) - # 2 triangles + 2 quadrangles - elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) - t = HalfEdgeTopology(elems) - @test paramdim(t) == 2 - @test nelements(t) == 4 - @test nfacets(t) == 9 - @test nvertices(t) == 6 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 9 - @test nfaces(t, 0) == 6 - test_halfedge(elems, t) + # 2 triangles + 2 quadrangles + elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) + t = HalfEdgeTopology(elems) + @test paramdim(t) == 2 + @test nelements(t) == 4 + @test nfacets(t) == 9 + @test nvertices(t) == 6 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 9 + @test nfaces(t, 0) == 6 + test_halfedge(elems, t) - # 1 triangle + 3 quadrangles + 1 triangle hole - elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) - t = HalfEdgeTopology(elems) - @test paramdim(t) == 2 - @test nelements(t) == 4 - @test nfacets(t) == 11 - @test nvertices(t) == 7 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 11 - @test nfaces(t, 0) == 7 - @test vertices(t) == 1:7 - @test vertex(t, 1) == 1 - @test vertex(t, 7) == 7 - test_halfedge(elems, t) + # 1 triangle + 3 quadrangles + 1 triangle hole + elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) + t = HalfEdgeTopology(elems) + @test paramdim(t) == 2 + @test nelements(t) == 4 + @test nfacets(t) == 11 + @test nvertices(t) == 7 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 11 + @test nfaces(t, 0) == 7 + @test vertices(t) == 1:7 + @test vertex(t, 1) == 1 + @test vertex(t, 7) == 7 + test_halfedge(elems, t) - # no need to sort elements with consistent orientation - elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) - t = HalfEdgeTopology(elems, sort=false) - @test paramdim(t) == 2 - @test nelements(t) == 4 - @test nfacets(t) == 11 - @test nvertices(t) == 7 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 11 - @test nfaces(t, 0) == 7 - test_halfedge(elems, t) + # no need to sort elements with consistent orientation + elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) + t = HalfEdgeTopology(elems, sort=false) + @test paramdim(t) == 2 + @test nelements(t) == 4 + @test nfacets(t) == 11 + @test nvertices(t) == 7 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 11 + @test nfaces(t, 0) == 7 + test_halfedge(elems, t) - # correct construction from inconsistent orientation - e = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) - t = HalfEdgeTopology(e) - n = collect(elements(t)) - @test n[1] == e[1] - @test n[2] != e[2] - @test n[3] != e[3] - @test n[4] != e[4] + # correct construction from inconsistent orientation + e = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) + t = HalfEdgeTopology(e) + n = collect(elements(t)) + @test n[1] == e[1] + @test n[2] != e[2] + @test n[3] != e[3] + @test n[4] != e[4] - # more challenging case with inconsistent orientation - e = connect.([(4, 1, 5), (2, 6, 4), (3, 5, 6), (4, 5, 6)]) - t = HalfEdgeTopology(e) - n = collect(elements(t)) - @test n == connect.([(5, 4, 1), (6, 2, 4), (6, 5, 3), (4, 5, 6)]) - end + # more challenging case with inconsistent orientation + e = connect.([(4, 1, 5), (2, 6, 4), (3, 5, 6), (4, 5, 6)]) + t = HalfEdgeTopology(e) + n = collect(elements(t)) + @test n == connect.([(5, 4, 1), (6, 2, 4), (6, 5, 3), (4, 5, 6)]) +end - @testset "SimpleTopology" begin - # 2 triangles - elems = connect.([(1, 2, 3), (4, 3, 2)]) - t = SimpleTopology(elems) - @test paramdim(t) == 2 - @test connec4elem(t, 1) == (1, 2, 3) - @test connec4elem(t, 2) == (4, 3, 2) - @test nvertices(t) == 4 - @test nelements(t) == 2 - @test vertices(t) == 1:4 - @test vertex(t, 1) == 1 - @test vertex(t, 4) == 4 - @test nfaces(t, 2) == 2 - @test nfaces(t, 1) == 0 - @test nfaces(t, 0) == 4 +@testitem "SimpleTopology" setup = [Setup] begin + # 2 triangles + elems = connect.([(1, 2, 3), (4, 3, 2)]) + t = SimpleTopology(elems) + @test paramdim(t) == 2 + @test connec4elem(t, 1) == (1, 2, 3) + @test connec4elem(t, 2) == (4, 3, 2) + @test nvertices(t) == 4 + @test nelements(t) == 2 + @test vertices(t) == 1:4 + @test vertex(t, 1) == 1 + @test vertex(t, 4) == 4 + @test nfaces(t, 2) == 2 + @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 4 - # 2 triangles + 2 quadrangles - elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) - t = SimpleTopology(elems) - @test connec4elem(t, 1) == (1, 2, 6, 5) - @test connec4elem(t, 2) == (2, 4, 6) - @test connec4elem(t, 3) == (4, 3, 5, 6) - @test connec4elem(t, 4) == (1, 5, 3) - @test nelements(t) == 4 - @test nfacets(t) == 0 - @test nvertices(t) == 6 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 0 - @test nfaces(t, 0) == 6 + # 2 triangles + 2 quadrangles + elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) + t = SimpleTopology(elems) + @test connec4elem(t, 1) == (1, 2, 6, 5) + @test connec4elem(t, 2) == (2, 4, 6) + @test connec4elem(t, 3) == (4, 3, 5, 6) + @test connec4elem(t, 4) == (1, 5, 3) + @test nelements(t) == 4 + @test nfacets(t) == 0 + @test nvertices(t) == 6 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 6 - # 1 triangle + 3 quadrangles + 1 triangle hole - elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) - t = SimpleTopology(elems) - @test connec4elem(t, 1) == (1, 2, 6, 5) - @test connec4elem(t, 2) == (2, 4, 7, 6) - @test connec4elem(t, 3) == (4, 3, 7) - @test connec4elem(t, 4) == (3, 1, 5, 7) - @test nelements(t) == 4 - @test nfacets(t) == 0 - @test nvertices(t) == 7 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 0 - @test nfaces(t, 0) == 7 + # 1 triangle + 3 quadrangles + 1 triangle hole + elems = connect.([(1, 2, 6, 5), (2, 4, 7, 6), (4, 3, 7), (3, 1, 5, 7)]) + t = SimpleTopology(elems) + @test connec4elem(t, 1) == (1, 2, 6, 5) + @test connec4elem(t, 2) == (2, 4, 7, 6) + @test connec4elem(t, 3) == (4, 3, 7) + @test connec4elem(t, 4) == (3, 1, 5, 7) + @test nelements(t) == 4 + @test nfacets(t) == 0 + @test nvertices(t) == 7 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 0 + @test nfaces(t, 0) == 7 - # convert from other topologies - g = GridTopology(2, 2) - t = convert(SimpleTopology, g) - @test nelements(t) == 4 - @test nfacets(t) == 12 - @test nvertices(t) == 9 - @test nfaces(t, 2) == 4 - @test nfaces(t, 1) == 12 - @test nfaces(t, 0) == 9 - end + # convert from other topologies + g = GridTopology(2, 2) + t = convert(SimpleTopology, g) + @test nelements(t) == 4 + @test nfacets(t) == 12 + @test nvertices(t) == 9 + @test nfaces(t, 2) == 4 + @test nfaces(t, 1) == 12 + @test nfaces(t, 0) == 9 end diff --git a/test/toporelations.jl b/test/toporelations.jl index a398f775f..4b158ade1 100644 --- a/test/toporelations.jl +++ b/test/toporelations.jl @@ -1,572 +1,570 @@ -@testset "TopologicalRelation" begin - @testset "GridTopology" begin - # 3 grid - t = GridTopology(3) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 2) - @test ∂(2) == (2, 3) - @test ∂(3) == (3, 4) - 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (1, 2) - @test 𝒞(3) == (2, 3) - @test 𝒞(4) == (3,) - 𝒜 = Adjacency{1}(t) - @test 𝒜(1) == (2,) - @test 𝒜(2) == (1, 3) - @test 𝒜(3) == (2,) - 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == (2,) - @test 𝒜(2) == (1, 3) - @test 𝒜(3) == (2, 4) - @test 𝒜(4) == (3,) +@testitem "GridTopology" setup = [Setup] begin + # 3 grid + t = GridTopology(3) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 2) + @test ∂(2) == (2, 3) + @test ∂(3) == (3, 4) + 𝒞 = Coboundary{0,1}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2, 3) + @test 𝒞(4) == (3,) + 𝒜 = Adjacency{1}(t) + @test 𝒜(1) == (2,) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2,) + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == (2,) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2, 4) + @test 𝒜(4) == (3,) - # 2x3 grid - t = GridTopology(2, 3) - ∂ = Boundary{2,0}(t) - @test ∂(1) == (1, 2, 5, 4) - @test ∂(2) == (2, 3, 6, 5) - @test ∂(3) == (4, 5, 8, 7) - @test ∂(4) == (5, 6, 9, 8) - @test ∂(5) == (7, 8, 11, 10) - @test ∂(6) == (8, 9, 12, 11) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (1, 2) - @test 𝒞(3) == (2,) - @test 𝒞(4) == (1, 3) - @test 𝒞(5) == (1, 2, 3, 4) - @test 𝒞(6) == (2, 4) - @test 𝒞(7) == (3, 5) - @test 𝒞(8) == (3, 4, 5, 6) - @test 𝒞(9) == (4, 6) - @test 𝒞(10) == (5,) - @test 𝒞(11) == (5, 6) - @test 𝒞(12) == (6,) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 4) - @test ∂(2) == (2, 5) - @test ∂(3) == (3, 6) - @test ∂(4) == (4, 7) - @test ∂(5) == (5, 8) - @test ∂(6) == (6, 9) - @test ∂(7) == (7, 10) - @test ∂(8) == (8, 11) - @test ∂(9) == (9, 12) - @test ∂(10) == (1, 2) - @test ∂(11) == (4, 5) - @test ∂(12) == (7, 8) - @test ∂(13) == (10, 11) - @test ∂(14) == (2, 3) - @test ∂(15) == (5, 6) - @test ∂(16) == (8, 9) - @test ∂(17) == (11, 12) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (2, 3) - @test 𝒜(2) == (1, 4) - @test 𝒜(3) == (4, 1, 5) - @test 𝒜(4) == (3, 2, 6) - @test 𝒜(5) == (6, 3) - @test 𝒜(6) == (5, 4) + # 2x3 grid + t = GridTopology(2, 3) + ∂ = Boundary{2,0}(t) + @test ∂(1) == (1, 2, 5, 4) + @test ∂(2) == (2, 3, 6, 5) + @test ∂(3) == (4, 5, 8, 7) + @test ∂(4) == (5, 6, 9, 8) + @test ∂(5) == (7, 8, 11, 10) + @test ∂(6) == (8, 9, 12, 11) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3, 5) + @test 𝒞(8) == (3, 4, 5, 6) + @test 𝒞(9) == (4, 6) + @test 𝒞(10) == (5,) + @test 𝒞(11) == (5, 6) + @test 𝒞(12) == (6,) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 4) + @test ∂(2) == (2, 5) + @test ∂(3) == (3, 6) + @test ∂(4) == (4, 7) + @test ∂(5) == (5, 8) + @test ∂(6) == (6, 9) + @test ∂(7) == (7, 10) + @test ∂(8) == (8, 11) + @test ∂(9) == (9, 12) + @test ∂(10) == (1, 2) + @test ∂(11) == (4, 5) + @test ∂(12) == (7, 8) + @test ∂(13) == (10, 11) + @test ∂(14) == (2, 3) + @test ∂(15) == (5, 6) + @test ∂(16) == (8, 9) + @test ∂(17) == (11, 12) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (2, 3) + @test 𝒜(2) == (1, 4) + @test 𝒜(3) == (4, 1, 5) + @test 𝒜(4) == (3, 2, 6) + @test 𝒜(5) == (6, 3) + @test 𝒜(6) == (5, 4) - # 2x2 grid - t = GridTopology(2, 2) - ∂ = Boundary{2,0}(t) - @test ∂(1) == (1, 2, 5, 4) - @test ∂(2) == (2, 3, 6, 5) - @test ∂(3) == (4, 5, 8, 7) - @test ∂(4) == (5, 6, 9, 8) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (1, 2) - @test 𝒞(3) == (2,) - @test 𝒞(4) == (1, 3) - @test 𝒞(5) == (1, 2, 3, 4) - @test 𝒞(6) == (2, 4) - @test 𝒞(7) == (3,) - @test 𝒞(8) == (3, 4) - @test 𝒞(9) == (4,) - 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == (2, 4) - @test 𝒜(2) == (1, 3, 5) - @test 𝒜(3) == (2, 6) - @test 𝒜(4) == (5, 1, 7) - @test 𝒜(5) == (4, 6, 2, 8) - @test 𝒜(6) == (5, 3, 9) - @test 𝒜(7) == (8, 4) - @test 𝒜(8) == (7, 9, 5) - @test 𝒜(9) == (8, 6) + # 2x2 grid + t = GridTopology(2, 2) + ∂ = Boundary{2,0}(t) + @test ∂(1) == (1, 2, 5, 4) + @test ∂(2) == (2, 3, 6, 5) + @test ∂(3) == (4, 5, 8, 7) + @test ∂(4) == (5, 6, 9, 8) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (4,) + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == (2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 6) + @test 𝒜(4) == (5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 3, 9) + @test 𝒜(7) == (8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 6) - # 2x2 (periodic x aperiodic) grid - t = GridTopology((2, 2), (true, false)) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 3) - @test ∂(2) == (2, 4) - @test ∂(3) == (3, 5) - @test ∂(4) == (4, 6) - @test ∂(5) == (1, 2) - @test ∂(6) == (3, 4) - @test ∂(7) == (5, 6) - @test ∂(8) == (2, 1) - @test ∂(9) == (4, 3) - @test ∂(10) == (6, 5) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (2, 1) - @test 𝒞(2) == (1, 2) - @test 𝒞(3) == (2, 1, 4, 3) - @test 𝒞(4) == (1, 2, 3, 4) - @test 𝒞(5) == (4, 3) - @test 𝒞(6) == (3, 4) + # 2x2 (periodic x aperiodic) grid + t = GridTopology((2, 2), (true, false)) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 3) + @test ∂(2) == (2, 4) + @test ∂(3) == (3, 5) + @test ∂(4) == (4, 6) + @test ∂(5) == (1, 2) + @test ∂(6) == (3, 4) + @test ∂(7) == (5, 6) + @test ∂(8) == (2, 1) + @test ∂(9) == (4, 3) + @test ∂(10) == (6, 5) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (2, 1) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2, 1, 4, 3) + @test 𝒞(4) == (1, 2, 3, 4) + @test 𝒞(5) == (4, 3) + @test 𝒞(6) == (3, 4) - # 2x2 (aperiodic x periodic) grid - t = GridTopology((2, 2), (false, true)) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 4) - @test ∂(2) == (2, 5) - @test ∂(3) == (3, 6) - @test ∂(4) == (4, 1) - @test ∂(5) == (5, 2) - @test ∂(6) == (6, 3) - @test ∂(7) == (1, 2) - @test ∂(8) == (4, 5) - @test ∂(9) == (2, 3) - @test ∂(10) == (5, 6) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (3, 1) - @test 𝒞(2) == (3, 4, 1, 2) - @test 𝒞(3) == (4, 2) - @test 𝒞(4) == (1, 3) - @test 𝒞(5) == (1, 2, 3, 4) - @test 𝒞(6) == (2, 4) + # 2x2 (aperiodic x periodic) grid + t = GridTopology((2, 2), (false, true)) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 4) + @test ∂(2) == (2, 5) + @test ∂(3) == (3, 6) + @test ∂(4) == (4, 1) + @test ∂(5) == (5, 2) + @test ∂(6) == (6, 3) + @test ∂(7) == (1, 2) + @test ∂(8) == (4, 5) + @test ∂(9) == (2, 3) + @test ∂(10) == (5, 6) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (3, 1) + @test 𝒞(2) == (3, 4, 1, 2) + @test 𝒞(3) == (4, 2) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) - # 2x2 (periodic x periodic) grid - t = GridTopology((2, 2), (true, true)) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 3) - @test ∂(2) == (2, 4) - @test ∂(3) == (3, 1) - @test ∂(4) == (4, 2) - @test ∂(5) == (1, 2) - @test ∂(6) == (3, 4) - @test ∂(7) == (2, 1) - @test ∂(8) == (4, 3) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (4, 3, 2, 1) - @test 𝒞(2) == (3, 4, 1, 2) - @test 𝒞(3) == (2, 1, 4, 3) - @test 𝒞(4) == (1, 2, 3, 4) + # 2x2 (periodic x periodic) grid + t = GridTopology((2, 2), (true, true)) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 3) + @test ∂(2) == (2, 4) + @test ∂(3) == (3, 1) + @test ∂(4) == (4, 2) + @test ∂(5) == (1, 2) + @test ∂(6) == (3, 4) + @test ∂(7) == (2, 1) + @test ∂(8) == (4, 3) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (4, 3, 2, 1) + @test 𝒞(2) == (3, 4, 1, 2) + @test 𝒞(3) == (2, 1, 4, 3) + @test 𝒞(4) == (1, 2, 3, 4) - # 3x3 grid - t = GridTopology(3, 3) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (2, 4) - @test 𝒜(2) == (1, 3, 5) - @test 𝒜(3) == (2, 6) - @test 𝒜(4) == (5, 1, 7) - @test 𝒜(5) == (4, 6, 2, 8) - @test 𝒜(6) == (5, 3, 9) - @test 𝒜(7) == (8, 4) - @test 𝒜(8) == (7, 9, 5) - @test 𝒜(9) == (8, 6) + # 3x3 grid + t = GridTopology(3, 3) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 6) + @test 𝒜(4) == (5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 3, 9) + @test 𝒜(7) == (8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 6) - # 3x3 grid (periodic x aperiodic) grid - t = GridTopology((3, 3), (true, false)) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (3, 2, 4) - @test 𝒜(2) == (1, 3, 5) - @test 𝒜(3) == (2, 1, 6) - @test 𝒜(4) == (6, 5, 1, 7) - @test 𝒜(5) == (4, 6, 2, 8) - @test 𝒜(6) == (5, 4, 3, 9) - @test 𝒜(7) == (9, 8, 4) - @test 𝒜(8) == (7, 9, 5) - @test 𝒜(9) == (8, 7, 6) + # 3x3 grid (periodic x aperiodic) grid + t = GridTopology((3, 3), (true, false)) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (3, 2, 4) + @test 𝒜(2) == (1, 3, 5) + @test 𝒜(3) == (2, 1, 6) + @test 𝒜(4) == (6, 5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 4, 3, 9) + @test 𝒜(7) == (9, 8, 4) + @test 𝒜(8) == (7, 9, 5) + @test 𝒜(9) == (8, 7, 6) - # 3x3 grid (periodic x periodic) grid - t = GridTopology((3, 3), (true, true)) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (3, 2, 7, 4) - @test 𝒜(2) == (1, 3, 8, 5) - @test 𝒜(3) == (2, 1, 9, 6) - @test 𝒜(4) == (6, 5, 1, 7) - @test 𝒜(5) == (4, 6, 2, 8) - @test 𝒜(6) == (5, 4, 3, 9) - @test 𝒜(7) == (9, 8, 4, 1) - @test 𝒜(8) == (7, 9, 5, 2) - @test 𝒜(9) == (8, 7, 6, 3) + # 3x3 grid (periodic x periodic) grid + t = GridTopology((3, 3), (true, true)) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (3, 2, 7, 4) + @test 𝒜(2) == (1, 3, 8, 5) + @test 𝒜(3) == (2, 1, 9, 6) + @test 𝒜(4) == (6, 5, 1, 7) + @test 𝒜(5) == (4, 6, 2, 8) + @test 𝒜(6) == (5, 4, 3, 9) + @test 𝒜(7) == (9, 8, 4, 1) + @test 𝒜(8) == (7, 9, 5, 2) + @test 𝒜(9) == (8, 7, 6, 3) - # 3x4 grid - t = GridTopology(3, 4) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 2, 17, 18) - @test ∂(2) == (2, 3, 22, 23) - @test ∂(3) == (3, 4, 27, 28) - @test ∂(4) == (5, 6, 18, 19) - @test ∂(5) == (6, 7, 23, 24) - @test ∂(6) == (7, 8, 28, 29) - @test ∂(7) == (9, 10, 19, 20) - @test ∂(8) == (10, 11, 24, 25) - @test ∂(9) == (11, 12, 29, 30) - @test ∂(10) == (13, 14, 20, 21) - @test ∂(11) == (14, 15, 25, 26) - @test ∂(12) == (15, 16, 30, 31) + # 3x4 grid + t = GridTopology(3, 4) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 2, 17, 18) + @test ∂(2) == (2, 3, 22, 23) + @test ∂(3) == (3, 4, 27, 28) + @test ∂(4) == (5, 6, 18, 19) + @test ∂(5) == (6, 7, 23, 24) + @test ∂(6) == (7, 8, 28, 29) + @test ∂(7) == (9, 10, 19, 20) + @test ∂(8) == (10, 11, 24, 25) + @test ∂(9) == (11, 12, 29, 30) + @test ∂(10) == (13, 14, 20, 21) + @test ∂(11) == (14, 15, 25, 26) + @test ∂(12) == (15, 16, 30, 31) - # 3x4 (periodic x aperiodic) grid - t = GridTopology((3, 4), (true, false)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 2, 13, 14) - @test ∂(2) == (2, 3, 18, 19) - @test ∂(3) == (3, 1, 23, 24) - @test ∂(4) == (4, 5, 14, 15) - @test ∂(5) == (5, 6, 19, 20) - @test ∂(6) == (6, 4, 24, 25) - @test ∂(7) == (7, 8, 15, 16) - @test ∂(8) == (8, 9, 20, 21) - @test ∂(9) == (9, 7, 25, 26) - @test ∂(10) == (10, 11, 16, 17) - @test ∂(11) == (11, 12, 21, 22) - @test ∂(12) == (12, 10, 26, 27) + # 3x4 (periodic x aperiodic) grid + t = GridTopology((3, 4), (true, false)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 2, 13, 14) + @test ∂(2) == (2, 3, 18, 19) + @test ∂(3) == (3, 1, 23, 24) + @test ∂(4) == (4, 5, 14, 15) + @test ∂(5) == (5, 6, 19, 20) + @test ∂(6) == (6, 4, 24, 25) + @test ∂(7) == (7, 8, 15, 16) + @test ∂(8) == (8, 9, 20, 21) + @test ∂(9) == (9, 7, 25, 26) + @test ∂(10) == (10, 11, 16, 17) + @test ∂(11) == (11, 12, 21, 22) + @test ∂(12) == (12, 10, 26, 27) - # 3x4 (aperiodic x periodic) grid - t = GridTopology((3, 4), (false, true)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 2, 17, 18) - @test ∂(2) == (2, 3, 21, 22) - @test ∂(3) == (3, 4, 25, 26) - @test ∂(4) == (5, 6, 18, 19) - @test ∂(5) == (6, 7, 22, 23) - @test ∂(6) == (7, 8, 26, 27) - @test ∂(7) == (9, 10, 19, 20) - @test ∂(8) == (10, 11, 23, 24) - @test ∂(9) == (11, 12, 27, 28) - @test ∂(10) == (13, 14, 20, 17) - @test ∂(11) == (14, 15, 24, 21) - @test ∂(12) == (15, 16, 28, 25) + # 3x4 (aperiodic x periodic) grid + t = GridTopology((3, 4), (false, true)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 2, 17, 18) + @test ∂(2) == (2, 3, 21, 22) + @test ∂(3) == (3, 4, 25, 26) + @test ∂(4) == (5, 6, 18, 19) + @test ∂(5) == (6, 7, 22, 23) + @test ∂(6) == (7, 8, 26, 27) + @test ∂(7) == (9, 10, 19, 20) + @test ∂(8) == (10, 11, 23, 24) + @test ∂(9) == (11, 12, 27, 28) + @test ∂(10) == (13, 14, 20, 17) + @test ∂(11) == (14, 15, 24, 21) + @test ∂(12) == (15, 16, 28, 25) - # 3x4 (periodic x periodic) grid - t = GridTopology((3, 4), (true, true)) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 2, 13, 14) - @test ∂(2) == (2, 3, 17, 18) - @test ∂(3) == (3, 1, 21, 22) - @test ∂(4) == (4, 5, 14, 15) - @test ∂(5) == (5, 6, 18, 19) - @test ∂(6) == (6, 4, 22, 23) - @test ∂(7) == (7, 8, 15, 16) - @test ∂(8) == (8, 9, 19, 20) - @test ∂(9) == (9, 7, 23, 24) - @test ∂(10) == (10, 11, 16, 13) - @test ∂(11) == (11, 12, 20, 17) - @test ∂(12) == (12, 10, 24, 21) + # 3x4 (periodic x periodic) grid + t = GridTopology((3, 4), (true, true)) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 2, 13, 14) + @test ∂(2) == (2, 3, 17, 18) + @test ∂(3) == (3, 1, 21, 22) + @test ∂(4) == (4, 5, 14, 15) + @test ∂(5) == (5, 6, 18, 19) + @test ∂(6) == (6, 4, 22, 23) + @test ∂(7) == (7, 8, 15, 16) + @test ∂(8) == (8, 9, 19, 20) + @test ∂(9) == (9, 7, 23, 24) + @test ∂(10) == (10, 11, 16, 13) + @test ∂(11) == (11, 12, 20, 17) + @test ∂(12) == (12, 10, 24, 21) - # 2x2x2 grid - t = GridTopology(2, 2, 2) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 13, 14, 25, 26) - @test ∂(2) == (2, 3, 16, 17, 28, 29) - @test ∂(3) == (4, 5, 14, 15, 31, 32) - @test ∂(4) == (5, 6, 17, 18, 34, 35) - @test ∂(5) == (7, 8, 19, 20, 26, 27) - @test ∂(6) == (8, 9, 22, 23, 29, 30) - @test ∂(7) == (10, 11, 20, 21, 32, 33) - @test ∂(8) == (11, 12, 23, 24, 35, 36) - 𝒞 = Coboundary{0,3}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (1, 2) - @test 𝒞(3) == (2,) - @test 𝒞(4) == (1, 3) - @test 𝒞(5) == (1, 2, 3, 4) - @test 𝒞(6) == (2, 4) - @test 𝒞(7) == (3,) - @test 𝒞(8) == (3, 4) - @test 𝒞(9) == (4,) - @test 𝒞(10) == (1, 5) - @test 𝒞(11) == (1, 2, 5, 6) - @test 𝒞(12) == (2, 6) - @test 𝒞(13) == (1, 3, 5, 7) - @test 𝒞(14) == (1, 2, 3, 4, 5, 6, 7, 8) - @test 𝒞(15) == (2, 4, 6, 8) - @test 𝒞(16) == (3, 7) - @test 𝒞(17) == (3, 4, 7, 8) - @test 𝒞(18) == (4, 8) - @test 𝒞(19) == (5,) - @test 𝒞(20) == (5, 6) - @test 𝒞(21) == (6,) - @test 𝒞(22) == (5, 7) - @test 𝒞(23) == (5, 6, 7, 8) - @test 𝒞(24) == (6, 8) - @test 𝒞(25) == (7,) - @test 𝒞(26) == (7, 8) - @test 𝒞(27) == (8,) - 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == (2, 3, 5) - @test 𝒜(2) == (1, 4, 6) - @test 𝒜(3) == (4, 1, 7) - @test 𝒜(4) == (3, 2, 8) - @test 𝒜(5) == (6, 7, 1) - @test 𝒜(6) == (5, 8, 2) - @test 𝒜(7) == (8, 5, 3) - @test 𝒜(8) == (7, 6, 4) + # 2x2x2 grid + t = GridTopology(2, 2, 2) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 13, 14, 25, 26) + @test ∂(2) == (2, 3, 16, 17, 28, 29) + @test ∂(3) == (4, 5, 14, 15, 31, 32) + @test ∂(4) == (5, 6, 17, 18, 34, 35) + @test ∂(5) == (7, 8, 19, 20, 26, 27) + @test ∂(6) == (8, 9, 22, 23, 29, 30) + @test ∂(7) == (10, 11, 20, 21, 32, 33) + @test ∂(8) == (11, 12, 23, 24, 35, 36) + 𝒞 = Coboundary{0,3}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (1, 2) + @test 𝒞(3) == (2,) + @test 𝒞(4) == (1, 3) + @test 𝒞(5) == (1, 2, 3, 4) + @test 𝒞(6) == (2, 4) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (4,) + @test 𝒞(10) == (1, 5) + @test 𝒞(11) == (1, 2, 5, 6) + @test 𝒞(12) == (2, 6) + @test 𝒞(13) == (1, 3, 5, 7) + @test 𝒞(14) == (1, 2, 3, 4, 5, 6, 7, 8) + @test 𝒞(15) == (2, 4, 6, 8) + @test 𝒞(16) == (3, 7) + @test 𝒞(17) == (3, 4, 7, 8) + @test 𝒞(18) == (4, 8) + @test 𝒞(19) == (5,) + @test 𝒞(20) == (5, 6) + @test 𝒞(21) == (6,) + @test 𝒞(22) == (5, 7) + @test 𝒞(23) == (5, 6, 7, 8) + @test 𝒞(24) == (6, 8) + @test 𝒞(25) == (7,) + @test 𝒞(26) == (7, 8) + @test 𝒞(27) == (8,) + 𝒜 = Adjacency{3}(t) + @test 𝒜(1) == (2, 3, 5) + @test 𝒜(2) == (1, 4, 6) + @test 𝒜(3) == (4, 1, 7) + @test 𝒜(4) == (3, 2, 8) + @test 𝒜(5) == (6, 7, 1) + @test 𝒜(6) == (5, 8, 2) + @test 𝒜(7) == (8, 5, 3) + @test 𝒜(8) == (7, 6, 4) - # 2x2x2 (periodic x aperiodic x aperiodic) grid - t = GridTopology((2, 2, 2), (true, false, false)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 9, 10, 21, 22) - @test ∂(2) == (2, 1, 12, 13, 24, 25) - @test ∂(3) == (3, 4, 10, 11, 27, 28) - @test ∂(4) == (4, 3, 13, 14, 30, 31) - @test ∂(5) == (5, 6, 15, 16, 22, 23) - @test ∂(6) == (6, 5, 18, 19, 25, 26) - @test ∂(7) == (7, 8, 16, 17, 28, 29) - @test ∂(8) == (8, 7, 19, 20, 31, 32) + # 2x2x2 (periodic x aperiodic x aperiodic) grid + t = GridTopology((2, 2, 2), (true, false, false)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 9, 10, 21, 22) + @test ∂(2) == (2, 1, 12, 13, 24, 25) + @test ∂(3) == (3, 4, 10, 11, 27, 28) + @test ∂(4) == (4, 3, 13, 14, 30, 31) + @test ∂(5) == (5, 6, 15, 16, 22, 23) + @test ∂(6) == (6, 5, 18, 19, 25, 26) + @test ∂(7) == (7, 8, 16, 17, 28, 29) + @test ∂(8) == (8, 7, 19, 20, 31, 32) - # 2x2x2 (aperiodic x periodic x aperiodic) grid - t = GridTopology((2, 2, 2), (false, true, false)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 13, 14, 21, 22) - @test ∂(2) == (2, 3, 15, 16, 24, 25) - @test ∂(3) == (4, 5, 14, 13, 27, 28) - @test ∂(4) == (5, 6, 16, 15, 30, 31) - @test ∂(5) == (7, 8, 17, 18, 22, 23) - @test ∂(6) == (8, 9, 19, 20, 25, 26) - @test ∂(7) == (10, 11, 18, 17, 28, 29) - @test ∂(8) == (11, 12, 20, 19, 31, 32) + # 2x2x2 (aperiodic x periodic x aperiodic) grid + t = GridTopology((2, 2, 2), (false, true, false)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 13, 14, 21, 22) + @test ∂(2) == (2, 3, 15, 16, 24, 25) + @test ∂(3) == (4, 5, 14, 13, 27, 28) + @test ∂(4) == (5, 6, 16, 15, 30, 31) + @test ∂(5) == (7, 8, 17, 18, 22, 23) + @test ∂(6) == (8, 9, 19, 20, 25, 26) + @test ∂(7) == (10, 11, 18, 17, 28, 29) + @test ∂(8) == (11, 12, 20, 19, 31, 32) - # 2x2x2 (aperiodic x aperiodic x periodic) grid - t = GridTopology((2, 2, 2), (false, false, true)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 13, 14, 25, 26) - @test ∂(2) == (2, 3, 16, 17, 27, 28) - @test ∂(3) == (4, 5, 14, 15, 29, 30) - @test ∂(4) == (5, 6, 17, 18, 31, 32) - @test ∂(5) == (7, 8, 19, 20, 26, 25) - @test ∂(6) == (8, 9, 22, 23, 28, 27) - @test ∂(7) == (10, 11, 20, 21, 30, 29) - @test ∂(8) == (11, 12, 23, 24, 32, 31) + # 2x2x2 (aperiodic x aperiodic x periodic) grid + t = GridTopology((2, 2, 2), (false, false, true)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 13, 14, 25, 26) + @test ∂(2) == (2, 3, 16, 17, 27, 28) + @test ∂(3) == (4, 5, 14, 15, 29, 30) + @test ∂(4) == (5, 6, 17, 18, 31, 32) + @test ∂(5) == (7, 8, 19, 20, 26, 25) + @test ∂(6) == (8, 9, 22, 23, 28, 27) + @test ∂(7) == (10, 11, 20, 21, 30, 29) + @test ∂(8) == (11, 12, 23, 24, 32, 31) - # 2x2x2 (periodic x periodic x aperiodic) grid - t = GridTopology((2, 2, 2), (true, true, false)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 9, 10, 17, 18) - @test ∂(2) == (2, 1, 11, 12, 20, 21) - @test ∂(3) == (3, 4, 10, 9, 23, 24) - @test ∂(4) == (4, 3, 12, 11, 26, 27) - @test ∂(5) == (5, 6, 13, 14, 18, 19) - @test ∂(6) == (6, 5, 15, 16, 21, 22) - @test ∂(7) == (7, 8, 14, 13, 24, 25) - @test ∂(8) == (8, 7, 16, 15, 27, 28) + # 2x2x2 (periodic x periodic x aperiodic) grid + t = GridTopology((2, 2, 2), (true, true, false)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 9, 10, 17, 18) + @test ∂(2) == (2, 1, 11, 12, 20, 21) + @test ∂(3) == (3, 4, 10, 9, 23, 24) + @test ∂(4) == (4, 3, 12, 11, 26, 27) + @test ∂(5) == (5, 6, 13, 14, 18, 19) + @test ∂(6) == (6, 5, 15, 16, 21, 22) + @test ∂(7) == (7, 8, 14, 13, 24, 25) + @test ∂(8) == (8, 7, 16, 15, 27, 28) - # 2x2x2 (periodic x aperiodic x periodic) grid - t = GridTopology((2, 2, 2), (true, false, true)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 9, 10, 21, 22) - @test ∂(2) == (2, 1, 12, 13, 23, 24) - @test ∂(3) == (3, 4, 10, 11, 25, 26) - @test ∂(4) == (4, 3, 13, 14, 27, 28) - @test ∂(5) == (5, 6, 15, 16, 22, 21) - @test ∂(6) == (6, 5, 18, 19, 24, 23) - @test ∂(7) == (7, 8, 16, 17, 26, 25) - @test ∂(8) == (8, 7, 19, 20, 28, 27) + # 2x2x2 (periodic x aperiodic x periodic) grid + t = GridTopology((2, 2, 2), (true, false, true)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 9, 10, 21, 22) + @test ∂(2) == (2, 1, 12, 13, 23, 24) + @test ∂(3) == (3, 4, 10, 11, 25, 26) + @test ∂(4) == (4, 3, 13, 14, 27, 28) + @test ∂(5) == (5, 6, 15, 16, 22, 21) + @test ∂(6) == (6, 5, 18, 19, 24, 23) + @test ∂(7) == (7, 8, 16, 17, 26, 25) + @test ∂(8) == (8, 7, 19, 20, 28, 27) - # 2x2x2 (aperiodic x periodic x periodic) grid - t = GridTopology((2, 2, 2), (false, true, true)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 13, 14, 21, 22) - @test ∂(2) == (2, 3, 15, 16, 23, 24) - @test ∂(3) == (4, 5, 14, 13, 25, 26) - @test ∂(4) == (5, 6, 16, 15, 27, 28) - @test ∂(5) == (7, 8, 17, 18, 22, 21) - @test ∂(6) == (8, 9, 19, 20, 24, 23) - @test ∂(7) == (10, 11, 18, 17, 26, 25) - @test ∂(8) == (11, 12, 20, 19, 28, 27) + # 2x2x2 (aperiodic x periodic x periodic) grid + t = GridTopology((2, 2, 2), (false, true, true)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 13, 14, 21, 22) + @test ∂(2) == (2, 3, 15, 16, 23, 24) + @test ∂(3) == (4, 5, 14, 13, 25, 26) + @test ∂(4) == (5, 6, 16, 15, 27, 28) + @test ∂(5) == (7, 8, 17, 18, 22, 21) + @test ∂(6) == (8, 9, 19, 20, 24, 23) + @test ∂(7) == (10, 11, 18, 17, 26, 25) + @test ∂(8) == (11, 12, 20, 19, 28, 27) - # 2x2x2 (periodic x periodic x periodic) grid - t = GridTopology((2, 2, 2), (true, true, true)) - ∂ = Boundary{3,2}(t) - @test ∂(1) == (1, 2, 9, 10, 17, 18) - @test ∂(2) == (2, 1, 11, 12, 19, 20) - @test ∂(3) == (3, 4, 10, 9, 21, 22) - @test ∂(4) == (4, 3, 12, 11, 23, 24) - @test ∂(5) == (5, 6, 13, 14, 18, 17) - @test ∂(6) == (6, 5, 15, 16, 20, 19) - @test ∂(7) == (7, 8, 14, 13, 22, 21) - @test ∂(8) == (8, 7, 16, 15, 24, 23) + # 2x2x2 (periodic x periodic x periodic) grid + t = GridTopology((2, 2, 2), (true, true, true)) + ∂ = Boundary{3,2}(t) + @test ∂(1) == (1, 2, 9, 10, 17, 18) + @test ∂(2) == (2, 1, 11, 12, 19, 20) + @test ∂(3) == (3, 4, 10, 9, 21, 22) + @test ∂(4) == (4, 3, 12, 11, 23, 24) + @test ∂(5) == (5, 6, 13, 14, 18, 17) + @test ∂(6) == (6, 5, 15, 16, 20, 19) + @test ∂(7) == (7, 8, 14, 13, 22, 21) + @test ∂(8) == (8, 7, 16, 15, 24, 23) - # 2x3x2 grid - t = GridTopology(2, 3, 2) - ∂ = Boundary{3,0}(t) - @test ∂(1) == (1, 2, 5, 4, 13, 14, 17, 16) - @test ∂(2) == (2, 3, 6, 5, 14, 15, 18, 17) - @test ∂(3) == (4, 5, 8, 7, 16, 17, 20, 19) - @test ∂(12) == (20, 21, 24, 23, 32, 33, 36, 35) + # 2x3x2 grid + t = GridTopology(2, 3, 2) + ∂ = Boundary{3,0}(t) + @test ∂(1) == (1, 2, 5, 4, 13, 14, 17, 16) + @test ∂(2) == (2, 3, 6, 5, 14, 15, 18, 17) + @test ∂(3) == (4, 5, 8, 7, 16, 17, 20, 19) + @test ∂(12) == (20, 21, 24, 23, 32, 33, 36, 35) - # 3x2x2 grid - t = GridTopology(3, 2, 2) - 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == (2, 4, 7) - @test 𝒜(2) == (1, 3, 5, 8) - @test 𝒜(3) == (2, 6, 9) - @test 𝒜(4) == (5, 1, 10) - @test 𝒜(5) == (4, 6, 2, 11) - @test 𝒜(6) == (5, 3, 12) - @test 𝒜(7) == (8, 10, 1) - @test 𝒜(8) == (7, 9, 11, 2) - @test 𝒜(9) == (8, 12, 3) - @test 𝒜(10) == (11, 7, 4) - @test 𝒜(11) == (10, 12, 8, 5) - @test 𝒜(12) == (11, 9, 6) + # 3x2x2 grid + t = GridTopology(3, 2, 2) + 𝒜 = Adjacency{3}(t) + @test 𝒜(1) == (2, 4, 7) + @test 𝒜(2) == (1, 3, 5, 8) + @test 𝒜(3) == (2, 6, 9) + @test 𝒜(4) == (5, 1, 10) + @test 𝒜(5) == (4, 6, 2, 11) + @test 𝒜(6) == (5, 3, 12) + @test 𝒜(7) == (8, 10, 1) + @test 𝒜(8) == (7, 9, 11, 2) + @test 𝒜(9) == (8, 12, 3) + @test 𝒜(10) == (11, 7, 4) + @test 𝒜(11) == (10, 12, 8, 5) + @test 𝒜(12) == (11, 9, 6) - # 3x2x2 (periodic x aperiodic x aperiodic) grid - t = GridTopology((3, 2, 2), (true, false, false)) - 𝒜 = Adjacency{3}(t) - @test 𝒜(1) == (3, 2, 4, 7) - @test 𝒜(2) == (1, 3, 5, 8) - @test 𝒜(3) == (2, 1, 6, 9) - @test 𝒜(4) == (6, 5, 1, 10) - @test 𝒜(5) == (4, 6, 2, 11) - @test 𝒜(6) == (5, 4, 3, 12) - @test 𝒜(7) == (9, 8, 10, 1) - @test 𝒜(8) == (7, 9, 11, 2) - @test 𝒜(9) == (8, 7, 12, 3) - @test 𝒜(10) == (12, 11, 7, 4) - @test 𝒜(11) == (10, 12, 8, 5) - @test 𝒜(12) == (11, 10, 9, 6) + # 3x2x2 (periodic x aperiodic x aperiodic) grid + t = GridTopology((3, 2, 2), (true, false, false)) + 𝒜 = Adjacency{3}(t) + @test 𝒜(1) == (3, 2, 4, 7) + @test 𝒜(2) == (1, 3, 5, 8) + @test 𝒜(3) == (2, 1, 6, 9) + @test 𝒜(4) == (6, 5, 1, 10) + @test 𝒜(5) == (4, 6, 2, 11) + @test 𝒜(6) == (5, 4, 3, 12) + @test 𝒜(7) == (9, 8, 10, 1) + @test 𝒜(8) == (7, 9, 11, 2) + @test 𝒜(9) == (8, 7, 12, 3) + @test 𝒜(10) == (12, 11, 7, 4) + @test 𝒜(11) == (10, 12, 8, 5) + @test 𝒜(12) == (11, 10, 9, 6) - # invalid relations - t = GridTopology(2, 3) - @test_throws AssertionError Boundary{3,0}(t) - @test_throws AssertionError Coboundary{0,3}(t) - @test_throws AssertionError Adjacency{3}(t) - @test_throws AssertionError Boundary{0,2}(t) - @test_throws AssertionError Coboundary{2,0}(t) - end + # invalid relations + t = GridTopology(2, 3) + @test_throws AssertionError Boundary{3,0}(t) + @test_throws AssertionError Coboundary{0,3}(t) + @test_throws AssertionError Adjacency{3}(t) + @test_throws AssertionError Boundary{0,2}(t) + @test_throws AssertionError Coboundary{2,0}(t) +end - @testset "HalfEdgeTopology" begin - # 2 triangles - elems = connect.([(1, 2, 3), (4, 3, 2)]) - t = HalfEdgeTopology(elems) - ∂ = Boundary{2,0}(t) - @test ∂(1) == (2, 3, 1) - @test ∂(2) == (3, 2, 4) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 3, 2) - @test ∂(2) == (1, 4, 5) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (3, 2) - @test ∂(2) == (1, 2) - @test ∂(3) == (3, 1) - @test ∂(4) == (2, 4) - @test ∂(5) == (4, 3) - 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == (2, 3) - @test 𝒞(2) == (4, 1, 2) - @test 𝒞(3) == (3, 1, 5) - @test 𝒞(4) == (5, 4) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (2, 1) - @test 𝒞(3) == (1, 2) - @test 𝒞(4) == (2,) - 𝒞 = Coboundary{1,2}(t) - @test 𝒞(1) == (2, 1) - @test 𝒞(2) == (1,) - @test 𝒞(3) == (1,) - @test 𝒞(4) == (2,) - @test 𝒞(5) == (2,) - 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == (2, 3) - @test 𝒜(2) == (4, 3, 1) - @test 𝒜(3) == (1, 2, 4) - @test 𝒜(4) == (3, 2) +@testitem "HalfEdgeTopology" setup = [Setup] begin + # 2 triangles + elems = connect.([(1, 2, 3), (4, 3, 2)]) + t = HalfEdgeTopology(elems) + ∂ = Boundary{2,0}(t) + @test ∂(1) == (2, 3, 1) + @test ∂(2) == (3, 2, 4) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 3, 2) + @test ∂(2) == (1, 4, 5) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (3, 2) + @test ∂(2) == (1, 2) + @test ∂(3) == (3, 1) + @test ∂(4) == (2, 4) + @test ∂(5) == (4, 3) + 𝒞 = Coboundary{0,1}(t) + @test 𝒞(1) == (2, 3) + @test 𝒞(2) == (4, 1, 2) + @test 𝒞(3) == (3, 1, 5) + @test 𝒞(4) == (5, 4) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (2, 1) + @test 𝒞(3) == (1, 2) + @test 𝒞(4) == (2,) + 𝒞 = Coboundary{1,2}(t) + @test 𝒞(1) == (2, 1) + @test 𝒞(2) == (1,) + @test 𝒞(3) == (1,) + @test 𝒞(4) == (2,) + @test 𝒞(5) == (2,) + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == (2, 3) + @test 𝒜(2) == (4, 3, 1) + @test 𝒜(3) == (1, 2, 4) + @test 𝒜(4) == (3, 2) - # 2 triangles + 2 quadrangles - elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) - t = HalfEdgeTopology(elems) - ∂ = Boundary{2,0}(t) - @test ∂(1) == (1, 2, 6, 5) - @test ∂(2) == (6, 2, 4) - @test ∂(3) == (6, 4, 3, 5) - @test ∂(4) == (3, 1, 5) - ∂ = Boundary{2,1}(t) - @test ∂(1) == (1, 3, 5, 6) - @test ∂(2) == (3, 9, 4) - @test ∂(3) == (4, 7, 8, 5) - @test ∂(4) == (2, 6, 8) - ∂ = Boundary{1,0}(t) - @test ∂(1) == (1, 2) - @test ∂(2) == (3, 1) - @test ∂(3) == (6, 2) - @test ∂(4) == (4, 6) - @test ∂(5) == (5, 6) - @test ∂(6) == (1, 5) - @test ∂(7) == (4, 3) - @test ∂(8) == (3, 5) - @test ∂(9) == (2, 4) - 𝒞 = Coboundary{0,1}(t) - @test 𝒞(1) == (1, 6, 2) - @test 𝒞(2) == (9, 3, 1) - @test 𝒞(3) == (2, 8, 7) - @test 𝒞(4) == (7, 4, 9) - @test 𝒞(5) == (5, 8, 6) - @test 𝒞(6) == (3, 4, 5) - 𝒞 = Coboundary{0,2}(t) - @test 𝒞(1) == (1, 4) - @test 𝒞(2) == (2, 1) - @test 𝒞(3) == (4, 3) - @test 𝒞(4) == (3, 2) - @test 𝒞(5) == (3, 4, 1) - @test 𝒞(6) == (2, 3, 1) - 𝒞 = Coboundary{1,2}(t) - @test 𝒞(1) == (1,) - @test 𝒞(2) == (4,) - @test 𝒞(3) == (2, 1) - @test 𝒞(4) == (2, 3) - @test 𝒞(5) == (3, 1) - @test 𝒞(6) == (4, 1) - @test 𝒞(7) == (3,) - @test 𝒞(8) == (3, 4) - @test 𝒞(9) == (2,) - 𝒜 = Adjacency{0}(t) - @test 𝒜(1) == (2, 5, 3) - @test 𝒜(2) == (4, 6, 1) - @test 𝒜(3) == (1, 5, 4) - @test 𝒜(4) == (3, 6, 2) - @test 𝒜(5) == (6, 3, 1) - @test 𝒜(6) == (2, 4, 5) + # 2 triangles + 2 quadrangles + elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) + t = HalfEdgeTopology(elems) + ∂ = Boundary{2,0}(t) + @test ∂(1) == (1, 2, 6, 5) + @test ∂(2) == (6, 2, 4) + @test ∂(3) == (6, 4, 3, 5) + @test ∂(4) == (3, 1, 5) + ∂ = Boundary{2,1}(t) + @test ∂(1) == (1, 3, 5, 6) + @test ∂(2) == (3, 9, 4) + @test ∂(3) == (4, 7, 8, 5) + @test ∂(4) == (2, 6, 8) + ∂ = Boundary{1,0}(t) + @test ∂(1) == (1, 2) + @test ∂(2) == (3, 1) + @test ∂(3) == (6, 2) + @test ∂(4) == (4, 6) + @test ∂(5) == (5, 6) + @test ∂(6) == (1, 5) + @test ∂(7) == (4, 3) + @test ∂(8) == (3, 5) + @test ∂(9) == (2, 4) + 𝒞 = Coboundary{0,1}(t) + @test 𝒞(1) == (1, 6, 2) + @test 𝒞(2) == (9, 3, 1) + @test 𝒞(3) == (2, 8, 7) + @test 𝒞(4) == (7, 4, 9) + @test 𝒞(5) == (5, 8, 6) + @test 𝒞(6) == (3, 4, 5) + 𝒞 = Coboundary{0,2}(t) + @test 𝒞(1) == (1, 4) + @test 𝒞(2) == (2, 1) + @test 𝒞(3) == (4, 3) + @test 𝒞(4) == (3, 2) + @test 𝒞(5) == (3, 4, 1) + @test 𝒞(6) == (2, 3, 1) + 𝒞 = Coboundary{1,2}(t) + @test 𝒞(1) == (1,) + @test 𝒞(2) == (4,) + @test 𝒞(3) == (2, 1) + @test 𝒞(4) == (2, 3) + @test 𝒞(5) == (3, 1) + @test 𝒞(6) == (4, 1) + @test 𝒞(7) == (3,) + @test 𝒞(8) == (3, 4) + @test 𝒞(9) == (2,) + 𝒜 = Adjacency{0}(t) + @test 𝒜(1) == (2, 5, 3) + @test 𝒜(2) == (4, 6, 1) + @test 𝒜(3) == (1, 5, 4) + @test 𝒜(4) == (3, 6, 2) + @test 𝒜(5) == (6, 3, 1) + @test 𝒜(6) == (2, 4, 5) - # 2 triangles + 2 quadrangles - elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) - t = HalfEdgeTopology(elems) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (2, 3, 4) - @test 𝒜(2) == (1, 3) - @test 𝒜(3) == (2, 4, 1) - @test 𝒜(4) == (1, 3) + # 2 triangles + 2 quadrangles + elems = connect.([(1, 2, 6, 5), (2, 4, 6), (4, 3, 5, 6), (1, 5, 3)]) + t = HalfEdgeTopology(elems) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (2, 3, 4) + @test 𝒜(2) == (1, 3) + @test 𝒜(3) == (2, 4, 1) + @test 𝒜(4) == (1, 3) - # 4 quadrangles in a grid - elems = connect.([(1, 2, 5, 4), (2, 3, 6, 5), (4, 5, 8, 7), (5, 6, 9, 8)]) - t = HalfEdgeTopology(elems) - 𝒜 = Adjacency{2}(t) - @test 𝒜(1) == (3, 2) - @test 𝒜(2) == (1, 4) - @test 𝒜(3) == (1, 4) - @test 𝒜(4) == (3, 2) + # 4 quadrangles in a grid + elems = connect.([(1, 2, 5, 4), (2, 3, 6, 5), (4, 5, 8, 7), (5, 6, 9, 8)]) + t = HalfEdgeTopology(elems) + 𝒜 = Adjacency{2}(t) + @test 𝒜(1) == (3, 2) + @test 𝒜(2) == (1, 4) + @test 𝒜(3) == (1, 4) + @test 𝒜(4) == (3, 2) - # invalid relations - elems = connect.([(1, 2, 3), (4, 3, 2)]) - t = HalfEdgeTopology(elems) - @test_throws AssertionError Boundary{3,0}(t) - @test_throws AssertionError Coboundary{0,3}(t) - @test_throws AssertionError Adjacency{3}(t) - @test_throws AssertionError Boundary{0,2}(t) - @test_throws AssertionError Coboundary{2,0}(t) - end + # invalid relations + elems = connect.([(1, 2, 3), (4, 3, 2)]) + t = HalfEdgeTopology(elems) + @test_throws AssertionError Boundary{3,0}(t) + @test_throws AssertionError Coboundary{0,3}(t) + @test_throws AssertionError Adjacency{3}(t) + @test_throws AssertionError Boundary{0,2}(t) + @test_throws AssertionError Coboundary{2,0}(t) +end - @testset "SimpleTopology" begin - elems = connect.([(1, 2, 3), (4, 3, 2)]) - t = SimpleTopology(elems) - ∂ = Boundary{2,0}(t) - @test ∂(1) == (1, 2, 3) - @test ∂(2) == (4, 3, 2) - end +@testitem "SimpleTopology" setup = [Setup] begin + elems = connect.([(1, 2, 3), (4, 3, 2)]) + t = SimpleTopology(elems) + ∂ = Boundary{2,0}(t) + @test ∂(1) == (1, 2, 3) + @test ∂(2) == (4, 3, 2) end diff --git a/test/trajecs.jl b/test/trajecs.jl index f6fb9641f..99f209d5f 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -1,33 +1,31 @@ -@testset "Trajectories" begin - @testset "CylindricalTrajectory" begin - s = Segment(cart(0, 0, 0), cart(0, 0, 1)) - c = [s(t) for t in range(T(0), stop=T(1), length=10)] - t = CylindricalTrajectory(c) - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test eltype(t) <: Cylinder - @test nelements(t) == 10 - @test radius(t) == T(1) * u"m" - @test topology(t) == GridTopology(10) +@testitem "CylindricalTrajectory" setup = [Setup] begin + s = Segment(cart(0, 0, 0), cart(0, 0, 1)) + c = [s(t) for t in range(T(0), stop=T(1), length=10)] + t = CylindricalTrajectory(c) + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test eltype(t) <: Cylinder + @test nelements(t) == 10 + @test radius(t) == T(1) * u"m" + @test topology(t) == GridTopology(10) - b = BezierCurve([cart(0, 0, 0), cart(3, 3, 0), cart(3, 0, 7)]) - c = [b(t) for t in range(T(0), stop=T(1), length=20)] - t = CylindricalTrajectory(c, T(2)) - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test eltype(t) <: Cylinder - @test nelements(t) == 20 - @test radius(t) == T(2) * u"m" - @test topology(t) == GridTopology(20) + b = BezierCurve([cart(0, 0, 0), cart(3, 3, 0), cart(3, 0, 7)]) + c = [b(t) for t in range(T(0), stop=T(1), length=20)] + t = CylindricalTrajectory(c, T(2)) + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test eltype(t) <: Cylinder + @test nelements(t) == 20 + @test radius(t) == T(2) * u"m" + @test topology(t) == GridTopology(20) - # trajectory with single cylinder - t = CylindricalTrajectory([cart(0, 0, 0)], T(1)) - @test crs(t) <: Cartesian{NoDatum} - @test Meshes.lentype(t) == ℳ - @test eltype(t) <: Cylinder - @test nelements(t) == 1 - @test radius(t) == T(1) * u"m" - @test topology(t) == GridTopology(1) - @test t[1] == Cylinder(cart(0, 0, -0.5), cart(0, 0, 0.5), T(1)) - end + # trajectory with single cylinder + t = CylindricalTrajectory([cart(0, 0, 0)], T(1)) + @test crs(t) <: Cartesian{NoDatum} + @test Meshes.lentype(t) == ℳ + @test eltype(t) <: Cylinder + @test nelements(t) == 1 + @test radius(t) == T(1) * u"m" + @test topology(t) == GridTopology(1) + @test t[1] == Cylinder(cart(0, 0, -0.5), cart(0, 0, 0.5), T(1)) end diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl index 4ccf01f4f..62ad809ff 100644 --- a/test/transformedgeoms.jl +++ b/test/transformedgeoms.jl @@ -1,4 +1,4 @@ -@testset "TransformedGeometry" begin +@testitem "TransformedGeometry" setup = [Setup] begin b = Box(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) tb = TransformedGeometry(b, t) diff --git a/test/transforms.jl b/test/transforms.jl index 6955caa44..17f1048d7 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1,1961 +1,1957 @@ -@testset "Transforms" begin - @testset "Rotate" begin - @test isaffine(Rotate) - @test TB.isrevertible(Rotate) - @test TB.isinvertible(Rotate) - @test TB.inverse(Rotate(Angle2d(T(π / 2)))) == Rotate(Angle2d(-T(π / 2))) - rot = Angle2d(T(π / 2)) - f = Rotate(rot) - @test TB.parameters(f) == (; rot) - - # ---- - # VEC - # ---- - - f = Rotate(Angle2d(T(π / 2))) - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r ≈ vector(0, 1) - @test TB.revert(f, r, c) ≈ v - - # ------ - # POINT - # ------ - - f = Rotate(Angle2d(T(π / 2))) - g = cart(1, 0) - r, c = TB.apply(f, g) - @test r ≈ cart(0, 1) - @test TB.revert(f, r, c) ≈ g - - # -------- - # SEGMENT - # -------- - - f = Rotate(Angle2d(T(π / 2))) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(0, 0), cart(0, 1)) - @test TB.revert(f, r, c) ≈ g - - # ---- - # BOX - # ---- - - f = Rotate(Angle2d(T(π / 2))) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Quadrangle(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) - @test TB.revert(f, r, c) ≈ g - - f = Rotate(vector(1, 0, 0), vector(0, 1, 0)) - g = Box(cart(0, 0, 0), cart(1, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Hexahedron( - cart(0, 0, 0), - cart(0, 1, 0), - cart(-1, 1, 0), - cart(-1, 0, 0), - cart(0, 0, 1), - cart(0, 1, 1), - cart(-1, 1, 1), - cart(-1, 0, 1) - ) - h = TB.revert(f, r, c) - @test h ≈ convert(Hexahedron, g) - - # ---------- - # ROPE/RING - # ---------- - - f = Rotate(Angle2d(T(π / 2))) - g = Rope(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Rope(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) - @test TB.revert(f, r, c) ≈ g - - f = Rotate(Angle2d(T(π / 2))) - g = Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Ring(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # TRIANGLE - # --------- - - f = Rotate(AngleAxis(T(π / 2), T(0), T(0), T(1))) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(-1, 0, 0)) - @test TB.revert(f, r, c) ≈ g - - f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(0, 0, 0), cart(0, 0, -1), cart(0, 1, 0)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # POLYAREA - # --------- - - f = Rotate(Angle2d(T(π / 2))) - p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - r, c = TB.apply(f, p) - @test r ≈ PolyArea(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) - @test TB.revert(f, r, c) ≈ p - - # ---------- - # MULTIGEOM - # ---------- - - f = Rotate(Angle2d(T(π / 2))) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ g - - # ------ - # PLANE - # ------ - - f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) - g = Plane(cart(0, 0, 0), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Plane(cart(0, 0, 0), vector(1, 0, 0)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # CYLINDER - # --------- - - f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) - g = Cylinder(T(1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(cart(0, 0, 0), cart(1, 0, 0)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # POINTSET - # --------- - - f = Rotate(Angle2d(T(π / 2))) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([cart(0, 0), cart(0, 1), cart(-1, 1)]) - @test TB.revert(f, r, c) ≈ d - - # ------------ - # GEOMETRYSET - # ------------ - - f = Rotate(Angle2d(T(π / 2))) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ d - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - @test all(TB.revert(f, r, c) .≈ d) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Rotate(Angle2d(T(π / 2))) - d = cartgrid(10, 10) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Rotate(Angle2d(T(π / 2))) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Rotate(Angle2d(T(π / 2))) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ----------- - # SIMPLEMESH - # ----------- - - f = Rotate(Angle2d(T(π / 2))) - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # --------- - # FALLBACK - # --------- - - f = Rotate(T(π / 2)) - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r ≈ vector(0, 1) - @test TB.revert(f, r, c) ≈ v - - # CRS propagation - f = Rotate(Angle2d(T(π / 2))) - p = merc(1, 0) - @test crs(f(p)) === crs(p) - end - - @testset "Translate" begin - @test isaffine(Translate) - @test TB.isrevertible(Translate) - @test TB.isinvertible(Translate) - @test TB.inverse(Translate(T(1), T(2))) == Translate(T(-1), T(-2)) - offsets = (T(1) * u"m", T(2) * u"m") - f = Translate(offsets) - @test TB.parameters(f) == (; offsets) - f = Translate(T(1), T(2)) - @test TB.parameters(f) == (; offsets) - f = Translate(T(1), 2) - @test TB.parameters(f) == (; offsets) - f = Translate(1, 2) - @test TB.parameters(f) == (; offsets) - - # ---- - # VEC - # ---- - - f = Translate(T(1), T(1)) - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r ≈ vector(1, 0) - @test TB.revert(f, r, c) ≈ v - - # ------ - # POINT - # ------ - - f = Translate(T(1), T(1)) - g = cart(1, 0) - r, c = TB.apply(f, g) - @test r ≈ cart(2, 1) - @test TB.revert(f, r, c) ≈ g - - # -------- - # SEGMENT - # -------- - - f = Translate(T(1), T(1)) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(1, 1), cart(2, 1)) - @test TB.revert(f, r, c) ≈ g - - # ---- - # BOX - # ---- - - f = Translate(T(1), T(1)) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r isa Box - @test r ≈ Box(cart(1, 1), cart(2, 2)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # TRIANGLE - # --------- - - f = Translate(T(1), T(2), T(3)) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(1, 2, 3), cart(2, 2, 3), cart(1, 3, 4)) - @test TB.revert(f, r, c) ≈ g - - # ---------- - # MULTIGEOM - # ---------- - - f = Translate(T(1), T(1)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ g - - # ------ - # PLANE - # ------ - - f = Translate(T(0), T(0), T(1)) - g = Plane(cart(0, 0, 0), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Plane(cart(0, 0, 1), vector(0, 0, 1)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # CYLINDER - # --------- - - f = Translate(T(0), T(0), T(1)) - g = Cylinder(T(1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(cart(0, 0, 1), cart(0, 0, 2)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # POINTSET - # --------- - - f = Translate(T(1), T(1)) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([cart(1, 1), cart(2, 1), cart(2, 2)]) - @test TB.revert(f, r, c) ≈ d - - # ------------ - # GEOMETRYSET - # ------------ - - f = Translate(T(1), T(1)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ d - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - @test all(TB.revert(f, r, c) .≈ d) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Translate(T(1), T(1)) - d = cartgrid(10, 10) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) - @test TB.revert(f, r, c) ≈ d - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Translate(T(1), T(1)) - d = RectilinearGrid(T.(0:10), T.(0:10)) - r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r ≈ RectilinearGrid(T.(1:11), T.(1:11)) - @test TB.revert(f, r, c) ≈ d - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Translate(T(1), T(1)) - d = StructuredGrid(repeat(T.(0:10), 1, 11), repeat(T.(0:10)', 11, 1)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r ≈ StructuredGrid(repeat(T.(1:11), 1, 11), repeat(T.(1:11)', 11, 1)) - @test TB.revert(f, r, c) ≈ d - - # ----------- - # SIMPLEMESH - # ----------- - - f = Translate(T(1), T(1)) - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - end - - @testset "Affine" begin - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - @test isaffine(f) - @test TB.isrevertible(f) - @test TB.isinvertible(f) - @test TB.inverse(f) == Affine(Angle2d(-T(π / 2)), Angle2d(-T(π / 2)) * -T[1, 1]) - f = Affine(T[6 3; 10 5], T[1, 1]) - @test !TB.isrevertible(f) - @test !TB.isinvertible(f) - A, b = Angle2d(T(π / 2)), SVector(T(1) * u"m", T(1) * u"m") - f = Affine(A, b) - @test TB.parameters(f) == (; A, b) - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - @test TB.parameters(f) == (; A, b) - f = Affine(Angle2d(T(π / 2)), [1, 1]) - @test TB.parameters(f) == (; A, b) - - # ---- - # VEC - # ---- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r ≈ vector(0, 1) - @test TB.revert(f, r, c) ≈ v - - # ------ - # POINT - # ------ - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = cart(1, 0) - r, c = TB.apply(f, g) - @test r ≈ cart(1, 2) - @test TB.revert(f, r, c) ≈ g - - # -------- - # SEGMENT - # -------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(1, 1), cart(1, 2)) - @test TB.revert(f, r, c) ≈ g - - # ---- - # BOX - # ---- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Quadrangle(cart(1, 1), cart(1, 2), cart(0, 2), cart(0, 1)) - @test TB.revert(f, r, c) ≈ g - - f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) - g = Box(cart(0, 0, 0), cart(1, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Hexahedron( - cart(1, 2, 3), - cart(1, 2, 2), - cart(1, 3, 2), - cart(1, 3, 3), - cart(2, 2, 3), - cart(2, 2, 2), - cart(2, 3, 2), - cart(2, 3, 3) - ) - h = TB.revert(f, r, c) - @test h ≈ convert(Hexahedron, g) - - # --------- - # TRIANGLE - # --------- - - f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(1, 2, 3), cart(1, 2, 2), cart(2, 3, 3)) - @test TB.revert(f, r, c) ≈ g - - # ---------- - # MULTIGEOM - # ---------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ g - - # ------ - # PLANE - # ------ - - f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) - g = Plane(cart(0, 0, 0), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Plane(cart(0, 0, 1), vector(1, 0, 0)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # CYLINDER - # --------- - - f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) - g = Cylinder(T(1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(cart(0, 0, 1), cart(1, 0, 1)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # POINTSET - # --------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([cart(1, 1), cart(1, 2), cart(0, 2)]) - @test TB.revert(f, r, c) ≈ d - - # ------------ - # GEOMETRYSET - # ------------ - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ d - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - @test all(TB.revert(f, r, c) .≈ d) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = cartgrid(10, 10) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ---------- - # TRANSFORM - # ---------- - - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - s = Rotate(T(π / 2)) → Translate(T(1), T(1)) - v = vector(1, 0) - g1 = cart(1, 0) - g2 = Segment(cart(0, 0), cart(1, 0)) - g3 = Box(cart(0, 0), cart(1, 1)) - @test f(v) ≈ s(v) - @test f(g1) ≈ s(g1) - @test f(g2) ≈ s(g2) - @test f(g3) ≈ s(g3) - - # ------------ - # CONSTRUCTOR - # ------------ - - # conversion to SArray - f = Affine(T[0 -1; 1 0], SVector{2}(T[1, 1])) - @test f.A isa SMatrix - f = Affine(SMatrix{2,2}(T[0 -1; 1 0]), T[1, 1]) - @test f.b isa SVector - f = Affine(T[0 -1; 1 0], T[1, 1]) - @test f.A isa SMatrix - @test f.b isa SVector - - # CRS propagation - f = Affine(Angle2d(T(π / 2)), T[1, 1]) - p = merc(1, 0) - @test crs(f(p)) === crs(p) - - # error: A must be a square matrix - @test_throws ArgumentError Affine(T[1 1; 2 2; 3 3], T[1, 2]) - # error: A and b must have the same dimension - @test_throws ArgumentError Affine(T[1 1; 2 2], T[1, 2, 3]) - end - - @testset "Scale" begin - @test isaffine(Scale) - @test TB.isrevertible(Scale) - @test TB.isinvertible(Scale) - @test TB.inverse(Scale(T(1), T(2))) == Scale(T(1), T(1 / 2)) - factors = (T(1), T(2)) - f = Scale(factors) - @test TB.parameters(f) == (; factors) - - # ---- - # VEC - # ---- - - f = Scale(T(1), T(2)) - v = vector(1, 1) - r, c = TB.apply(f, v) - @test r ≈ vector(1, 2) - @test TB.revert(f, r, c) ≈ v - - # ------ - # POINT - # ------ - - f = Scale(T(1), T(2)) - g = cart(1, 1) - r, c = TB.apply(f, g) - @test r ≈ cart(1, 2) - @test TB.revert(f, r, c) ≈ g - - # -------- - # SEGMENT - # -------- - - f = Scale(T(1), T(2)) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(0, 0), cart(1, 0)) - @test TB.revert(f, r, c) ≈ g - - f = Scale(T(2), T(1)) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(0, 0), cart(2, 0)) - @test TB.revert(f, r, c) ≈ g - - # ---- - # BOX - # ---- - - f = Scale(T(1), T(2)) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r isa Box - @test r ≈ Box(cart(0, 0), cart(1, 2)) - @test TB.revert(f, r, c) ≈ g - - # ----- - # BALL - # ----- - - f = Scale(T(1), T(2)) - g = Ball(cart(1, 2), T(3)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ------- - # SPHERE - # ------- - - f = Scale(T(1), T(2)) - g = Sphere(cart(1, 2), T(3)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - f = Scale(T(1), T(2), T(3)) - g = Sphere(cart(1, 2, 3), T(4)) - r, c = TB.apply(f, g) - @test r isa Ellipsoid - @test r ≈ Ellipsoid(T.((4, 8, 12)), cart(1, 4, 9)) - @test discretize(TB.revert(f, r, c)) ≈ discretize(g) - - f = Scale(T(2)) - g = Sphere(cart(1, 2), T(3)) - r, c = TB.apply(f, g) - @test r isa Sphere - @test r ≈ Sphere(cart(2, 4), T(6)) - @test TB.revert(f, r, c) ≈ g - - f = Scale(T(2)) - g = Sphere(cart(1, 2, 3), T(4)) - r, c = TB.apply(f, g) - @test r isa Sphere - @test r ≈ Sphere(cart(2, 4, 6), T(8)) - @test TB.revert(f, r, c) ≈ g - - # ---------- - # ELLIPSOID - # ---------- - - f = Scale(T(1), T(2), T(3)) - g = Ellipsoid(T.((1, 2, 3))) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ----- - # DISK - # ----- - - f = Scale(T(1), T(2), T(3)) - g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ------- - # CIRCLE - # ------- - - f = Scale(T(1), T(2), T(3)) - g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ---------------- - # CYLINDERSURFACE - # ---------------- - - f = Scale(T(1), T(2), T(3)) - g = CylinderSurface(T(1)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ------------------ - # PARABOLOIDSURFACE - # ------------------ - - f = Scale(T(1), T(2), T(3)) - g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test discretize(TB.revert(f, r, c)) ≈ m - - # ------ - # TORUS - # ------ - - f = Scale(T(1), T(2), T(3)) - g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - @test centroid(r) ≈ f(centroid(g)) - @test discretize(TB.revert(f, r, c)) ≈ m - - # --------- - # TRIANGLE - # --------- - - f = Scale(T(1), T(2), T(3)) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 2, 3)) - @test TB.revert(f, r, c) ≈ g - - # ---------- - # MULTIGEOM - # ---------- - - f = Scale(T(1), T(2)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ g - - # ------ - # PLANE - # ------ - - f = Scale(T(1), T(1), T(2)) - g = Plane(cart(1, 1, 1), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ Plane(cart(1, 1, 2), vector(0, 0, 1)) - @test TB.revert(f, r, c) ≈ g - - f = Scale(T(2), T(1), T(1)) - g = Plane(cart(1, 1, 1), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ g - @test TB.revert(f, r, c) ≈ g - - # --------- - # POINTSET - # --------- - - f = Scale(T(1), T(2)) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([cart(0, 0), cart(1, 0), cart(1, 2)]) - @test TB.revert(f, r, c) ≈ d - - # ------------ - # GEOMETRYSET - # ------------ - - f = Scale(T(1), T(2)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ d - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - @test all(TB.revert(f, r, c) .≈ d) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Scale(T(1), T(2)) - d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid(cart(1, 2), cart(11, 22), dims=(10, 10)) - @test TB.revert(f, r, c) ≈ d - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Scale(T(1), T(2)) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Scale(T(1), T(2)) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ----------- - # SIMPLEMESH - # ----------- - - f = Scale(T(1), T(2)) - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - end - - @testset "Stretch" begin - @test !isaffine(Stretch) - @test TB.isrevertible(Stretch) - @test TB.isinvertible(Stretch) - @test TB.inverse(Stretch(T(1), T(2))) == Stretch(T(1), T(1 / 2)) - factors = (T(1), T(2)) - f = Stretch(factors) - @test TB.parameters(f) == (; factors) - - # ---- - # VEC - # ---- - - f = Stretch(T(1), T(2)) - v = vector(1, 1) - r, c = TB.apply(f, v) - @test r ≈ vector(1, 2) - @test TB.revert(f, r, c) ≈ v - - # ------ - # POINT - # ------ - - f = Stretch(T(1), T(2)) - g = cart(1, 1) - r, c = TB.apply(f, g) - @test r ≈ cart(1, 1) - @test TB.revert(f, r, c) ≈ g - - # -------- - # SEGMENT - # -------- - - f = Stretch(T(1), T(2)) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(0, 0), cart(1, 0)) - @test TB.revert(f, r, c) ≈ g - - f = Stretch(T(2), T(1)) - g = Segment(cart(0, 0), cart(1, 0)) - r, c = TB.apply(f, g) - @test r ≈ Segment(cart(-0.5, 0), cart(1.5, 0)) - @test TB.revert(f, r, c) ≈ g - - # ---- - # BOX - # ---- - - f = Stretch(T(1), T(2)) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r isa Box - @test r ≈ Box(cart(0, -0.5), cart(1, 1.5)) - @test TB.revert(f, r, c) ≈ g - - # --------- - # TRIANGLE - # --------- - - f = Stretch(T(1), T(2), T(2)) - g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(cart(0, -1 / 3, -1 / 3), cart(1, -1 / 3, -1 / 3), cart(0, 10 / 6, 10 / 6)) - @test TB.revert(f, r, c) ≈ g - - # ---------- - # MULTIGEOM - # ---------- - - f = Stretch(T(1), T(2)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ g - - # ------ - # PLANE - # ------ - - f = Stretch(T(1), T(1), T(2)) - g = Plane(cart(1, 1, 1), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ g - @test TB.revert(f, r, c) ≈ g - - f = Stretch(T(2), T(1), T(1)) - g = Plane(cart(1, 1, 1), vector(0, 0, 1)) - r, c = TB.apply(f, g) - @test r ≈ g - @test TB.revert(f, r, c) ≈ g - - # --------- - # POINTSET - # --------- - - f = Stretch(T(1), T(2)) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([cart(0, -1 / 3), cart(1, -1 / 3), cart(1, 10 / 6)]) - @test TB.revert(f, r, c) ≈ d - - # ------------ - # GEOMETRYSET - # ------------ - - f = Stretch(T(1), T(2)) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - @test TB.revert(f, r, c) ≈ d - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - @test all(TB.revert(f, r, c) .≈ d) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Stretch(T(1), T(2)) - d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid(cart(1, -4), cart(11, 16), dims=(10, 10)) - @test TB.revert(f, r, c) ≈ d - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Stretch(T(1), T(2)) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Stretch(T(1), T(2)) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - - # ----------- - # SIMPLEMESH - # ----------- - - f = Stretch(T(1), T(2)) - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) - @test TB.revert(f, r, c) ≈ d - end - - @testset "StdCoords" begin - @test !isaffine(StdCoords) - @test TB.isrevertible(StdCoords) - - # --------- - # POINTSET - # --------- - - f = StdCoords() - d = view(PointSet(randpoint2(100)), 1:50) - r, c = TB.apply(f, d) - @test all(sides(boundingbox(r)) .≤ oneunit(ℳ)) - @test TB.revert(f, r, c) ≈ d - r2 = TB.reapply(f, d, c) - @test r == r2 - - # -------------- - # CARTESIANGRID - # -------------- - - f = StdCoords() - d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 10)) - @test TB.revert(f, r, c) ≈ d - - f = StdCoords() - d = cartgrid(10, 20) - r, c = TB.apply(f, d) - @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 20)) - @test TB.revert(f, r, c) ≈ d - r2 = TB.reapply(f, d, c) - @test r == r2 - end - - @testset "Proj" begin - @test !isaffine(Proj(Polar)) - @test !TB.isrevertible(Proj(Polar)) - @test !TB.isinvertible(Proj(Polar)) - @test TB.parameters(Proj(Polar)) == (; CRS=Polar) - @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) - @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) - f = Proj(Mercator) - @test sprint(show, f) == "Proj(CRS: Mercator)" - @test sprint(show, MIME"text/plain"(), f) == """ - Proj transform - └─ CRS: Mercator""" - - # ---- - # VEC - # ---- - - f = Proj(Polar) - v = vector(1, 0) - r, c = TB.apply(f, v) - @test r == v - - # ------ - # POINT - # ------ - - f = Proj(Polar) - g = cart(1, 1) - r, c = TB.apply(f, g) - @test r ≈ Point(Polar(T(√2), T(π / 4))) - - # change the manifold - f = Proj(Mercator) - g = latlon(0, 0) - r, c = TB.apply(f, g) - @test manifold(r) === 𝔼{2} - @test r ≈ merc(0, 0) - - # preserve the manifold - f = Proj(Cartesian) - g = latlon(0, 0) - r, c = TB.apply(f, g) - @test manifold(r) === 🌐 - @test r ≈ cart(6378137.0, 0, 0) - - # -------- - # SEGMENT - # -------- - - f = Proj(Polar) - g = Segment(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Segment(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) - - # ---- - # BOX - # ---- - - f = Proj(Polar) - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Box(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) - - # --------- - # TRIANGLE - # --------- - - f = Proj(Polar) - g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))) - - # ---------- - # MULTIGEOM - # ---------- - - f = Proj(Polar) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - - # --------- - # CYLINDER - # --------- - - f = Proj(Cylindrical) - g = Cylinder(cart(0, 0, 0), cart(1, 1, 1)) - r, c = TB.apply(f, g) - @test r ≈ Cylinder(Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(√2), T(π / 4), T(1)))) - - # --------- - # POINTSET - # --------- - - f = Proj(Polar) - d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))]) - - # ------------ - # GEOMETRYSET - # ------------ - - f = Proj(Polar) - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Proj(Polar) - d = CartesianGrid((10, 10), cart(1, 1), T.((1, 1))) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid((10, 10), Point(Polar(T(√2), T(π / 4))), T.((1, 1))) - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Proj(Polar) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Proj(Polar) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # ----------- - # SIMPLEMESH - # ----------- - - f = Proj(Polar) - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # -------------- - # SPECIAL CASES - # -------------- - - f = Proj(Mercator) - g = Box(latlon(0, 180), latlon(45, 90)) - r, c = TB.apply(f, g) - @test manifold(r) === 𝔼{2} - - f = Proj(LatLon) - g = Box(merc(0, 0), merc(1, 1)) - r, c = TB.apply(f, g) - @test manifold(r) === 🌐 - end - - @testset "LengthUnit" begin - @test !isaffine(LengthUnit(u"km")) - @test !TB.isrevertible(LengthUnit(u"cm")) - @test !TB.isinvertible(LengthUnit(u"km")) - @test TB.parameters(LengthUnit(u"cm")) == (; unit=u"cm") - - # ---- - # VEC - # ---- - - f = LengthUnit(u"km") - v = vector(1000, 0) - r, c = TB.apply(f, v) - @test r ≈ Vec(T(1) * u"km", T(0) * u"km") - - # ------ - # POINT - # ------ - - f = LengthUnit(u"cm") - g = cart(1, 1) - r, c = TB.apply(f, g) - @test r ≈ Point(T(100) * u"cm", T(100) * u"cm") - - f = LengthUnit(u"km") - g = Point(Polar(T(1000), T(π / 4))) - r, c = TB.apply(f, g) - @test r ≈ Point(Polar(T(1) * u"km", T(π / 4) * u"rad")) - - f = LengthUnit(u"cm") - g = Point(Cylindrical(T(1), T(π / 4), T(1))) - r, c = TB.apply(f, g) - @test r ≈ Point(Cylindrical(T(100) * u"cm", T(π / 4) * u"rad", T(100) * u"cm")) - - f = LengthUnit(u"km") - g = Point(Spherical(T(1000), T(π / 4), T(π / 4))) - r, c = TB.apply(f, g) - @test r ≈ Point(Spherical(T(1) * u"km", T(π / 4) * u"rad", T(π / 4) * u"rad")) - - f = LengthUnit(u"cm") - g = Point(Mercator(T(1), T(1))) - @test_throws ArgumentError TB.apply(f, g) - - # -------- - # SEGMENT - # -------- - - f = LengthUnit(u"km") - g = Segment(cart(0, 0), cart(1000, 1000)) - r, c = TB.apply(f, g) - @test r ≈ Segment(Point(T(0) * u"km", T(0) * u"km"), Point(T(1) * u"km", T(1) * u"km")) - - # ---- - # BOX - # ---- - - f = LengthUnit(u"cm") - g = Box(cart(0, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r isa Box - @test r ≈ Box(Point(T(0) * u"cm", T(0) * u"cm"), Point(T(100) * u"cm", T(100) * u"cm")) - - # ------- - # SPHERE - # ------- - - f = LengthUnit(u"km") - g = Sphere(cart(0, 0), T(1000)) - r, c = TB.apply(f, g) - @test r isa Sphere - @test r ≈ Sphere(Point(T(0) * u"km", T(0) * u"km"), T(1) * u"km") - - # ---------- - # ELLIPSOID - # ---------- - - f = LengthUnit(u"cm") - g = Ellipsoid(T.((1, 1, 1)), cart(0, 0, 0)) - r, c = TB.apply(f, g) - @test r isa Ellipsoid - @test r ≈ - Ellipsoid((T(100) * u"cm", T(100) * u"cm", T(100) * u"cm"), Point(T(0) * u"cm", T(0) * u"cm", T(0) * u"cm")) - - # --------- - # TRIANGLE - # --------- - - f = LengthUnit(u"km") - g = Triangle(cart(0, 0), cart(1000, 0), cart(1000, 1000)) - r, c = TB.apply(f, g) - @test r ≈ Triangle( - Point(T(0) * u"km", T(0) * u"km"), - Point(T(1) * u"km", T(0) * u"km"), - Point(T(1) * u"km", T(1) * u"km") - ) - - # ---------- - # MULTIGEOM - # ---------- - - f = LengthUnit(u"cm") - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r ≈ Multi([f(t), f(t)]) - - # --------- - # POINTSET - # --------- - - f = LengthUnit(u"km") - d = PointSet([cart(0, 0), cart(1000, 0), cart(1000, 1000)]) - r, c = TB.apply(f, d) - @test r ≈ PointSet([ - Point(T(0) * u"km", T(0) * u"km"), - Point(T(1) * u"km", T(0) * u"km"), - Point(T(1) * u"km", T(1) * u"km") - ]) +@testitem "Rotate" setup = [Setup] begin + @test isaffine(Rotate) + @test TB.isrevertible(Rotate) + @test TB.isinvertible(Rotate) + @test TB.inverse(Rotate(Angle2d(T(π / 2)))) == Rotate(Angle2d(-T(π / 2))) + rot = Angle2d(T(π / 2)) + f = Rotate(rot) + @test TB.parameters(f) == (; rot) + + # ---- + # VEC + # ---- + + f = Rotate(Angle2d(T(π / 2))) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r ≈ vector(0, 1) + @test TB.revert(f, r, c) ≈ v + + # ------ + # POINT + # ------ + + f = Rotate(Angle2d(T(π / 2))) + g = cart(1, 0) + r, c = TB.apply(f, g) + @test r ≈ cart(0, 1) + @test TB.revert(f, r, c) ≈ g + + # -------- + # SEGMENT + # -------- + + f = Rotate(Angle2d(T(π / 2))) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(0, 0), cart(0, 1)) + @test TB.revert(f, r, c) ≈ g + + # ---- + # BOX + # ---- + + f = Rotate(Angle2d(T(π / 2))) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Quadrangle(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) + @test TB.revert(f, r, c) ≈ g + + f = Rotate(vector(1, 0, 0), vector(0, 1, 0)) + g = Box(cart(0, 0, 0), cart(1, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Hexahedron( + cart(0, 0, 0), + cart(0, 1, 0), + cart(-1, 1, 0), + cart(-1, 0, 0), + cart(0, 0, 1), + cart(0, 1, 1), + cart(-1, 1, 1), + cart(-1, 0, 1) + ) + h = TB.revert(f, r, c) + @test h ≈ convert(Hexahedron, g) + + # ---------- + # ROPE/RING + # ---------- + + f = Rotate(Angle2d(T(π / 2))) + g = Rope(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Rope(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) + @test TB.revert(f, r, c) ≈ g + + f = Rotate(Angle2d(T(π / 2))) + g = Ring(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Ring(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # TRIANGLE + # --------- + + f = Rotate(AngleAxis(T(π / 2), T(0), T(0), T(1))) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(0, 0, 0), cart(0, 1, 0), cart(-1, 0, 0)) + @test TB.revert(f, r, c) ≈ g + + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(0, 0, 0), cart(0, 0, -1), cart(0, 1, 0)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # POLYAREA + # --------- + + f = Rotate(Angle2d(T(π / 2))) + p = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + r, c = TB.apply(f, p) + @test r ≈ PolyArea(cart(0, 0), cart(0, 1), cart(-1, 1), cart(-1, 0)) + @test TB.revert(f, r, c) ≈ p + + # ---------- + # MULTIGEOM + # ---------- + + f = Rotate(Angle2d(T(π / 2))) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ g + + # ------ + # PLANE + # ------ + + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Plane(cart(0, 0, 0), vector(1, 0, 0)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # CYLINDER + # --------- + + f = Rotate(vector(0, 0, 1), vector(1, 0, 0)) + g = Cylinder(T(1)) + r, c = TB.apply(f, g) + @test r ≈ Cylinder(cart(0, 0, 0), cart(1, 0, 0)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # POINTSET + # --------- + + f = Rotate(Angle2d(T(π / 2))) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(0, 0), cart(0, 1), cart(-1, 1)]) + @test TB.revert(f, r, c) ≈ d + + # ------------ + # GEOMETRYSET + # ------------ + + f = Rotate(Angle2d(T(π / 2))) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ d + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + @test all(TB.revert(f, r, c) .≈ d) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Rotate(Angle2d(T(π / 2))) + d = cartgrid(10, 10) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Rotate(Angle2d(T(π / 2))) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Rotate(Angle2d(T(π / 2))) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ----------- + # SIMPLEMESH + # ----------- + + f = Rotate(Angle2d(T(π / 2))) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------- + # FALLBACK + # --------- + + f = Rotate(T(π / 2)) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r ≈ vector(0, 1) + @test TB.revert(f, r, c) ≈ v + + # CRS propagation + f = Rotate(Angle2d(T(π / 2))) + p = merc(1, 0) + @test crs(f(p)) === crs(p) +end + +@testitem "Translate" setup = [Setup] begin + @test isaffine(Translate) + @test TB.isrevertible(Translate) + @test TB.isinvertible(Translate) + @test TB.inverse(Translate(T(1), T(2))) == Translate(T(-1), T(-2)) + offsets = (T(1) * u"m", T(2) * u"m") + f = Translate(offsets) + @test TB.parameters(f) == (; offsets) + f = Translate(T(1), T(2)) + @test TB.parameters(f) == (; offsets) + f = Translate(T(1), 2) + @test TB.parameters(f) == (; offsets) + f = Translate(1, 2) + @test TB.parameters(f) == (; offsets) + + # ---- + # VEC + # ---- + + f = Translate(T(1), T(1)) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r ≈ vector(1, 0) + @test TB.revert(f, r, c) ≈ v + + # ------ + # POINT + # ------ + + f = Translate(T(1), T(1)) + g = cart(1, 0) + r, c = TB.apply(f, g) + @test r ≈ cart(2, 1) + @test TB.revert(f, r, c) ≈ g + + # -------- + # SEGMENT + # -------- + + f = Translate(T(1), T(1)) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(1, 1), cart(2, 1)) + @test TB.revert(f, r, c) ≈ g + + # ---- + # BOX + # ---- + + f = Translate(T(1), T(1)) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(cart(1, 1), cart(2, 2)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # TRIANGLE + # --------- + + f = Translate(T(1), T(2), T(3)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(1, 2, 3), cart(2, 2, 3), cart(1, 3, 4)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # MULTIGEOM + # ---------- + + f = Translate(T(1), T(1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ g + + # ------ + # PLANE + # ------ + + f = Translate(T(0), T(0), T(1)) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Plane(cart(0, 0, 1), vector(0, 0, 1)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # CYLINDER + # --------- + + f = Translate(T(0), T(0), T(1)) + g = Cylinder(T(1)) + r, c = TB.apply(f, g) + @test r ≈ Cylinder(cart(0, 0, 1), cart(0, 0, 2)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # POINTSET + # --------- + + f = Translate(T(1), T(1)) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(1, 1), cart(2, 1), cart(2, 2)]) + @test TB.revert(f, r, c) ≈ d + + # ------------ + # GEOMETRYSET + # ------------ + + f = Translate(T(1), T(1)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ d + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + @test all(TB.revert(f, r, c) .≈ d) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Translate(T(1), T(1)) + d = cartgrid(10, 10) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Translate(T(1), T(1)) + d = RectilinearGrid(T.(0:10), T.(0:10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ RectilinearGrid(T.(1:11), T.(1:11)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Translate(T(1), T(1)) + d = StructuredGrid(repeat(T.(0:10), 1, 11), repeat(T.(0:10)', 11, 1)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ StructuredGrid(repeat(T.(1:11), 1, 11), repeat(T.(1:11)', 11, 1)) + @test TB.revert(f, r, c) ≈ d + + # ----------- + # SIMPLEMESH + # ----------- + + f = Translate(T(1), T(1)) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d +end + +@testitem "Affine" setup = [Setup] begin + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + @test isaffine(f) + @test TB.isrevertible(f) + @test TB.isinvertible(f) + @test TB.inverse(f) == Affine(Angle2d(-T(π / 2)), Angle2d(-T(π / 2)) * -T[1, 1]) + f = Affine(T[6 3; 10 5], T[1, 1]) + @test !TB.isrevertible(f) + @test !TB.isinvertible(f) + A, b = Angle2d(T(π / 2)), SVector(T(1) * u"m", T(1) * u"m") + f = Affine(A, b) + @test TB.parameters(f) == (; A, b) + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + @test TB.parameters(f) == (; A, b) + f = Affine(Angle2d(T(π / 2)), [1, 1]) + @test TB.parameters(f) == (; A, b) + + # ---- + # VEC + # ---- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r ≈ vector(0, 1) + @test TB.revert(f, r, c) ≈ v + + # ------ + # POINT + # ------ + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + g = cart(1, 0) + r, c = TB.apply(f, g) + @test r ≈ cart(1, 2) + @test TB.revert(f, r, c) ≈ g + + # -------- + # SEGMENT + # -------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(1, 1), cart(1, 2)) + @test TB.revert(f, r, c) ≈ g + + # ---- + # BOX + # ---- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Quadrangle(cart(1, 1), cart(1, 2), cart(0, 2), cart(0, 1)) + @test TB.revert(f, r, c) ≈ g + + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) + g = Box(cart(0, 0, 0), cart(1, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Hexahedron( + cart(1, 2, 3), + cart(1, 2, 2), + cart(1, 3, 2), + cart(1, 3, 3), + cart(2, 2, 3), + cart(2, 2, 2), + cart(2, 3, 2), + cart(2, 3, 3) + ) + h = TB.revert(f, r, c) + @test h ≈ convert(Hexahedron, g) + + # --------- + # TRIANGLE + # --------- + + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[1, 2, 3]) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(1, 2, 3), cart(1, 2, 2), cart(2, 3, 3)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # MULTIGEOM + # ---------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ g + + # ------ + # PLANE + # ------ + + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Plane(cart(0, 0, 1), vector(1, 0, 0)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # CYLINDER + # --------- + + f = Affine(rotation_between(SVector{3,T}(0, 0, 1), SVector{3,T}(1, 0, 0)), T[0, 0, 1]) + g = Cylinder(T(1)) + r, c = TB.apply(f, g) + @test r ≈ Cylinder(cart(0, 0, 1), cart(1, 0, 1)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # POINTSET + # --------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(1, 1), cart(1, 2), cart(0, 2)]) + @test TB.revert(f, r, c) ≈ d + + # ------------ + # GEOMETRYSET + # ------------ + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ d + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + @test all(TB.revert(f, r, c) .≈ d) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = cartgrid(10, 10) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ---------- + # TRANSFORM + # ---------- + + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + s = Rotate(T(π / 2)) → Translate(T(1), T(1)) + v = vector(1, 0) + g1 = cart(1, 0) + g2 = Segment(cart(0, 0), cart(1, 0)) + g3 = Box(cart(0, 0), cart(1, 1)) + @test f(v) ≈ s(v) + @test f(g1) ≈ s(g1) + @test f(g2) ≈ s(g2) + @test f(g3) ≈ s(g3) + + # ------------ + # CONSTRUCTOR + # ------------ + + # conversion to SArray + f = Affine(T[0 -1; 1 0], SVector{2}(T[1, 1])) + @test f.A isa SMatrix + f = Affine(SMatrix{2,2}(T[0 -1; 1 0]), T[1, 1]) + @test f.b isa SVector + f = Affine(T[0 -1; 1 0], T[1, 1]) + @test f.A isa SMatrix + @test f.b isa SVector + + # CRS propagation + f = Affine(Angle2d(T(π / 2)), T[1, 1]) + p = merc(1, 0) + @test crs(f(p)) === crs(p) + + # error: A must be a square matrix + @test_throws ArgumentError Affine(T[1 1; 2 2; 3 3], T[1, 2]) + # error: A and b must have the same dimension + @test_throws ArgumentError Affine(T[1 1; 2 2], T[1, 2, 3]) +end + +@testitem "Scale" setup = [Setup] begin + @test isaffine(Scale) + @test TB.isrevertible(Scale) + @test TB.isinvertible(Scale) + @test TB.inverse(Scale(T(1), T(2))) == Scale(T(1), T(1 / 2)) + factors = (T(1), T(2)) + f = Scale(factors) + @test TB.parameters(f) == (; factors) + + # ---- + # VEC + # ---- + + f = Scale(T(1), T(2)) + v = vector(1, 1) + r, c = TB.apply(f, v) + @test r ≈ vector(1, 2) + @test TB.revert(f, r, c) ≈ v + + # ------ + # POINT + # ------ + + f = Scale(T(1), T(2)) + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r ≈ cart(1, 2) + @test TB.revert(f, r, c) ≈ g + + # -------- + # SEGMENT + # -------- + + f = Scale(T(1), T(2)) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(0, 0), cart(1, 0)) + @test TB.revert(f, r, c) ≈ g + + f = Scale(T(2), T(1)) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(0, 0), cart(2, 0)) + @test TB.revert(f, r, c) ≈ g + + # ---- + # BOX + # ---- + + f = Scale(T(1), T(2)) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(cart(0, 0), cart(1, 2)) + @test TB.revert(f, r, c) ≈ g + + # ----- + # BALL + # ----- + + f = Scale(T(1), T(2)) + g = Ball(cart(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------- + # SPHERE + # ------- + + f = Scale(T(1), T(2)) + g = Sphere(cart(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + f = Scale(T(1), T(2), T(3)) + g = Sphere(cart(1, 2, 3), T(4)) + r, c = TB.apply(f, g) + @test r isa Ellipsoid + @test r ≈ Ellipsoid(T.((4, 8, 12)), cart(1, 4, 9)) + @test discretize(TB.revert(f, r, c)) ≈ discretize(g) + + f = Scale(T(2)) + g = Sphere(cart(1, 2), T(3)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(cart(2, 4), T(6)) + @test TB.revert(f, r, c) ≈ g + + f = Scale(T(2)) + g = Sphere(cart(1, 2, 3), T(4)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(cart(2, 4, 6), T(8)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # ELLIPSOID + # ---------- + + f = Scale(T(1), T(2), T(3)) + g = Ellipsoid(T.((1, 2, 3))) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ----- + # DISK + # ----- + + f = Scale(T(1), T(2), T(3)) + g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------- + # CIRCLE + # ------- + + f = Scale(T(1), T(2), T(3)) + g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ---------------- + # CYLINDERSURFACE + # ---------------- + + f = Scale(T(1), T(2), T(3)) + g = CylinderSurface(T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------------------ + # PARABOLOIDSURFACE + # ------------------ + + f = Scale(T(1), T(2), T(3)) + g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------ + # TORUS + # ------ + + f = Scale(T(1), T(2), T(3)) + g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # --------- + # TRIANGLE + # --------- + + f = Scale(T(1), T(2), T(3)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 2, 3)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # MULTIGEOM + # ---------- + + f = Scale(T(1), T(2)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ g + + # ------ + # PLANE + # ------ + + f = Scale(T(1), T(1), T(2)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ Plane(cart(1, 1, 2), vector(0, 0, 1)) + @test TB.revert(f, r, c) ≈ g + + f = Scale(T(2), T(1), T(1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ g + @test TB.revert(f, r, c) ≈ g + + # --------- + # POINTSET + # --------- + + f = Scale(T(1), T(2)) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(0, 0), cart(1, 0), cart(1, 2)]) + @test TB.revert(f, r, c) ≈ d + + # ------------ + # GEOMETRYSET + # ------------ + + f = Scale(T(1), T(2)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ d + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + @test all(TB.revert(f, r, c) .≈ d) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Scale(T(1), T(2)) + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid(cart(1, 2), cart(11, 22), dims=(10, 10)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Scale(T(1), T(2)) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Scale(T(1), T(2)) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ----------- + # SIMPLEMESH + # ----------- + + f = Scale(T(1), T(2)) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d +end + +@testitem "Stretch" setup = [Setup] begin + @test !isaffine(Stretch) + @test TB.isrevertible(Stretch) + @test TB.isinvertible(Stretch) + @test TB.inverse(Stretch(T(1), T(2))) == Stretch(T(1), T(1 / 2)) + factors = (T(1), T(2)) + f = Stretch(factors) + @test TB.parameters(f) == (; factors) + + # ---- + # VEC + # ---- + + f = Stretch(T(1), T(2)) + v = vector(1, 1) + r, c = TB.apply(f, v) + @test r ≈ vector(1, 2) + @test TB.revert(f, r, c) ≈ v + + # ------ + # POINT + # ------ + + f = Stretch(T(1), T(2)) + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r ≈ cart(1, 1) + @test TB.revert(f, r, c) ≈ g + + # -------- + # SEGMENT + # -------- + + f = Stretch(T(1), T(2)) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(0, 0), cart(1, 0)) + @test TB.revert(f, r, c) ≈ g + + f = Stretch(T(2), T(1)) + g = Segment(cart(0, 0), cart(1, 0)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(-0.5, 0), cart(1.5, 0)) + @test TB.revert(f, r, c) ≈ g + + # ---- + # BOX + # ---- + + f = Stretch(T(1), T(2)) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(cart(0, -0.5), cart(1, 1.5)) + @test TB.revert(f, r, c) ≈ g + + # --------- + # TRIANGLE + # --------- + + f = Stretch(T(1), T(2), T(2)) + g = Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(0, -1 / 3, -1 / 3), cart(1, -1 / 3, -1 / 3), cart(0, 10 / 6, 10 / 6)) + @test TB.revert(f, r, c) ≈ g + + # ---------- + # MULTIGEOM + # ---------- + + f = Stretch(T(1), T(2)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ g + + # ------ + # PLANE + # ------ + + f = Stretch(T(1), T(1), T(2)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ g + @test TB.revert(f, r, c) ≈ g + + f = Stretch(T(2), T(1), T(1)) + g = Plane(cart(1, 1, 1), vector(0, 0, 1)) + r, c = TB.apply(f, g) + @test r ≈ g + @test TB.revert(f, r, c) ≈ g + + # --------- + # POINTSET + # --------- + + f = Stretch(T(1), T(2)) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(0, -1 / 3), cart(1, -1 / 3), cart(1, 10 / 6)]) + @test TB.revert(f, r, c) ≈ d + + # ------------ + # GEOMETRYSET + # ------------ + + f = Stretch(T(1), T(2)) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + @test TB.revert(f, r, c) ≈ d + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + @test all(TB.revert(f, r, c) .≈ d) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Stretch(T(1), T(2)) + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid(cart(1, -4), cart(11, 16), dims=(10, 10)) + @test TB.revert(f, r, c) ≈ d + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Stretch(T(1), T(2)) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Stretch(T(1), T(2)) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + + # ----------- + # SIMPLEMESH + # ----------- + + f = Stretch(T(1), T(2)) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d +end + +@testitem "StdCoords" setup = [Setup] begin + @test !isaffine(StdCoords) + @test TB.isrevertible(StdCoords) + + # --------- + # POINTSET + # --------- + + f = StdCoords() + d = view(PointSet(randpoint2(100)), 1:50) + r, c = TB.apply(f, d) + @test all(sides(boundingbox(r)) .≤ oneunit(ℳ)) + @test TB.revert(f, r, c) ≈ d + r2 = TB.reapply(f, d, c) + @test r == r2 + + # -------------- + # CARTESIANGRID + # -------------- + + f = StdCoords() + d = CartesianGrid(cart(1, 1), cart(11, 11), dims=(10, 10)) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 10)) + @test TB.revert(f, r, c) ≈ d + + f = StdCoords() + d = cartgrid(10, 20) + r, c = TB.apply(f, d) + @test r ≈ CartesianGrid(cart(-0.5, -0.5), cart(0.5, 0.5), dims=(10, 20)) + @test TB.revert(f, r, c) ≈ d + r2 = TB.reapply(f, d, c) + @test r == r2 +end + +@testitem "Proj" setup = [Setup] begin + @test !isaffine(Proj(Polar)) + @test !TB.isrevertible(Proj(Polar)) + @test !TB.isinvertible(Proj(Polar)) + @test TB.parameters(Proj(Polar)) == (; CRS=Polar) + @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) + @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) + f = Proj(Mercator) + @test sprint(show, f) == "Proj(CRS: CoordRefSystems.Mercator)" + @test sprint(show, MIME"text/plain"(), f) == """ + Proj transform + └─ CRS: CoordRefSystems.Mercator""" + + # ---- + # VEC + # ---- + + f = Proj(Polar) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r == v + + # ------ + # POINT + # ------ + + f = Proj(Polar) + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r ≈ Point(Polar(T(√2), T(π / 4))) + + # change the manifold + f = Proj(Mercator) + g = latlon(0, 0) + r, c = TB.apply(f, g) + @test manifold(r) === 𝔼{2} + @test r ≈ merc(0, 0) + + # preserve the manifold + f = Proj(Cartesian) + g = latlon(0, 0) + r, c = TB.apply(f, g) + @test manifold(r) === 🌐 + @test r ≈ cart(6378137.0, 0, 0) + + # -------- + # SEGMENT + # -------- + + f = Proj(Polar) + g = Segment(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Segment(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # ---- + # BOX + # ---- + + f = Proj(Polar) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Box(Point(Polar(T(0), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # --------- + # TRIANGLE + # --------- + + f = Proj(Polar) + g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))) + + # ---------- + # MULTIGEOM + # ---------- + + f = Proj(Polar) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + + # --------- + # CYLINDER + # --------- + + f = Proj(Cylindrical) + g = Cylinder(cart(0, 0, 0), cart(1, 1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Cylinder(Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(√2), T(π / 4), T(1)))) + + # --------- + # POINTSET + # --------- + + f = Proj(Polar) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([Point(Polar(T(0), T(0))), Point(Polar(T(1), T(0))), Point(Polar(T(√2), T(π / 4)))]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Proj(Polar) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Proj(Polar) + d = CartesianGrid((10, 10), cart(1, 1), T.((1, 1))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid((10, 10), Point(Polar(T(√2), T(π / 4))), T.((1, 1))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Proj(Polar) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Proj(Polar) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Proj(Polar) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # -------------- + # SPECIAL CASES + # -------------- + + f = Proj(Mercator) + g = Box(latlon(0, 180), latlon(45, 90)) + r, c = TB.apply(f, g) + @test manifold(r) === 𝔼{2} + + f = Proj(LatLon) + g = Box(merc(0, 0), merc(1, 1)) + r, c = TB.apply(f, g) + @test manifold(r) === 🌐 +end + +@testitem "LengthUnit" setup = [Setup] begin + @test !isaffine(LengthUnit(u"km")) + @test !TB.isrevertible(LengthUnit(u"cm")) + @test !TB.isinvertible(LengthUnit(u"km")) + @test TB.parameters(LengthUnit(u"cm")) == (; unit=u"cm") + + # ---- + # VEC + # ---- + + f = LengthUnit(u"km") + v = vector(1000, 0) + r, c = TB.apply(f, v) + @test r ≈ Vec(T(1) * u"km", T(0) * u"km") + + # ------ + # POINT + # ------ + + f = LengthUnit(u"cm") + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r ≈ Point(T(100) * u"cm", T(100) * u"cm") + + f = LengthUnit(u"km") + g = Point(Polar(T(1000), T(π / 4))) + r, c = TB.apply(f, g) + @test r ≈ Point(Polar(T(1) * u"km", T(π / 4) * u"rad")) + + f = LengthUnit(u"cm") + g = Point(Cylindrical(T(1), T(π / 4), T(1))) + r, c = TB.apply(f, g) + @test r ≈ Point(Cylindrical(T(100) * u"cm", T(π / 4) * u"rad", T(100) * u"cm")) + + f = LengthUnit(u"km") + g = Point(Spherical(T(1000), T(π / 4), T(π / 4))) + r, c = TB.apply(f, g) + @test r ≈ Point(Spherical(T(1) * u"km", T(π / 4) * u"rad", T(π / 4) * u"rad")) + + f = LengthUnit(u"cm") + g = Point(Mercator(T(1), T(1))) + @test_throws ArgumentError TB.apply(f, g) + + # -------- + # SEGMENT + # -------- + + f = LengthUnit(u"km") + g = Segment(cart(0, 0), cart(1000, 1000)) + r, c = TB.apply(f, g) + @test r ≈ Segment(Point(T(0) * u"km", T(0) * u"km"), Point(T(1) * u"km", T(1) * u"km")) + + # ---- + # BOX + # ---- + + f = LengthUnit(u"cm") + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r isa Box + @test r ≈ Box(Point(T(0) * u"cm", T(0) * u"cm"), Point(T(100) * u"cm", T(100) * u"cm")) + + # ------- + # SPHERE + # ------- + + f = LengthUnit(u"km") + g = Sphere(cart(0, 0), T(1000)) + r, c = TB.apply(f, g) + @test r isa Sphere + @test r ≈ Sphere(Point(T(0) * u"km", T(0) * u"km"), T(1) * u"km") + + # ---------- + # ELLIPSOID + # ---------- + + f = LengthUnit(u"cm") + g = Ellipsoid(T.((1, 1, 1)), cart(0, 0, 0)) + r, c = TB.apply(f, g) + @test r isa Ellipsoid + @test r ≈ Ellipsoid((T(100) * u"cm", T(100) * u"cm", T(100) * u"cm"), Point(T(0) * u"cm", T(0) * u"cm", T(0) * u"cm")) + + # --------- + # TRIANGLE + # --------- + + f = LengthUnit(u"km") + g = Triangle(cart(0, 0), cart(1000, 0), cart(1000, 1000)) + r, c = TB.apply(f, g) + @test r ≈ Triangle( + Point(T(0) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(1) * u"km") + ) + + # ---------- + # MULTIGEOM + # ---------- + + f = LengthUnit(u"cm") + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + + # --------- + # POINTSET + # --------- + + f = LengthUnit(u"km") + d = PointSet([cart(0, 0), cart(1000, 0), cart(1000, 1000)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([ + Point(T(0) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(0) * u"km"), + Point(T(1) * u"km", T(1) * u"km") + ]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = LengthUnit(u"cm") + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = LengthUnit(u"km") + d = CartesianGrid((10, 10), cart(1000, 1000), T.((1000, 1000))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), (T(1) * u"km", T(1) * u"km")) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = LengthUnit(u"cm") + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = LengthUnit(u"km") + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ----------- + # SIMPLEMESH + # ----------- + + f = LengthUnit(u"cm") + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) +end + +@testitem "Shadow" setup = [Setup] begin + @test !isaffine(Shadow(:xy)) + @test !TB.isrevertible(Shadow("xy")) + @test !TB.isinvertible(Shadow(:xy)) + @test TB.parameters(Shadow("xy")) == (; dims=(1, 2)) + @test TB.parameters(Shadow(:yx)) == (; dims=(2, 1)) + @test TB.parameters(Shadow("xz")) == (; dims=(1, 3)) + @test TB.parameters(Shadow(:yz)) == (; dims=(2, 3)) + @test_throws ArgumentError Shadow(:xk) + + # ---- + # VEC + # ---- + + f = Shadow(:xy) + v = vector(1, 2, 3) + r, c = TB.apply(f, v) + @test r == vector(1, 2) + + # ------ + # POINT + # ------ + + f = Shadow(:xz) + g = cart(1, 2, 3) + r, c = TB.apply(f, g) + @test r == cart(1, 3) + + # -------- + # SEGMENT + # -------- + + f = Shadow(:yz) + g = Segment(cart(1, 2, 3), cart(4, 5, 6)) + r, c = TB.apply(f, g) + @test r == Segment(cart(2, 3), cart(5, 6)) + + # ---- + # BOX + # ---- + + f = Shadow(:xy) + g = Box(cart(1, 2, 3), cart(4, 5, 6)) + r, c = TB.apply(f, g) + @test r isa Box + @test r == Box(cart(1, 2), cart(4, 5)) + + # ------ + # PLANE + # ------ + + f = Shadow(:xz) + g = Plane(cart(0, 0, 0), vector(0, 0, 1)) + @test_throws ArgumentError TB.apply(f, g) + + # ---------- + # ELLIPSOID + # ---------- + + f = Shadow(:yz) + g = Ellipsoid(T.((1, 2, 3))) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ----- + # DISK + # ----- + + f = Shadow(:xy) + g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ------- + # CIRCLE + # ------- + + f = Shadow(:xz) + g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ---------------- + # CYLINDERSURFACE + # ---------------- + + f = Shadow(:yz) + g = CylinderSurface(T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ------------ + # CONESURFACE + # ------------ + + f = Shadow(:xy) + p = Plane(cart(0, 0, 0), vector(0, 0, 1)) + g = ConeSurface(Disk(p, T(2)), cart(0, 0, 1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # --------------- + # FRUSTUMSURFACE + # --------------- + + f = Shadow(:xz) + pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) + pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) + g = FrustumSurface(Disk(pb, T(1)), Disk(pt, T(2))) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ------------------ + # PARABOLOIDSURFACE + # ------------------ + + f = Shadow(:yz) + g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # ------ + # TORUS + # ------ + + f = Shadow(:xy) + g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + + # --------- + # TRIANGLE + # --------- + + f = Shadow(:xz) + g = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) + r, c = TB.apply(f, g) + @test r == Triangle(cart(1, 3), cart(4, 6), cart(7, 9)) + + # ---------- + # MULTIGEOM + # ---------- + + f = Shadow(:yz) + t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r == Multi([f(t), f(t)]) + + # --------- + # POINTSET + # --------- + + f = Shadow(:xy) + d = PointSet([cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)]) + r, c = TB.apply(f, d) + @test r == PointSet([cart(1, 2), cart(4, 5), cart(7, 8)]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Shadow(:xz) + t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r == GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .== [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Shadow(:yz) + d = CartesianGrid((10, 11, 12), cart(1, 2, 3), T.((1.0, 1.1, 1.2))) + r, c = TB.apply(f, d) + @test r isa CartesianGrid + @test r == CartesianGrid((11, 12), cart(2, 3), T.((1.1, 1.2))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Shadow(:xy) + d = convert(RectilinearGrid, cartgrid(10, 11, 12)) + r, c = TB.apply(f, d) + @test r isa RectilinearGrid + @test r == RectilinearGrid(Meshes.xyz(d)[1], Meshes.xyz(d)[2]) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Shadow(:xz) + d = convert(StructuredGrid, cartgrid(10, 11, 12)) + r, c = TB.apply(f, d) + @test r isa StructuredGrid + @test r == StructuredGrid(Meshes.XYZ(d)[1][:, 1, :], Meshes.XYZ(d)[3][:, 1, :]) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Shadow(:yz) + p = cart.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r == SimpleMesh(f.(vertices(d)), topology(d)) +end + +@testitem "Crop" setup = [Setup] begin + @test !isaffine(Crop(x=(T(2), T(4)))) + @test !TB.isrevertible(Crop(x=(T(2), T(4)))) + @test !TB.isinvertible(Crop(x=(T(2), T(4)))) + @test TB.parameters(Crop(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) + @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) + @test TB.parameters(Crop(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) + @test_throws ArgumentError Crop(x=(T(2) * u"°", T(4) * u"°")) + + # --------- + # POINTSET + # --------- + + f = Crop(x=(T(1.5), T(3.5))) + d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) + r, c = TB.apply(f, d) + @test r == PointSet([cart(2, 1), cart(3, 1)]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Crop(x=(T(1.5), T(3.5))) + t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + t2 = t1 |> Translate(T(2), T(2)) + t3 = t2 |> Translate(T(2), T(2)) + d = GeometrySet([t1, t2, t3]) + r, c = TB.apply(f, d) + @test r == GeometrySet([t2]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Crop(z=(T(1.5), T(4.5))) + d = cartgrid(10, 10, 10) + r, c = TB.apply(f, d) + @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Crop(y=(T(3.5), T(6.5))) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Crop(x=(T(5.5), T(8.5))) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Crop(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) + d = convert(SimpleMesh, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) +end + +@testitem "Repair(0)" setup = [Setup] begin + @test !isaffine(Repair) + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) + rpoly = poly |> Repair(0) + @test nvertices(rpoly) == 4 + @test vertices(rpoly) == cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + + repair = Repair(0) + @test sprint(show, repair) == "Repair(K: 0)" + @test sprint(show, MIME"text/plain"(), repair) == """ + Repair transform + └─ K: 0""" +end + +@testitem "Repair(1)" setup = [Setup] begin + # a tetrahedron with an unused vertex + points = cart.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) + connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) + mesh = SimpleMesh(points, connec) + rmesh = mesh |> Repair(1) + @test nvertices(rmesh) == nvertices(mesh) - 1 + @test nelements(rmesh) == nelements(mesh) + @test cart(5, 5, 5) ∉ vertices(rmesh) +end + +@testitem "Repair(2)" setup = [Setup] begin end - # ------------ - # GEOMETRYSET - # ------------ - - f = LengthUnit(u"cm") - t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r ≈ GeometrySet([f(t), f(t)]) - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .≈ [f(t), f(t)]) - - # -------------- - # CARTESIANGRID - # -------------- - - f = LengthUnit(u"km") - d = CartesianGrid((10, 10), cart(1000, 1000), T.((1000, 1000))) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid((10, 10), Point(T(1) * u"km", T(1) * u"km"), (T(1) * u"km", T(1) * u"km")) - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = LengthUnit(u"cm") - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = LengthUnit(u"km") - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # ----------- - # SIMPLEMESH - # ----------- - - f = LengthUnit(u"cm") - p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - end - - @testset "Shadow" begin - @test !isaffine(Shadow(:xy)) - @test !TB.isrevertible(Shadow("xy")) - @test !TB.isinvertible(Shadow(:xy)) - @test TB.parameters(Shadow("xy")) == (; dims=(1, 2)) - @test TB.parameters(Shadow(:yx)) == (; dims=(2, 1)) - @test TB.parameters(Shadow("xz")) == (; dims=(1, 3)) - @test TB.parameters(Shadow(:yz)) == (; dims=(2, 3)) - @test_throws ArgumentError Shadow(:xk) - - # ---- - # VEC - # ---- - - f = Shadow(:xy) - v = vector(1, 2, 3) - r, c = TB.apply(f, v) - @test r == vector(1, 2) - - # ------ - # POINT - # ------ - - f = Shadow(:xz) - g = cart(1, 2, 3) - r, c = TB.apply(f, g) - @test r == cart(1, 3) - - # -------- - # SEGMENT - # -------- - - f = Shadow(:yz) - g = Segment(cart(1, 2, 3), cart(4, 5, 6)) - r, c = TB.apply(f, g) - @test r == Segment(cart(2, 3), cart(5, 6)) - - # ---- - # BOX - # ---- - - f = Shadow(:xy) - g = Box(cart(1, 2, 3), cart(4, 5, 6)) - r, c = TB.apply(f, g) - @test r isa Box - @test r == Box(cart(1, 2), cart(4, 5)) - - # ------ - # PLANE - # ------ - - f = Shadow(:xz) - g = Plane(cart(0, 0, 0), vector(0, 0, 1)) - @test_throws ArgumentError TB.apply(f, g) - - # ---------- - # ELLIPSOID - # ---------- - - f = Shadow(:yz) - g = Ellipsoid(T.((1, 2, 3))) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ----- - # DISK - # ----- - - f = Shadow(:xy) - g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ------- - # CIRCLE - # ------- - - f = Shadow(:xz) - g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ---------------- - # CYLINDERSURFACE - # ---------------- - - f = Shadow(:yz) - g = CylinderSurface(T(1)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ------------ - # CONESURFACE - # ------------ - - f = Shadow(:xy) - p = Plane(cart(0, 0, 0), vector(0, 0, 1)) - g = ConeSurface(Disk(p, T(2)), cart(0, 0, 1)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # --------------- - # FRUSTUMSURFACE - # --------------- - - f = Shadow(:xz) - pb = Plane(cart(0, 0, 0), vector(0, 0, 1)) - pt = Plane(cart(0, 0, 10), vector(0, 0, 1)) - g = FrustumSurface(Disk(pb, T(1)), Disk(pt, T(2))) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ------------------ - # PARABOLOIDSURFACE - # ------------------ - - f = Shadow(:yz) - g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # ------ - # TORUS - # ------ - - f = Shadow(:xy) - g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) - m = discretize(g) - r, c = TB.apply(f, g) - @test discretize(r) ≈ f(m) - - # --------- - # TRIANGLE - # --------- - - f = Shadow(:xz) - g = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) - r, c = TB.apply(f, g) - @test r == Triangle(cart(1, 3), cart(4, 6), cart(7, 9)) - - # ---------- - # MULTIGEOM - # ---------- - - f = Shadow(:yz) - t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) - g = Multi([t, t]) - r, c = TB.apply(f, g) - @test r == Multi([f(t), f(t)]) - - # --------- - # POINTSET - # --------- - - f = Shadow(:xy) - d = PointSet([cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)]) - r, c = TB.apply(f, d) - @test r == PointSet([cart(1, 2), cart(4, 5), cart(7, 8)]) - - # ------------ - # GEOMETRYSET - # ------------ - - f = Shadow(:xz) - t = Triangle(cart(1, 2, 3), cart(4, 5, 6), cart(7, 8, 9)) - d = GeometrySet([t, t]) - r, c = TB.apply(f, d) - @test r == GeometrySet([f(t), f(t)]) - d = [t, t] - r, c = TB.apply(f, d) - @test all(r .== [f(t), f(t)]) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Shadow(:yz) - d = CartesianGrid((10, 11, 12), cart(1, 2, 3), T.((1.0, 1.1, 1.2))) - r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r == CartesianGrid((11, 12), cart(2, 3), T.((1.1, 1.2))) - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Shadow(:xy) - d = convert(RectilinearGrid, cartgrid(10, 11, 12)) - r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r == RectilinearGrid(Meshes.xyz(d)[1], Meshes.xyz(d)[2]) - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Shadow(:xz) - d = convert(StructuredGrid, cartgrid(10, 11, 12)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r == StructuredGrid(Meshes.XYZ(d)[1][:, 1, :], Meshes.XYZ(d)[3][:, 1, :]) - - # ----------- - # SIMPLEMESH - # ----------- - - f = Shadow(:yz) - p = cart.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)]) - c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) - d = SimpleMesh(p, c) - r, c = TB.apply(f, d) - @test r == SimpleMesh(f.(vertices(d)), topology(d)) - end - - @testset "Crop" begin - @test !isaffine(Crop(x=(T(2), T(4)))) - @test !TB.isrevertible(Crop(x=(T(2), T(4)))) - @test !TB.isinvertible(Crop(x=(T(2), T(4)))) - @test TB.parameters(Crop(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) - @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) - @test TB.parameters(Crop(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) - @test_throws ArgumentError Crop(x=(T(2) * u"°", T(4) * u"°")) - - # --------- - # POINTSET - # --------- - - f = Crop(x=(T(1.5), T(3.5))) - d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) - r, c = TB.apply(f, d) - @test r == PointSet([cart(2, 1), cart(3, 1)]) - - # ------------ - # GEOMETRYSET - # ------------ - - f = Crop(x=(T(1.5), T(3.5))) - t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - t2 = t1 |> Translate(T(2), T(2)) - t3 = t2 |> Translate(T(2), T(2)) - d = GeometrySet([t1, t2, t3]) - r, c = TB.apply(f, d) - @test r == GeometrySet([t2]) - - # -------------- - # CARTESIANGRID - # -------------- - - f = Crop(z=(T(1.5), T(4.5))) - d = cartgrid(10, 10, 10) - r, c = TB.apply(f, d) - @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) - - # ---------------- - # RECTILINEARGRID - # ---------------- - - f = Crop(y=(T(3.5), T(6.5))) - d = convert(RectilinearGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Crop(x=(T(5.5), T(8.5))) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) - - # ----------- - # SIMPLEMESH - # ----------- - - f = Crop(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) - d = convert(SimpleMesh, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) - end - - @testset "Repair(0)" begin - @test !isaffine(Repair) - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)])) - rpoly = poly |> Repair(0) - @test nvertices(rpoly) == 4 - @test vertices(rpoly) == cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - - repair = Repair(0) - @test sprint(show, repair) == "Repair(K: 0)" - @test sprint(show, MIME"text/plain"(), repair) == """ - Repair transform - └─ K: 0""" - end - - @testset "Repair(1)" begin - # a tetrahedron with an unused vertex - points = cart.([(0, 0, 0), (0, 0, 1), (5, 5, 5), (0, 1, 0), (1, 0, 0)]) - connec = connect.([(1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 4, 5)]) - mesh = SimpleMesh(points, connec) - rmesh = mesh |> Repair(1) - @test nvertices(rmesh) == nvertices(mesh) - 1 - @test nelements(rmesh) == nelements(mesh) - @test cart(5, 5, 5) ∉ vertices(rmesh) - end - - @testset "Repair(2)" begin end - - @testset "Repair(3)" begin end - - @testset "Repair(4)" begin end - - @testset "Repair(5)" begin end - - @testset "Repair(6)" begin end - - @testset "Repair(7)" begin - # mesh with inconsistent orientation - points = randpoint3(6) - connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) - mesh = SimpleMesh(points, connec) - rmesh = mesh |> Repair(7) - topo = topology(mesh) - rtopo = topology(rmesh) - e = collect(elements(topo)) - n = collect(elements(rtopo)) - @test n[1] == e[1] - @test n[2] != e[2] - @test n[3] != e[3] - @test n[4] != e[4] - end - - @testset "Repair(8)" begin - poly = PolyArea( - cart.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)]) - ) - rpoly = poly |> Repair(8) - @test nvertices(rpoly) == 4 - @test vertices(rpoly) == cart.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) - - # degenerate triangle with repeated vertices - poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) - rpoly = poly |> Repair(8) - @test !hasholes(rpoly) - @test rings(rpoly) == [Ring(cart(0, 0))] - @test vertices(rpoly) == [cart(0, 0)] - end - - @testset "Repair(9)" begin - quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - repair = Repair(9) - rquad, cache = TB.apply(repair, quad) - @test rquad isa Quadrangle - @test rquad == quad - - outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) - inner1 = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) - inner2 = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) - poly = PolyArea([outer, inner1, inner2]) - repair = Repair(9) - rpoly, cache = TB.apply(repair, poly) - @test rpoly == PolyArea([outer, inner2, inner1]) - end - - @testset "Repair(10)" begin - outer = Ring(cart.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) - inner = Ring(cart.([(1, 1), (1, 2), (2, 2), (2, 1)])) - poly = PolyArea(outer, inner) - repair = Repair(10) - rpoly, cache = TB.apply(repair, poly) - @test nvertices(rpoly) == nvertices(poly) - @test length(first(rings(rpoly))) > length(first(rings(poly))) - opoly = TB.revert(repair, rpoly, cache) - @test opoly == poly - end - - @testset "Repair(11)" begin - outer = cart.([(0, 0), (0, 2), (2, 2), (2, 0)]) - inner = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - poly = PolyArea(outer, inner) - repair = Repair(11) - rpoly, cache = TB.apply(repair, poly) - router, rinner = rings(rpoly) - @test router == Ring(cart.([(0, 0), (2, 0), (2, 2), (0, 2)])) - @test rinner == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - end - - @testset "Repair(12)" begin - poly = PolyArea(cart.([(0, 0), (1, 0)])) - repair = Repair(12) - rpoly, cache = TB.apply(repair, poly) - @test rpoly == PolyArea(cart.([(0, 0), (0.5, 0.0), (1, 0)])) - - outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) - inner = cart.([(1, 2), (2, 3)]) - poly = PolyArea(outer, inner) - repair = Repair(12) - rpoly, cache = TB.apply(repair, poly) - @test rpoly == PolyArea(outer) - end - - @testset "Repair fallbacks" begin - quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - repair = Repair(10) - rquad, cache = TB.apply(repair, quad) - @test rquad isa Quadrangle - @test rquad == quad - - poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) - poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - multi = Multi([poly1, poly2]) - repair = Repair(11) - rmulti, cache = TB.apply(repair, multi) - @test rmulti == Multi([repair(poly1), repair(poly2)]) - - poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) - poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) - gset = GeometrySet([poly1, poly2]) - repair = Repair(11) - rgset, cache = TB.apply(repair, gset) - @test rgset == GeometrySet([repair(poly1), repair(poly2)]) - end - - @testset "Bridge" begin - @test !isaffine(Bridge) - δ = T(0.01) * u"m" - f = Bridge(δ) - @test TB.parameters(f) == (; δ) - f = Bridge(T(0.01)) - @test TB.parameters(f) == (; δ) - - # https://github.com/JuliaGeometry/Meshes.jl/issues/566 - outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) - inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) - inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) - poly = PolyArea([outer, inner₁, inner₂]) - bpoly = poly |> Bridge(T(0.1)) - @test !hasholes(bpoly) - @test nvertices(bpoly) == 15 - - # make sure that result is inferred - @inferred poly |> Bridge(T(0.1)) - - # unique and bridges - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) - cpoly = poly |> Repair(0) |> Bridge() - @test cpoly == PolyArea(cart.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) - - # basic ngon tests - t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - @test (t |> Bridge() |> boundary) == boundary(t) - q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @test (q |> Bridge() |> boundary) == boundary(q) - - # bridges between holes - outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) - hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) - hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) - poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) - @test vertices(poly) == - cart.([ - (0, 0), - (1, 0), - (1, 1), - (0, 1), - (0.2, 0.2), +@testitem "Repair(3)" setup = [Setup] begin end + +@testitem "Repair(4)" setup = [Setup] begin end + +@testitem "Repair(5)" setup = [Setup] begin end + +@testitem "Repair(6)" setup = [Setup] begin end + +@testitem "Repair(7)" setup = [Setup] begin + # mesh with inconsistent orientation + points = randpoint3(6) + connec = connect.([(1, 2, 3), (3, 4, 2), (4, 3, 5), (6, 3, 1)]) + mesh = SimpleMesh(points, connec) + rmesh = mesh |> Repair(7) + topo = topology(mesh) + rtopo = topology(rmesh) + e = collect(elements(topo)) + n = collect(elements(rtopo)) + @test n[1] == e[1] + @test n[2] != e[2] + @test n[3] != e[3] + @test n[4] != e[4] +end + +@testitem "Repair(8)" setup = [Setup] begin + poly = + PolyArea(cart.([(0.0, 0.0), (0.5, -0.5), (1.0, 0.0), (1.5, 0.5), (1.0, 1.0), (0.5, 1.5), (0.0, 1.0), (-0.5, 0.5)])) + rpoly = poly |> Repair(8) + @test nvertices(rpoly) == 4 + @test vertices(rpoly) == cart.([(0.5, -0.5), (1.5, 0.5), (0.5, 1.5), (-0.5, 0.5)]) + + # degenerate triangle with repeated vertices + poly = PolyArea(cart.([(0, 0), (1, 1), (1, 1)])) + rpoly = poly |> Repair(8) + @test !hasholes(rpoly) + @test rings(rpoly) == [Ring(cart(0, 0))] + @test vertices(rpoly) == [cart(0, 0)] +end + +@testitem "Repair(9)" setup = [Setup] begin + quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + repair = Repair(9) + rquad, cache = TB.apply(repair, quad) + @test rquad isa Quadrangle + @test rquad == quad + + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner1 = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner2 = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) + poly = PolyArea([outer, inner1, inner2]) + repair = Repair(9) + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea([outer, inner2, inner1]) +end + +@testitem "Repair(10)" setup = [Setup] begin + outer = Ring(cart.([(0, 0), (0, 3), (2, 3), (2, 2), (3, 2), (3, 0)])) + inner = Ring(cart.([(1, 1), (1, 2), (2, 2), (2, 1)])) + poly = PolyArea(outer, inner) + repair = Repair(10) + rpoly, cache = TB.apply(repair, poly) + @test nvertices(rpoly) == nvertices(poly) + @test length(first(rings(rpoly))) > length(first(rings(poly))) + opoly = TB.revert(repair, rpoly, cache) + @test opoly == poly +end + +@testitem "Repair(11)" setup = [Setup] begin + outer = cart.([(0, 0), (0, 2), (2, 2), (2, 0)]) + inner = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + poly = PolyArea(outer, inner) + repair = Repair(11) + rpoly, cache = TB.apply(repair, poly) + router, rinner = rings(rpoly) + @test router == Ring(cart.([(0, 0), (2, 0), (2, 2), (0, 2)])) + @test rinner == Ring(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) +end + +@testitem "Repair(12)" setup = [Setup] begin + poly = PolyArea(cart.([(0, 0), (1, 0)])) + repair = Repair(12) + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea(cart.([(0, 0), (0.5, 0.0), (1, 0)])) + + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + inner = cart.([(1, 2), (2, 3)]) + poly = PolyArea(outer, inner) + repair = Repair(12) + rpoly, cache = TB.apply(repair, poly) + @test rpoly == PolyArea(outer) +end + +@testitem "Repair fallbacks" setup = [Setup] begin + quad = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + repair = Repair(10) + rquad, cache = TB.apply(repair, quad) + @test rquad isa Quadrangle + @test rquad == quad + + poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) + poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + multi = Multi([poly1, poly2]) + repair = Repair(11) + rmulti, cache = TB.apply(repair, multi) + @test rmulti == Multi([repair(poly1), repair(poly2)]) + + poly1 = PolyArea(cart.([(0, 0), (0, 2), (2, 2), (2, 0)])) + poly2 = PolyArea(cart.([(0, 0), (0, 1), (1, 1), (1, 0)])) + gset = GeometrySet([poly1, poly2]) + repair = Repair(11) + rgset, cache = TB.apply(repair, gset) + @test rgset == GeometrySet([repair(poly1), repair(poly2)]) +end + +@testitem "Bridge" setup = [Setup] begin + @test !isaffine(Bridge) + δ = T(0.01) * u"m" + f = Bridge(δ) + @test TB.parameters(f) == (; δ) + f = Bridge(T(0.01)) + @test TB.parameters(f) == (; δ) + + # https://github.com/JuliaGeometry/Meshes.jl/issues/566 + outer = Ring(cart(6, 4), cart(6, 7), cart(1, 6), cart(1, 1), cart(5, 2)) + inner₁ = Ring(cart(3, 3), cart(3, 4), cart(4, 3)) + inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) + poly = PolyArea([outer, inner₁, inner₂]) + bpoly = poly |> Bridge(T(0.1)) + @test !hasholes(bpoly) + @test nvertices(bpoly) == 15 + + # make sure that result is inferred + @inferred poly |> Bridge(T(0.1)) + + # unique and bridges + poly = PolyArea(cart.([(0, 0), (1, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1), (0, 1)])) + cpoly = poly |> Repair(0) |> Bridge() + @test cpoly == PolyArea(cart.([(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)])) + + # basic ngon tests + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) + @test (t |> Bridge() |> boundary) == boundary(t) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @test (q |> Bridge() |> boundary) == boundary(q) + + # bridges between holes + outer = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + hole1 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + hole2 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly = PolyArea([outer, reverse(hole1), reverse(hole2)]) + @test vertices(poly) == + cart.([ + (0, 0), + (1, 0), + (1, 1), + (0, 1), + (0.2, 0.2), + (0.2, 0.4), + (0.4, 0.4), + (0.4, 0.2), + (0.6, 0.2), + (0.6, 0.4), + (0.8, 0.4), + (0.8, 0.2) + ]) + bpoly = poly |> Bridge(T(0.01)) + target = + cart.([ + (-0.0035355339059327372, 0.0035355339059327372), + (0.19646446609406729, 0.20353553390593274), (0.2, 0.4), - (0.4, 0.4), - (0.4, 0.2), - (0.6, 0.2), - (0.6, 0.4), + (0.4, 0.405), + (0.6, 0.405), (0.8, 0.4), - (0.8, 0.2) + (0.8, 0.2), + (0.6, 0.2), + (0.6, 0.395), + (0.4, 0.395), + (0.4, 0.2), + (0.20353553390593274, 0.19646446609406729), + (0.0035355339059327372, -0.0035355339059327372), + (1.0, 0.0), + (1.0, 1.0), + (0.0, 1.0) ]) - bpoly = poly |> Bridge(T(0.01)) - target = - cart.([ - (-0.0035355339059327372, 0.0035355339059327372), - (0.19646446609406729, 0.20353553390593274), - (0.2, 0.4), - (0.4, 0.405), - (0.6, 0.405), - (0.8, 0.4), - (0.8, 0.2), - (0.6, 0.2), - (0.6, 0.395), - (0.4, 0.395), - (0.4, 0.2), - (0.20353553390593274, 0.19646446609406729), - (0.0035355339059327372, -0.0035355339059327372), - (1.0, 0.0), - (1.0, 1.0), - (0.0, 1.0) - ]) - @test all(vertices(bpoly) .≈ target) - - poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) - bpoly = poly |> Bridge() - @test bpoly isa Quadrangle - @test bpoly == poly - - # bridge with latlon coords - outer = latlon.([(0, 0), (0, 90), (90, 90), (90, 0)]) - hole1 = latlon.([(10, 10), (10, 20), (20, 20), (20, 10)]) - hole2 = latlon.([(10, 80), (10, 90), (20, 90), (20, 80)]) - poly = PolyArea([outer, hole1, hole2]) - bpoly = poly |> Bridge() - @test nvertices(bpoly) == 16 - end - - @testset "Smoothing" begin - @test !isaffine(LambdaMuSmoothing) - n, λ, μ = 30, T(0.5), T(0) - f = LambdaMuSmoothing(n, λ, μ) - @test TB.parameters(f) == (; n, λ, μ) - - # smoothing doesn't change the topology - trans = LaplaceSmoothing(30) - @test TB.isrevertible(trans) - mesh = readply(T, joinpath(datadir, "beethoven.ply")) - smesh = trans(mesh) - @test nvertices(smesh) == nvertices(mesh) - @test nelements(smesh) == nelements(mesh) - @test topology(smesh) == topology(mesh) - - # smoothing doesn't change the topology - trans = TaubinSmoothing(30) - @test TB.isrevertible(trans) - mesh = readply(T, joinpath(datadir, "beethoven.ply")) - smesh = trans(mesh) - @test nvertices(smesh) == nvertices(mesh) - @test nelements(smesh) == nelements(mesh) - @test topology(smesh) == topology(mesh) - end + @test all(vertices(bpoly) .≈ target) + + poly = Quadrangle(cart(0, 1, 0), cart(1, 1, 0), cart(1, 0, 0), cart(0, 0, 0)) + bpoly = poly |> Bridge() + @test bpoly isa Quadrangle + @test bpoly == poly + + # bridge with latlon coords + outer = latlon.([(0, 0), (0, 90), (90, 90), (90, 0)]) + hole1 = latlon.([(10, 10), (10, 20), (20, 20), (20, 10)]) + hole2 = latlon.([(10, 80), (10, 90), (20, 90), (20, 80)]) + poly = PolyArea([outer, hole1, hole2]) + bpoly = poly |> Bridge() + @test nvertices(bpoly) == 16 +end + +@testitem "Smoothing" setup = [Setup] begin + @test !isaffine(LambdaMuSmoothing) + n, λ, μ = 30, T(0.5), T(0) + f = LambdaMuSmoothing(n, λ, μ) + @test TB.parameters(f) == (; n, λ, μ) + + # smoothing doesn't change the topology + trans = LaplaceSmoothing(30) + @test TB.isrevertible(trans) + mesh = readply(T, joinpath(datadir, "beethoven.ply")) + smesh = trans(mesh) + @test nvertices(smesh) == nvertices(mesh) + @test nelements(smesh) == nelements(mesh) + @test topology(smesh) == topology(mesh) + + # smoothing doesn't change the topology + trans = TaubinSmoothing(30) + @test TB.isrevertible(trans) + mesh = readply(T, joinpath(datadir, "beethoven.ply")) + smesh = trans(mesh) + @test nvertices(smesh) == nvertices(mesh) + @test nelements(smesh) == nelements(mesh) + @test topology(smesh) == topology(mesh) end diff --git a/test/traversing.jl b/test/traversing.jl index bd0c69f0b..a79a33ddf 100644 --- a/test/traversing.jl +++ b/test/traversing.jl @@ -1,91 +1,77 @@ -@testset "Paths" begin +@testitem "Traversing" setup = [Setup] begin grid = cartgrid(100, 100) - for path in [LinearPath(), RandomPath(), ShiftedPath(LinearPath(), 0), SourcePath(1:3)] p = traverse(grid, path) @test length(p) == 100 * 100 end - @testset "LinearPath" begin - p = traverse(grid, LinearPath()) - @test p == 1:(100 * 100) - end - - @testset "RandomPath" begin - p = traverse(grid, RandomPath()) - @test all(1 .≤ collect(p) .≤ 100 * 100) - - path = RandomPath(StableRNG(123)) - grid = cartgrid(3, 3) - @test traverse(grid, path) == [4, 7, 2, 1, 3, 8, 5, 6, 9] - end - - @testset "SourcePath" begin - grid = cartgrid(3, 3) - pset = PointSet(centroid.(grid)) - - for sdomain in [grid, pset] - p = traverse(sdomain, SourcePath([1, 9])) - @test collect(p) == [1, 9, 2, 4, 6, 8, 5, 3, 7] + grid = cartgrid(100, 100) + p = traverse(grid, LinearPath()) + @test p == 1:(100 * 100) - p = traverse(sdomain, SourcePath([1])) - @test collect(p) == [1, 2, 4, 5, 3, 7, 6, 8, 9] - end + grid = cartgrid(100, 100) + p = traverse(grid, RandomPath()) + @test all(1 .≤ collect(p) .≤ 100 * 100) + path = RandomPath(StableRNG(123)) + grid = cartgrid(3, 3) + @test traverse(grid, path) == [4, 7, 2, 1, 3, 8, 5, 6, 9] + + grid = cartgrid(3, 3) + pset = PointSet(centroid.(grid)) + for sdomain in [grid, pset] + t = traverse(sdomain, SourcePath([1, 9])) + @test collect(t) == [1, 9, 2, 4, 6, 8, 5, 3, 7] + + t = traverse(sdomain, SourcePath([1])) + @test collect(t) == [1, 2, 4, 5, 3, 7, 6, 8, 9] end - @testset "ShiftedPath" begin - grid = cartgrid(3, 3) - path = LinearPath() - for offset in [0, 1, -1] - spath = ShiftedPath(path, offset) - p = traverse(grid, path) - sp = traverse(grid, spath) - @test length(sp) == 9 - @test collect(sp) == circshift(p, -offset) - end + grid = cartgrid(3, 3) + path = LinearPath() + for offset in [0, 1, -1] + spath = ShiftedPath(path, offset) + t = traverse(grid, path) + st = traverse(grid, spath) + @test length(st) == 9 + @test collect(st) == circshift(t, -offset) end - @testset "MultiGridPath" begin - path = MultiGridPath() + path = MultiGridPath() - grid = cartgrid(3, 3) - @test traverse(grid, path) == [1, 3, 7, 9, 2, 4, 5, 6, 8] + grid = cartgrid(3, 3) + @test traverse(grid, path) == [1, 3, 7, 9, 2, 4, 5, 6, 8] - grid = cartgrid(3, 4) - @test traverse(grid, path) == [1, 3, 10, 12, 2, 7, 8, 9, 4, 5, 6, 11] + grid = cartgrid(3, 4) + @test traverse(grid, path) == [1, 3, 10, 12, 2, 7, 8, 9, 4, 5, 6, 11] - grid = CartesianGrid(3, 3, 2) - @test traverse(grid, path) == [1, 3, 7, 9, 10, 12, 16, 18, 2, 4, 5, 6, 8, 11, 13, 14, 15, 17] + grid = CartesianGrid(3, 3, 2) + @test traverse(grid, path) == [1, 3, 7, 9, 10, 12, 16, 18, 2, 4, 5, 6, 8, 11, 13, 14, 15, 17] - grid = RectilinearGrid(T.(0:3), T.(0:3)) - @test traverse(grid, path) == [1, 3, 7, 9, 2, 4, 5, 6, 8] + grid = RectilinearGrid(T.(0:3), T.(0:3)) + @test traverse(grid, path) == [1, 3, 7, 9, 2, 4, 5, 6, 8] - grid = RectilinearGrid(T.(0:0.5:2), T.(0:0.5:2)) - @test traverse(grid, path) == [1, 4, 13, 16, 3, 9, 11, 2, 5, 6, 7, 8, 10, 12, 14, 15] + grid = RectilinearGrid(T.(0:0.5:2), T.(0:0.5:2)) + @test traverse(grid, path) == [1, 4, 13, 16, 3, 9, 11, 2, 5, 6, 7, 8, 10, 12, 14, 15] - cgrid = cartgrid(4, 4) - rgrid = RectilinearGrid(T.(0:4), T.(0:4)) - @test traverse(cgrid, path) == traverse(rgrid, path) + cgrid = cartgrid(4, 4) + rgrid = RectilinearGrid(T.(0:4), T.(0:4)) + @test traverse(cgrid, path) == traverse(rgrid, path) - grid = cartgrid(3, 4) - vgrid = view(grid, 3:10) - @test traverse(vgrid, path) == [3, 10, 7, 8, 9, 4, 5, 6] - end + grid = cartgrid(3, 4) + vgrid = view(grid, 3:10) + @test traverse(vgrid, path) == [3, 10, 7, 8, 9, 4, 5, 6] - @testset "Miscellaneous" begin - if visualtests - paths = - [LinearPath(), RandomPath(StableRNG(123)), ShiftedPath(LinearPath(), 10), SourcePath(1:3), MultiGridPath()] + if visualtests + paths = [LinearPath(), RandomPath(StableRNG(123)), ShiftedPath(LinearPath(), 10), SourcePath(1:3), MultiGridPath()] - fnames = ["linear-path", "random-path", "shifted-path", "source-path", "multi-grid-path"] + fnames = ["linear-path", "random-path", "shifted-path", "source-path", "multi-grid-path"] - for (path, fname) in zip(paths, fnames) - for d in (6, 7) - grid = cartgrid(d, d) - elems = [grid[i] for i in traverse(grid, path)] - fig = viz(elems, color=1:length(elems)) - @test_reference "data/$fname-$(d)x$(d).png" fig - end + for (path, fname) in zip(paths, fnames) + for d in (6, 7) + agrid = cartgrid(d, d) + elems = [agrid[i] for i in traverse(agrid, path)] + fig = viz(elems, color=1:length(elems)) + @test_reference "data/$fname-$(d)x$(d).png" fig end end end diff --git a/test/utils.jl b/test/utils.jl index e9c3c16d4..480199714 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,4 +1,4 @@ -@testset "Utilities" begin +@testitem "Utilities" setup = [Setup] begin a, b, c = cart(0, 0), cart(1, 0), cart(0, 1) @test signarea(a, b, c) == T(0.5) * u"m^2" a, b, c = cart(0, 0), cart(0, 1), cart(1, 0) diff --git a/test/vectors.jl b/test/vectors.jl index b28468691..3022e76b5 100644 --- a/test/vectors.jl +++ b/test/vectors.jl @@ -1,4 +1,4 @@ -@testset "Vectors" begin +@testitem "Vectors" setup = [Setup] begin # vararg constructors @test eltype(Vec(1, 1)) == Meshes.Met{Float64} @test eltype(Vec(1.0, 1.0)) == Meshes.Met{Float64} diff --git a/test/viewing.jl b/test/viewing.jl index 79cfebbae..91c856316 100644 --- a/test/viewing.jl +++ b/test/viewing.jl @@ -1,4 +1,4 @@ -@testset "Viewing" begin +@testitem "Viewing" setup = [Setup] begin g = cartgrid(10, 10) v = view(g, 1:3) @test parent(v) == g diff --git a/test/winding.jl b/test/winding.jl index 467e65e5b..936473faa 100644 --- a/test/winding.jl +++ b/test/winding.jl @@ -1,4 +1,4 @@ -@testset "winding" begin +@testitem "Winding numbers" setup = [Setup] begin p = cart(0.5, 0.5) c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test winding(p, c) ≈ T(1) From bce606822476284be391391f5d666cc910ff3b6e Mon Sep 17 00:00:00 2001 From: Michael Ingold Date: Tue, 27 Aug 2024 15:24:19 -0400 Subject: [PATCH 295/423] New parameterization functions for Cone and ConeSurface (#1020) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Test change to radius from cos to tan * Drop unneeded type spec * Add new param functions for Cone and ConeSurface * Bugfix - missed name change * Bugfix - updated changed name * Bugfix in point-vector-svector operations * Adjust formatting to match prior style * Add tests for ConeSurface parameterization * Bugfix in test - wrong name * Add DomainError test for Cone param * Bugfix - wrong name in test * Bugfix - wrong name in test * Bugfix - promote tuple to Point for comparison * Add tests for Cone param function * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Update test/primitives.jl * Update test/primitives.jl * Rename arguments and internal names, use some abstract API * Update variable name * Rename arguments and internal names, use some abstract APIs * Bugfix - update name * Conversion to abstract methods * Revert test changes to simplify merge * Re-add tests after merge * Final adjustments --------- Co-authored-by: Júlio Hoffimann --- src/boundingboxes.jl | 2 +- src/geometries/primitives/cone.jl | 9 +++++++++ src/geometries/primitives/conesurface.jl | 11 +++-------- src/sampling/regular.jl | 6 +++--- test/primitives.jl | 12 ++++++++++-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index e0c764c13..8f5d1753b 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -55,7 +55,7 @@ end function boundingbox(c::ConeSurface) us = (0, 1 / 4, 1 / 2, 3 / 4) - vs = (1,) + vs = (0,) ps = [c(u, v) for (u, v) in Iterators.product(us, vs)] boundingbox([ps; apex(c)]) end diff --git a/src/geometries/primitives/cone.jl b/src/geometries/primitives/cone.jl index 716239ffa..61bf73d77 100644 --- a/src/geometries/primitives/cone.jl +++ b/src/geometries/primitives/cone.jl @@ -34,3 +34,12 @@ halfangle(c::Cone) = atan(radius(base(c)), height(c)) Base.isapprox(c₁::Cone, c₂::Cone; atol=atol(lentype(c₁)), kwargs...) = isapprox(boundary(c₁), boundary(c₂); atol, kwargs...) + +function (c::Cone)(r, φ, h) + if (r < 0 || r > 1) || (φ < 0 || φ > 1) || (h < 0 || h > 1) + throw(DomainError((r, φ, h), "c(r, φ, h) is not defined for r, φ, h outside [0, 1]³.")) + end + a = c.apex + b = c.base(r, φ) + Segment(b, a)(h) +end diff --git a/src/geometries/primitives/conesurface.jl b/src/geometries/primitives/conesurface.jl index 9b95e7a67..5679fd434 100644 --- a/src/geometries/primitives/conesurface.jl +++ b/src/geometries/primitives/conesurface.jl @@ -36,12 +36,7 @@ function (c::ConeSurface)(φ, h) if (φ < 0 || φ > 1) || (h < 0 || h > 1) throw(DomainError((φ, h), "c(φ, h) is not defined for φ, h outside [0, 1]².")) end - n = -normal(c.base) - v = c.base(T(0), T(0)) - c.apex - l = norm(v) - θ = ∠(n, v) - o = c.apex + T(h) * v - r = T(h) * l * cos(θ) - s = Circle(Plane(o, n), r) - s(T(φ)) + a = c.apex + b = c.base(one(T), φ) + Segment(b, a)(h) end diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 152ca1bfc..72533c0fc 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -75,9 +75,9 @@ firstoffset(::CylinderSurface) = (n -> zero(n), n -> zero(n)) lastoffset(::CylinderSurface) = (n -> inv(n), n -> zero(n)) extrapoints(c::CylinderSurface, sz) = (bottom(c)(0, 0), top(c)(0, 0)) -firstoffset(::ConeSurface) = (n -> zero(n), n -> inv(n)) -lastoffset(::ConeSurface) = (n -> inv(n), n -> zero(n)) -extrapoints(c::ConeSurface, sz) = (apex(c), base(c)(0, 0)) +firstoffset(::ConeSurface) = (n -> zero(n), n -> zero(n)) +lastoffset(::ConeSurface) = (n -> inv(n), n -> inv(n)) +extrapoints(c::ConeSurface, sz) = (base(c)(0, 0), apex(c)) firstoffset(::FrustumSurface) = (n -> zero(n), n -> zero(n)) lastoffset(::FrustumSurface) = (n -> inv(n), n -> zero(n)) diff --git a/test/primitives.jl b/test/primitives.jl index adea51d38..59b2889bd 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1046,15 +1046,19 @@ end @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ @test boundary(c) == ConeSurface(d, a) + @test_throws DomainError c(T(0), T(0), nextfloat(T(1))) p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = T.((0, 0, 1)) + a = cart(0, 0, 1) c = Cone(d, a) @test embeddim(c) == 3 @test paramdim(c) == 3 @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ + @test c(T(0), T(0), T(0)) ≈ centroid(p) + @test c(T(0), T(0), T(1)) ≈ a + @test c(T(1.0), T(0.25), T(0.0)) ≈ cart(0, 2, 0) p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) @@ -1120,15 +1124,19 @@ end @test crs(s) <: Cartesian{NoDatum} @test Meshes.lentype(s) == ℳ @test isnothing(boundary(s)) + @test_throws DomainError s(T(0), nextfloat(T(1))) p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) - a = T.((0, 0, 1)) + a = cart(0, 0, 1) c = ConeSurface(d, a) @test embeddim(c) == 3 @test paramdim(c) == 2 @test crs(c) <: Cartesian{NoDatum} @test Meshes.lentype(c) == ℳ + @test c(T(0), T(0)) ≈ cart(2, 0, 0) + @test c(T(0), T(1)) ≈ a + @test c(T(0.25), T(0)) ≈ cart(0, 2, 0) p = Plane(cart(0, 0, 0), vector(0, 0, 1)) d = Disk(p, T(2)) From 6a01fe85fa68ed0cd73db00bfc08f775d2683488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 27 Aug 2024 17:48:06 -0300 Subject: [PATCH 296/423] Rename clip/refine methods for clarity (#1026) --- docs/src/algorithms/clipping.md | 6 +++--- docs/src/algorithms/refinement.md | 8 ++++---- src/Meshes.jl | 4 ++-- src/clipping/sutherlandhodgman.jl | 8 ++++---- src/intersections/polygons.jl | 4 ++-- src/refinement/catmullclark.jl | 6 +++--- test/clipping.jl | 16 ++++++++-------- test/refinement.jl | 20 ++++++++++---------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/src/algorithms/clipping.md b/docs/src/algorithms/clipping.md index 434ef591b..c2655598f 100644 --- a/docs/src/algorithms/clipping.md +++ b/docs/src/algorithms/clipping.md @@ -10,10 +10,10 @@ clip ClippingMethod ``` -## SutherlandHodgman +## Sutherland-Hodgman ```@docs -SutherlandHodgman +SutherlandHodgmanClipping ``` ```@example clipping @@ -26,7 +26,7 @@ poly = PolyArea([outer, inner]) other = Box((0,1), (3,7)) # clipped polygon -clipped = clip(poly, other, SutherlandHodgman()) +clipped = clip(poly, other, SutherlandHodgmanClipping()) viz(poly) viz!(other, color = :black, alpha = 0.2) diff --git a/docs/src/algorithms/refinement.md b/docs/src/algorithms/refinement.md index 52d25344e..ce6d43d92 100644 --- a/docs/src/algorithms/refinement.md +++ b/docs/src/algorithms/refinement.md @@ -79,7 +79,7 @@ fig ## Catmull-Clark ```@docs -CatmullClark +CatmullClarkRefinement ``` ```@example refinement @@ -89,9 +89,9 @@ connec = connect.([(1,4,3,2),(5,6,7,8),(1,2,6,5),(3,4,8,7),(1,5,8,4),(2,3,7,6)]) mesh = SimpleMesh(points, connec) # refine three times -ref1 = refine(mesh, CatmullClark()) -ref2 = refine(ref1, CatmullClark()) -ref3 = refine(ref2, CatmullClark()) +ref1 = refine(mesh, CatmullClarkRefinement()) +ref2 = refine(ref1, CatmullClarkRefinement()) +ref3 = refine(ref2, CatmullClarkRefinement()) fig = Mke.Figure(size = (800, 800)) viz(fig[1,1], mesh, showsegments = true) diff --git a/src/Meshes.jl b/src/Meshes.jl index 6d04de839..2d2064416 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -433,7 +433,7 @@ export # clipping ClippingMethod, - SutherlandHodgman, + SutherlandHodgmanClipping, clip, # intersections @@ -513,7 +513,7 @@ export TriRefinement, QuadRefinement, RegularRefinement, - CatmullClark, + CatmullClarkRefinement, TriSubdivision, refine, diff --git a/src/clipping/sutherlandhodgman.jl b/src/clipping/sutherlandhodgman.jl index cbd3250bc..aad74942b 100644 --- a/src/clipping/sutherlandhodgman.jl +++ b/src/clipping/sutherlandhodgman.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - SutherlandHodgman() + SutherlandHodgmanClipping() The Sutherland-Hodgman algorithm for clipping polygons. @@ -16,15 +16,15 @@ The Sutherland-Hodgman algorithm for clipping polygons. * The algorithm assumes that the clipping geometry is convex. """ -struct SutherlandHodgman <: ClippingMethod end +struct SutherlandHodgmanClipping <: ClippingMethod end -function clip(poly::Polygon, other::Geometry, method::SutherlandHodgman) +function clip(poly::Polygon, other::Geometry, method::SutherlandHodgmanClipping) c = [clip(ring, boundary(other), method) for ring in rings(poly)] r = [r for r in c if !isnothing(r)] isempty(r) ? nothing : PolyArea(r) end -function clip(ring::Ring, other::Ring, ::SutherlandHodgman) +function clip(ring::Ring, other::Ring, ::SutherlandHodgmanClipping) # make sure other ring is CCW occw = orientation(other) == CCW ? other : reverse(other) diff --git a/src/intersections/polygons.jl b/src/intersections/polygons.jl index 9688ef427..908c9fd13 100644 --- a/src/intersections/polygons.jl +++ b/src/intersections/polygons.jl @@ -5,9 +5,9 @@ function intersection(f, poly₁::Polygon, poly₂::Polygon) # TODO: use Weiler-Atherton or other more general clipping method clipped = if isconvex(poly₂) - clip(poly₁, poly₂, SutherlandHodgman()) + clip(poly₁, poly₂, SutherlandHodgmanClipping()) elseif isconvex(poly₁) - clip(poly₂, poly₁, SutherlandHodgman()) + clip(poly₂, poly₁, SutherlandHodgmanClipping()) else throw(ErrorException("intersection not implemented between two non-convex polygons")) end diff --git a/src/refinement/catmullclark.jl b/src/refinement/catmullclark.jl index c02a6c466..bcf0fcf69 100644 --- a/src/refinement/catmullclark.jl +++ b/src/refinement/catmullclark.jl @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ """ - CatmullClark() + CatmullClarkRefinement() Catmull-Clark refinement of polygonal meshes. @@ -19,9 +19,9 @@ surface. B-spline surfaces on arbitrary topological meshes] (https://www.sciencedirect.com/science/article/abs/pii/0010448578901100) """ -struct CatmullClark <: RefinementMethod end +struct CatmullClarkRefinement <: RefinementMethod end -function refine(mesh, ::CatmullClark) +function refine(mesh, ::CatmullClarkRefinement) # retrieve geometry and topology points = vertices(mesh) connec = topology(mesh) diff --git a/test/clipping.jl b/test/clipping.jl index c6bc5e0db..117f8a8ce 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -2,14 +2,14 @@ # triangle poly = Triangle(cart(6, 2), cart(3, 5), cart(0, 2)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test issimple(clipped) @test all(vertices(clipped) .≈ [cart(5, 3), cart(4, 4), cart(2, 4), cart(0, 2), cart(5, 2)]) # octagon poly = Octagon(cart(8, -2), cart(8, 5), cart(2, 5), cart(4, 3), cart(6, 3), cart(4, 1), cart(2, 1), cart(2, -2)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test !issimple(clipped) @test all( vertices(clipped) .≈ @@ -19,20 +19,20 @@ # inside poly = Quadrangle(cart(1, 0), cart(1, 1), cart(0, 1), cart(0, 0)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(poly)) # outside poly = Quadrangle(cart(7, 6), cart(7, 7), cart(6, 7), cart(6, 6)) other = Quadrangle(cart(5, 0), cart(5, 4), cart(0, 4), cart(0, 0)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test isnothing(clipped) # surrounded poly = Hexagon(cart(0, 2), cart(-2, 2), cart(-2, 0), cart(0, -2), cart(2, -2), cart(2, 0)) other = Hexagon(cart(1, 0), cart(0, 1), cart(-1, 1), cart(-1, 0), cart(0, -1), cart(1, -1)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test issimple(clipped) @test all(vertices(clipped) .≈ vertices(other)) @@ -41,7 +41,7 @@ inner = Ring(cart(4, 4), cart(2, 4), cart(3, 6)) poly = PolyArea([outer, inner]) other = Box(cart(0, 1), cart(3, 7)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) crings = rings(clipped) @test !issimple(clipped) @test all( @@ -55,7 +55,7 @@ inner = Ring(cart(1, 3), cart(3, 3), cart(3, 1), cart(1, 1)) poly = PolyArea([outer, inner]) other = Quadrangle(cart(4, 4), cart(0, 4), cart(0, 0), cart(4, 0)) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) @test !issimple(clipped) crings = rings(clipped) @test all(vertices(crings[1]) .≈ vertices(other)) @@ -67,7 +67,7 @@ inner₂ = Ring(cart(2, 5), cart(2, 6), cart(3, 5)) poly = PolyArea([outer, inner₁, inner₂]) other = PolyArea(Ring(cart(6, 1), cart(7, 2), cart(6, 5), cart(0, 2), cart(1, 1))) - clipped = clip(poly, other, SutherlandHodgman()) + clipped = clip(poly, other, SutherlandHodgmanClipping()) crings = rings(clipped) @test !issimple(clipped) @test length(crings) == 2 diff --git a/test/refinement.jl b/test/refinement.jl index 6b4a85944..02896e365 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -73,9 +73,9 @@ end points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) + ref1 = refine(mesh, CatmullClarkRefinement()) + ref2 = refine(ref1, CatmullClarkRefinement()) + ref3 = refine(ref2, CatmullClarkRefinement()) if visualtests fig = Mke.Figure(size=(900, 300)) @@ -88,9 +88,9 @@ end points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.25, 0.25), (0.75, 0.25), (0.5, 0.75)]) connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) + ref1 = refine(mesh, CatmullClarkRefinement()) + ref2 = refine(ref1, CatmullClarkRefinement()) + ref3 = refine(ref2, CatmullClarkRefinement()) if visualtests fig = Mke.Figure(size=(900, 300)) @@ -103,9 +103,9 @@ end points = cart.([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]) connec = connect.([(1, 4, 3, 2), (5, 6, 7, 8), (1, 2, 6, 5), (3, 4, 8, 7), (1, 5, 8, 4), (2, 3, 7, 6)]) mesh = SimpleMesh(points, connec) - ref1 = refine(mesh, CatmullClark()) - ref2 = refine(ref1, CatmullClark()) - ref3 = refine(ref2, CatmullClark()) + ref1 = refine(mesh, CatmullClarkRefinement()) + ref2 = refine(ref1, CatmullClarkRefinement()) + ref3 = refine(ref2, CatmullClarkRefinement()) if visualtests fig = Mke.Figure(size=(900, 300)) @@ -119,7 +119,7 @@ end points = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)]) mesh = SimpleMesh(points, connec) - ref = refine(mesh, CatmullClark()) + ref = refine(mesh, CatmullClarkRefinement()) @test crs(ref) === crs(mesh) end From df7927bc0a7ba02358efc622a1e6d8fff77dad41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 28 Aug 2024 15:13:25 -0300 Subject: [PATCH 297/423] Update Project.toml (#1028) --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 679feb358..74af2a495 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.49.5" +version = "0.50.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.11" +CoordRefSystems = "0.12" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" From 3ceb0792845488a4503859004b732c7d03727743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 28 Aug 2024 15:57:46 -0300 Subject: [PATCH 298/423] Fix rotation of Ellipsoid (#1029) * Fix rotation of Ellipsoid * Update test/primitives.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/transforms/rotate.jl | 2 ++ test/primitives.jl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 76dd10f73..6460b37a6 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -71,6 +71,8 @@ applycoord(t::Rotate, v::Vec) = urotapply(t.rot, v) applycoord(t::Rotate, b::Box) = TransformedGeometry(b, t) +applycoord(t::Rotate, e::Ellipsoid) = Ellipsoid(radii(e), center(e), t.rot * rotation(e)) + applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) applycoord(t::Rotate, g::RectilinearGrid) = TransformedGrid(g, t) diff --git a/test/primitives.jl b/test/primitives.jl index 59b2889bd..7419ad430 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -701,6 +701,10 @@ end equaltest(e) isapproxtest(e) + e = Ellipsoid((T(3), T(2), T(1))) + R = RotXYZ(T(π / 4), T(π / 5), T(π / 3)) + @test rotation(e |> Rotate(R)) == R + e = Ellipsoid((T(3), T(2), T(1))) @test sprint(show, e) == "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" From 0151261fafdd061682368785160eeeb843a6a53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 28 Aug 2024 15:58:06 -0300 Subject: [PATCH 299/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 74af2a495..3aaede274 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.50.0" +version = "0.50.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 14f7ce97188a13677b75530cc7fd51b0e3af9fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 28 Aug 2024 18:00:52 -0300 Subject: [PATCH 300/423] Fix Rotate of Ellipsoid (#1030) * Fix Rotate of Ellipsoid * Improve test --- src/transforms/rotate.jl | 2 +- test/primitives.jl | 4 ---- test/transforms.jl | 11 +++++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 6460b37a6..3323cb2bb 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -71,7 +71,7 @@ applycoord(t::Rotate, v::Vec) = urotapply(t.rot, v) applycoord(t::Rotate, b::Box) = TransformedGeometry(b, t) -applycoord(t::Rotate, e::Ellipsoid) = Ellipsoid(radii(e), center(e), t.rot * rotation(e)) +applycoord(t::Rotate, e::Ellipsoid) = Ellipsoid(radii(e), applycoord(t, center(e)), t.rot * rotation(e)) applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) diff --git a/test/primitives.jl b/test/primitives.jl index 7419ad430..59b2889bd 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -701,10 +701,6 @@ end equaltest(e) isapproxtest(e) - e = Ellipsoid((T(3), T(2), T(1))) - R = RotXYZ(T(π / 4), T(π / 5), T(π / 3)) - @test rotation(e |> Rotate(R)) == R - e = Ellipsoid((T(3), T(2), T(1))) @test sprint(show, e) == "Ellipsoid(radii: (3.0 m, 2.0 m, 1.0 m), center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), rotation: UniformScaling{Bool}(true))" diff --git a/test/transforms.jl b/test/transforms.jl index 17f1048d7..c5dace287 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -136,6 +136,17 @@ @test r ≈ Cylinder(cart(0, 0, 0), cart(1, 0, 0)) @test TB.revert(f, r, c) ≈ g + # ---------- + # ELLIPSOID + # ---------- + + R = RotXYZ(T(π / 4), T(π / 5), T(π / 3)) + f = Rotate(R) + g = Ellipsoid((T(3), T(2), T(1)), (T(1), T(1), T(1))) + r, c = TB.apply(f, g) + @test center(r) == center(g) |> Rotate(R) + @test rotation(r) == R + # --------- # POINTSET # --------- From b8d9d4d3a6172bcff89946dafa7da7350c4f9717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 28 Aug 2024 18:01:23 -0300 Subject: [PATCH 301/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3aaede274..857ff3c36 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.50.1" +version = "0.50.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 86c8c19acb274aa09c367f34e042d11691767890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 29 Aug 2024 10:01:28 -0300 Subject: [PATCH 302/423] Improve Rotate docs (#1031) * Improve Rotate docs * Fix docs --- docs/src/transforms.md | 6 +++--- src/transforms/rotate.jl | 45 ++++++++++++++-------------------------- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index caeb0c56e..faf8de5a0 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -30,11 +30,9 @@ Rotate ``` ```@example transforms -using Rotations: Angle2d - grid = CartesianGrid(10, 10) -mesh = grid |> Rotate(Angle2d(π/4)) +mesh = grid |> Rotate(π/4) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) @@ -83,6 +81,8 @@ Affine ``` ```@example transforms +using Rotations: Angle2d + grid = CartesianGrid(10, 10) mesh = grid |> Affine(Angle2d(π/4), [10., 20.]) diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 3323cb2bb..6c92a97be 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -3,52 +3,39 @@ # ------------------------------------------------------------------ """ - Rotate(rot) + Rotate(R) -Rotate geometry or mesh with rotation `rot` -from Rotations.jl. +Rotate geometry or domain with rotation `R` from Rotations.jl. -## Examples - -```julia -Rotate(one(RotXYZ{Float64})) # Generate identity rotation -Rotate(AngleAxis(0.2, 1.0, 0.0, 0.0)) # Rotate 0.2 radians around X-axis -Rotate(rand(QuatRotation{Float64})) # Generate random rotation -``` -""" -struct Rotate{R<:Rotation} <: CoordinateTransform - rot::R -end - -""" Rotate(u, v) Rotation mapping the axis directed by `u` to the axis directed by `v`. More precisely, it maps the plane passing through the origin with normal vector `u` to the plane passing through the origin with normal vector `v`. + Rotate(θ) + +Rotate the 2D geometry or domain by angle `θ`, in radians, using the +`Angle2d` rotation. + ## Examples ```julia -Rotate(Vec(1, 0, 0), Vec(1, 1, 1)) +Rotate(one(RotXYZ{Float64})) # identity rotation +Rotate(AngleAxis(0.2, 1.0, 0.0, 0.0)) # rotate 0.2 radians around X axis +Rotate(rand(QuatRotation{Float64})) # random rotation +Rotate(Vec(1, 0, 0), Vec(1, 1, 1)) # rotation from (1, 0, 0) to (1, 1, 1) +Rotate(π / 2) # 2D rotation with angle in radians ``` """ +struct Rotate{R<:Rotation} <: CoordinateTransform + rot::R +end + Rotate(u::Vec, v::Vec) = Rotate(urotbetween(u, v)) Rotate(u::Tuple, v::Tuple) = Rotate(Vec(u), Vec(v)) -""" - Rotate(θ) - -Rotate the 2D geometry or mesh by angle `θ`, in radians, -using the `Angle2d` rotation. - -## Examples - -```julia -Rotate(π / 2) -``` -""" Rotate(θ) = Rotate(Angle2d(θ)) parameters(t::Rotate) = (; rot=t.rot) From 1196bdce48f167104cdfd81c7055b48ec612a725 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:32:14 -0300 Subject: [PATCH 303/423] =?UTF-8?q?Implement=20promote=20for=20Points=20&?= =?UTF-8?q?=20Handle=20-180=C2=B0=20=3D=3D=20180=C2=B0=20in=20latlon=20coo?= =?UTF-8?q?rdinates=20(#1032)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Handle -180° == 180° in latlon coordinates * Fix typo * Apply suggestions * Fix tests * Implement promote for Points * Remove unused code * Fix promote * Fix tests * Rename variables * Bump version --- Project.toml | 2 +- src/geometries/primitives/point.jl | 25 +++++++++++++++--- test/primitives.jl | 42 +++++++++++++++++++++++------- test/transforms.jl | 2 +- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index 857ff3c36..b7d46dada 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.12" +CoordRefSystems = "0.13.1" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/src/geometries/primitives/point.jl b/src/geometries/primitives/point.jl index 89b006b8a..1c0e57001 100644 --- a/src/geometries/primitives/point.jl +++ b/src/geometries/primitives/point.jl @@ -44,15 +44,34 @@ Point(coords::CRS) = Point{_manifold(coords)}(coords) # convenience constructor Point(coords...) = Point(Cartesian(coords...)) -# conversions +# conversion Base.convert(::Type{Point{M,CRSₜ}}, p::Point{M,CRSₛ}) where {M,CRSₜ,CRSₛ} = Point{M}(convert(CRSₜ, p.coords)) Base.convert(::Type{Point{M,CRS}}, p::Point{M,CRS}) where {M,CRS} = p +# promotion +function Base.promote(A::Point, B::Point) + a, b = promote(coords(A), coords(B)) + Point(a), Point(b) +end + paramdim(::Type{<:Point}) = 0 -==(A::Point, B::Point) = to(A) == to(B) +function ==(A::Point, B::Point) + A′, B′ = promote(A, B) + to(A′) == to(B′) +end + +function ==(A::Point{🌐,<:LatLon}, B::Point{🌐,<:LatLon}) + A′, B′ = promote(A, B) + lat₁, lon₁ = A′.coords.lat, A′.coords.lon + lat₂, lon₂ = B′.coords.lat, B′.coords.lon + lat₁ == lat₂ && lon₁ == lon₂ || (abs(lon₁) == 180u"°" && lon₁ == -lon₂) +end -Base.isapprox(A::Point, B::Point; atol=atol(lentype(A)), kwargs...) = isapprox(to(A), to(B); atol, kwargs...) +function Base.isapprox(A::Point, B::Point; atol=atol(lentype(A)), kwargs...) + A′, B′ = promote(A, B) + isapprox(to(A′), to(B′); atol, kwargs...) +end """ coords(point) diff --git a/test/primitives.jl b/test/primitives.jl index 59b2889bd..2deac77dc 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -2,6 +2,9 @@ @test embeddim(Point(1)) == 1 @test embeddim(Point(1, 2)) == 2 @test embeddim(Point(1, 2, 3)) == 3 + @test paramdim(Point(1)) == 0 + @test paramdim(Point(1, 2)) == 0 + @test paramdim(Point(1, 2, 3)) == 0 @test crs(cart(1, 1)) <: Cartesian{NoDatum} @test crs(Point(Polar(T(√2), T(π / 4)))) <: Polar{NoDatum} @test crs(Point(Cylindrical(T(√2), T(π / 4), T(1)))) <: Cylindrical{NoDatum} @@ -11,6 +14,26 @@ @test Meshes.lentype(Point((T(1), T(1)))) == ℳ @test Meshes.lentype(Point(T(1), T(1))) == ℳ + # conversion + P = typeof(cart(1, 1)) + p1 = Point(1.0, 1.0) + p2 = convert(P, p1) + @test p2 isa P + p1 = Point(1.0f0, 1.0f0) + p2 = convert(P, p1) + @test p2 isa P + + # promotion + p1 = Point(T(1), T(1)) + p2 = Point(1.0, 1.0) + p3 = Point(1.0f0, 1.0f0) + ps = promote(p1, p2) + @test allequal(Meshes.lentype.(ps)) + @test Meshes.lentype(first(ps)) == Meshes.Met{Float64} + ps = promote(p1, p3) + @test allequal(Meshes.lentype.(ps)) + @test Meshes.lentype(first(ps)) == Meshes.Met{T} + equaltest(cart(1)) equaltest(cart(1, 2)) equaltest(cart(1, 2, 3)) @@ -18,6 +41,16 @@ isapproxtest(cart(1, 2)) isapproxtest(cart(1, 2, 3)) + # different datums + p1 = Point(Cartesian{WGS84{1762}}(T(1), T(1), T(1))) + p2 = Point(Cartesian{ITRF{2008}}(T(1), T(1), T(1))) + @test p1 == p2 + @test p1 ≈ p2 + + # latlon special cases + @test latlon(45, 180) == latlon(45, -180) + @test latlon(45, -180) == latlon(45, 180) + @test to(cart(1)) == vector(1) @test to(cart(1, 2)) == vector(1, 2) @test to(cart(1, 2, 3)) == vector(1, 2, 3) @@ -89,15 +122,6 @@ @test unit(Meshes.lentype(p)) == u"m" @test Unitful.numtype(Meshes.lentype(p)) === Float32 - # conversions - P = typeof(cart(1, 1)) - p1 = Point(1.0, 1.0) - p2 = convert(P, p1) - @test p2 isa P - p1 = Point(1.0f0, 1.0f0) - p2 = convert(P, p1) - @test p2 isa P - # centroid @test centroid(cart(1, 1)) == cart(1, 1) diff --git a/test/transforms.jl b/test/transforms.jl index c5dace287..db0b2d694 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1129,7 +1129,7 @@ end g = latlon(0, 0) r, c = TB.apply(f, g) @test manifold(r) === 🌐 - @test r ≈ cart(6378137.0, 0, 0) + @test r ≈ Point(Cartesian{WGS84Latest}(T(6378137.0), T(0), T(0))) # -------- # SEGMENT From fc8279b4eee6f7d8b6d20a9e777ae675a568345b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 29 Aug 2024 17:32:31 -0300 Subject: [PATCH 304/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b7d46dada..cdf0a120b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.50.2" +version = "0.50.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From e24e5acff709015fbb242df9e8cd273e83e3017b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 30 Aug 2024 14:06:50 -0300 Subject: [PATCH 305/423] Refactor viewing.jl (#1035) --- src/viewing.jl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/viewing.jl b/src/viewing.jl index 8987216e3..9daed4ce2 100644 --- a/src/viewing.jl +++ b/src/viewing.jl @@ -28,7 +28,11 @@ that intersect with the `geometry`. """ indices(domain::Domain, geometry::Geometry) = findall(intersects(geometry), domain) -function indices(grid::Grid, point::Point) +# -------------- +# OPTIMIZATIONS +# -------------- + +function indices(grid::CartesianGrid, point::Point) point ∉ grid && return Int[] # grid properties @@ -46,7 +50,7 @@ function indices(grid::Grid, point::Point) [LinearIndices(dims)[coords...]] end -function indices(grid::Grid, chain::Chain) +function indices(grid::CartesianGrid, chain::Chain) dims = size(grid) mask = falses(dims) @@ -55,11 +59,10 @@ function indices(grid::Grid, chain::Chain) _bresenham!(mask, grid, true, p₁, p₂) end - # convert to linear indices LinearIndices(dims)[mask] end -function indices(grid::Grid, poly::Polygon) +function indices(grid::CartesianGrid, poly::Polygon) dims = size(grid) mask = zeros(Int, dims) cpoly = poly ∩ boundingbox(grid) @@ -69,7 +72,6 @@ function indices(grid::Grid, poly::Polygon) _fill!(mask, grid, i, triangle) end - # convert to linear indices LinearIndices(dims)[mask .> 0] end @@ -93,9 +95,12 @@ function indices(grid::CartesianGrid, box::Box) LinearIndices(sz)[range] |> vec end -indices(domain::Domain, multi::Multi) = mapreduce(geom -> indices(domain, geom), vcat, parent(multi)) |> unique +indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique + +# ----------------- +# HELPER FUNCTIONS +# ----------------- -# utils function _fill!(mask, grid, val, triangle) v = vertices(triangle) From 090835fbf5dd1d2184b2b5d152b4b3aa57db3515 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:15:27 -0300 Subject: [PATCH 306/423] Fix boundary of `Box` (#1036) * Fix boundary of 'Box' * Fix tests * Apply suggestions --- src/boundary.jl | 49 ++++++++++++++++++++++++++-------------------- test/primitives.jl | 7 +++++-- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/boundary.jl b/src/boundary.jl index 1321dca11..f82996cd8 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -23,35 +23,42 @@ end boundary(::Plane) = nothing -boundary(b::Box) = _boundary(b, Val(embeddim(b))) +boundary(b::Box{𝔼{1}}) = Multi([minimum(b), maximum(b)]) -_boundary(b::Box, ::Val{1}) = Multi([minimum(b), maximum(b)]) - -function _boundary(b::Box, ::Val{2}) - A = to(minimum(b)) - B = to(maximum(b)) - v = Point.([(A[1], A[2]), (B[1], A[2]), (B[1], B[2]), (A[1], B[2])]) +function boundary(b::Box{𝔼{2}}) + A = convert(Cartesian, coords(minimum(b))) + B = convert(Cartesian, coords(maximum(b))) + point(x, y) = Point{𝔼{2}}(convert(crs(b), Cartesian{datum(crs(b))}(x, y))) + v = [point(A.x, A.y), point(B.x, A.y), point(B.x, B.y), point(A.x, B.y)] Ring(v) end -function _boundary(b::Box, ::Val{3}) - A = to(minimum(b)) - B = to(maximum(b)) - v = - Point.([ - (A[1], A[2], A[3]), - (B[1], A[2], A[3]), - (B[1], B[2], A[3]), - (A[1], B[2], A[3]), - (A[1], A[2], B[3]), - (B[1], A[2], B[3]), - (B[1], B[2], B[3]), - (A[1], B[2], B[3]) - ]) +function boundary(b::Box{𝔼{3}}) + A = convert(Cartesian, coords(minimum(b))) + B = convert(Cartesian, coords(maximum(b))) + point(x, y, z) = Point{𝔼{3}}(convert(crs(b), Cartesian{datum(crs(b))}(x, y, z))) + v = [ + point(A.x, A.y, A.z), + point(B.x, A.y, A.z), + point(B.x, B.y, A.z), + point(A.x, B.y, A.z), + point(A.x, A.y, B.z), + point(B.x, A.y, B.z), + point(B.x, B.y, B.z), + point(A.x, B.y, B.z) + ] c = [(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)] SimpleMesh(v, connect.(c)) end +function boundary(b::Box{🌐}) + A = convert(LatLon, coords(minimum(b))) + B = convert(LatLon, coords(maximum(b))) + point(lat, lon) = Point{🌐}(convert(crs(b), LatLon{datum(crs(b))}(lat, lon))) + v = [point(A.lat, A.lon), point(A.lat, B.lon), point(B.lat, B.lon), point(B.lat, A.lon)] + Ring(v) +end + boundary(b::Ball) = Sphere(center(b), radius(b)) boundary(::Sphere) = nothing diff --git a/test/primitives.jl b/test/primitives.jl index 2deac77dc..b5a18802a 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -44,8 +44,8 @@ # different datums p1 = Point(Cartesian{WGS84{1762}}(T(1), T(1), T(1))) p2 = Point(Cartesian{ITRF{2008}}(T(1), T(1), T(1))) - @test p1 == p2 - @test p1 ≈ p2 + @test_broken p1 == p2 + @test_broken p1 ≈ p2 # latlon special cases @test latlon(45, 180) == latlon(45, -180) @@ -460,6 +460,9 @@ end b = Box(cart(0, 0), cart(1, 1)) @test boundary(b) == Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + b = Box(latlon(0, 0), latlon(1, 1)) + @test boundary(b) == Ring(latlon.([(0, 0), (0, 1), (1, 1), (1, 0)])) + b = Box(cart(0, 0, 0), cart(1, 1, 1)) m = boundary(b) @test m isa Mesh From ae91ec76a6ea98cb52c6a0a9dcd396aee96a1bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 30 Aug 2024 18:15:44 -0300 Subject: [PATCH 307/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cdf0a120b..962cd8ac9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.50.3" +version = "0.50.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 6fc68a74908a91eb157b871447727af64312e700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 31 Aug 2024 08:09:46 -0300 Subject: [PATCH 308/423] Specialize collectat for AbstractVector --- src/utils/basic.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/basic.jl b/src/utils/basic.jl index 18b3909ed..d80298a07 100644 --- a/src/utils/basic.jl +++ b/src/utils/basic.jl @@ -21,7 +21,7 @@ end """ collectat(iter, inds) -Collect iterator `iter` at indices `inds` without materialization. +Collect iterable `iter` at indices `inds` efficiently. """ function collectat(iter, inds) if isempty(inds) @@ -35,6 +35,8 @@ function collectat(iter, inds) end end +collectat(vec::AbstractVector, inds) = vec[inds] + """ XYZ(xyz) From 0324c33f1db3ffb1b9ca20d78fa98f40eec8cd2b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:17:10 -0300 Subject: [PATCH 309/423] Fix tests (#1038) --- Project.toml | 2 +- test/primitives.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 962cd8ac9..61d2d1966 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1" -CoordRefSystems = "0.13.1" +CoordRefSystems = "0.13.4" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" diff --git a/test/primitives.jl b/test/primitives.jl index b5a18802a..e703aa642 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -44,8 +44,8 @@ # different datums p1 = Point(Cartesian{WGS84{1762}}(T(1), T(1), T(1))) p2 = Point(Cartesian{ITRF{2008}}(T(1), T(1), T(1))) - @test_broken p1 == p2 - @test_broken p1 ≈ p2 + @test p1 == p2 + @test p1 ≈ p2 # latlon special cases @test latlon(45, 180) == latlon(45, -180) From 53346e678c70a360a4ac706af90c8b01c8780967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 3 Sep 2024 10:56:29 -0300 Subject: [PATCH 310/423] Refactor domain indices (#1037) * Refactor domain indices * Remove 'indices(::Mesh, ::Polygon)' method * Add tests --------- Co-authored-by: Elias Carvalho --- src/Meshes.jl | 6 +-- src/domains.jl | 4 ++ src/{viewing.jl => indices.jl} | 50 +++++++++--------- test/{viewing.jl => indices.jl} | 92 ++++++++++++++++++++++++++++++--- test/meshes.jl | 8 +++ 5 files changed, 125 insertions(+), 35 deletions(-) rename src/{viewing.jl => indices.jl} (85%) rename test/{viewing.jl => indices.jl} (66%) diff --git a/src/Meshes.jl b/src/Meshes.jl index 2d2064416..13260951b 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -78,8 +78,8 @@ include("domains.jl") # utilities include("utils.jl") -# domain views -include("viewing.jl") +# domain indices +include("indices.jl") # domain partitions include("partitions.jl") @@ -323,7 +323,7 @@ export # trajectories CylindricalTrajectory, - # viewing + # indices indices, # partitions diff --git a/src/domains.jl b/src/domains.jl index 26996a5a2..ca4eaa8c5 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -58,6 +58,10 @@ Base.vcat(d1::Domain, d2::Domain) = GeometrySet(vcat(collect(d1), collect(d2))) Base.vcat(ds::Domain...) = reduce(vcat, ds) +Base.view(domain::Domain, inds::AbstractVector{Int}) = SubDomain(domain, inds) + +Base.view(domain::Domain, geometry::Geometry) = view(domain, indices(domain, geometry)) + """ embeddim(domain) diff --git a/src/viewing.jl b/src/indices.jl similarity index 85% rename from src/viewing.jl rename to src/indices.jl index 9daed4ce2..05a47d9ef 100644 --- a/src/viewing.jl +++ b/src/indices.jl @@ -2,36 +2,13 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -# ------------------- -# VIEWS WITH INDICES -# ------------------- - -Base.view(domain::Domain, inds::AbstractVector{Int}) = SubDomain(domain, inds) - -# ---------------------- -# VIEWS WITH GEOMETRIES -# ---------------------- - -""" - view(domain, geometry) - -Return a view of the `domain` containing all elements that -intersect with the `geometry`. -""" -Base.view(domain::Domain, geometry::Geometry) = view(domain, indices(domain, geometry)) - """ indices(domain, geometry) -Return the indices of the elements of the `domain` -that intersect with the `geometry`. +Return the indices of the elements of the `domain` that intersect with the `geometry`. """ indices(domain::Domain, geometry::Geometry) = findall(intersects(geometry), domain) -# -------------- -# OPTIMIZATIONS -# -------------- - function indices(grid::CartesianGrid, point::Point) point ∉ grid && return Int[] @@ -97,6 +74,31 @@ end indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique +function indices(grid::RectilinearGrid, box::Box) + # grid properties + sz = size(grid) + nd = length(sz) + + # intersection of boxes + lo, up = to.(extrema(boundingbox(grid) ∩ box)) + + # integer coordinates of lower point + ilo = ntuple(nd) do i + findlast(x -> x ≤ lo[i], xyz(grid)[i]) + end + + # integer coordinates of upper point + iup = ntuple(nd) do i + findfirst(x -> x ≥ up[i], xyz(grid)[i]) + end + + # integer coordinates of elements + range = CartesianIndex(ilo):CartesianIndex(iup .- 1) + + # convert to linear indices + LinearIndices(sz)[range] |> vec +end + # ----------------- # HELPER FUNCTIONS # ----------------- diff --git a/test/viewing.jl b/test/indices.jl similarity index 66% rename from test/viewing.jl rename to test/indices.jl index 91c856316..18644e6f7 100644 --- a/test/viewing.jl +++ b/test/indices.jl @@ -1,11 +1,4 @@ -@testitem "Viewing" setup = [Setup] begin - g = cartgrid(10, 10) - v = view(g, 1:3) - @test parent(v) == g - @test parentindices(v) == 1:3 - @test parent(g) == g - @test parentindices(g) == 1:100 - +@testitem "Indices" setup = [Setup] begin g = cartgrid(10, 10) b = Box(cart(1, 1), cart(5, 5)) v = view(g, b) @@ -16,6 +9,7 @@ @test centroid(v, 1) == cart(1, 1) @test centroid(v, nelements(v)) == cart(5, 5) + # boxes g = cartgrid(10, 10) p = PointSet(collect(vertices(g))) b = Ball(cart(0, 0), T(2)) @@ -26,6 +20,88 @@ @test nelements(v) == 6 @test to.(v) == vector.([(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)]) + b1 = Box(cart(0.6, 0.7), cart(1.0, 1.0)) + b2 = Box(cart(0.6, 0.7), cart(1.2, 1.2)) + b3 = Box(cart(0.0, 0.0), cart(0.6, 0.3)) + b4 = Box(cart(-0.2, -0.2), cart(0.6, 0.3)) + b5 = Box(cart(0.25, 0.15), cart(0.95, 0.85)) + b6 = Box(cart(0.35, 0.25), cart(0.85, 0.75)) + b7 = Box(cart(0.05, 0.05), cart(0.65, 0.35)) + b8 = Box(cart(0.55, 0.65), cart(0.95, 0.95)) + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + g = RectilinearGrid(x, y) + linds = LinearIndices(size(g)) + @test issetequal(indices(g, b1), [linds[4, 4], linds[5, 4], linds[4, 5], linds[5, 5]]) + @test issetequal(indices(g, b2), [linds[4, 4], linds[5, 4], linds[4, 5], linds[5, 5]]) + @test issetequal(indices(g, b3), [linds[1, 1], linds[2, 1], linds[3, 1], linds[1, 2], linds[2, 2], linds[3, 2]]) + @test issetequal(indices(g, b4), [linds[1, 1], linds[2, 1], linds[3, 1], linds[1, 2], linds[2, 2], linds[3, 2]]) + @test issetequal( + indices(g, b5), + [ + linds[2, 2], + linds[3, 2], + linds[4, 2], + linds[5, 2], + linds[2, 3], + linds[3, 3], + linds[4, 3], + linds[5, 3], + linds[2, 4], + linds[3, 4], + linds[4, 4], + linds[5, 4] + ] + ) + @test issetequal( + indices(g, b6), + [ + linds[2, 2], + linds[3, 2], + linds[4, 2], + linds[5, 2], + linds[2, 3], + linds[3, 3], + linds[4, 3], + linds[5, 3], + linds[2, 4], + linds[3, 4], + linds[4, 4], + linds[5, 4] + ] + ) + @test issetequal( + indices(g, b7), + [ + linds[1, 1], + linds[2, 1], + linds[3, 1], + linds[4, 1], + linds[1, 2], + linds[2, 2], + linds[3, 2], + linds[4, 2], + linds[1, 3], + linds[2, 3], + linds[3, 3], + linds[4, 3] + ] + ) + @test issetequal( + indices(g, b8), + [ + linds[3, 3], + linds[4, 3], + linds[5, 3], + linds[3, 4], + linds[4, 4], + linds[5, 4], + linds[3, 5], + linds[4, 5], + linds[5, 5] + ] + ) + # convex polygons tri = Triangle(cart(5, 7), cart(10, 12), cart(15, 7)) pent = Pentagon(cart(6, 1), cart(2, 10), cart(10, 16), cart(18, 10), cart(14, 1)) diff --git a/test/meshes.jl b/test/meshes.jl index af0143070..30f94da94 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -228,6 +228,14 @@ @test unit(eltype(s)) == u"m" @test Unitful.numtype(eltype(s)) === T + # views + grid = cartgrid(10, 10) + vgrid = view(grid, 1:3) + @test parent(vgrid) == grid + @test parentindices(vgrid) == 1:3 + @test parent(grid) == grid + @test parentindices(grid) == 1:100 + grid = cartgrid(200, 100) if T == Float32 @test sprint(show, MIME"text/plain"(), grid) == """ From e14655628839db8afb62471473889204729dab2b Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:36:58 -0300 Subject: [PATCH 311/423] Add 'Crop' methods for 'CartesianGrid' and 'RectilinearGrid' (#1039) --- src/indices.jl | 40 +++++++++++++++++++++++++--------------- src/transforms/crop.jl | 18 +++++++++++++++--- test/transforms.jl | 2 ++ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/indices.jl b/src/indices.jl index 05a47d9ef..5bfee3f17 100644 --- a/src/indices.jl +++ b/src/indices.jl @@ -53,6 +53,28 @@ function indices(grid::CartesianGrid, poly::Polygon) end function indices(grid::CartesianGrid, box::Box) + # cartesian range + range = cartesianrange(grid, box) + + # convert to linear indices + LinearIndices(size(grid))[range] |> vec +end + +indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique + +function indices(grid::RectilinearGrid, box::Box) + # cartesian range + range = cartesianrange(grid, box) + + # convert to linear indices + LinearIndices(size(grid))[range] |> vec +end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function cartesianrange(grid::CartesianGrid, box::Box) # grid properties or = minimum(grid) sp = spacing(grid) @@ -66,15 +88,10 @@ function indices(grid::CartesianGrid, box::Box) iup = min.(floor.(Int, (up - or) ./ sp) .+ 1, sz) # Cartesian range from corner to corner - range = CartesianIndex(Tuple(ilo)):CartesianIndex(Tuple(iup)) - - # convert to linear indices - LinearIndices(sz)[range] |> vec + CartesianIndex(Tuple(ilo)):CartesianIndex(Tuple(iup)) end -indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique - -function indices(grid::RectilinearGrid, box::Box) +function cartesianrange(grid::RectilinearGrid, box::Box) # grid properties sz = size(grid) nd = length(sz) @@ -93,16 +110,9 @@ function indices(grid::RectilinearGrid, box::Box) end # integer coordinates of elements - range = CartesianIndex(ilo):CartesianIndex(iup .- 1) - - # convert to linear indices - LinearIndices(sz)[range] |> vec + CartesianIndex(ilo):CartesianIndex(iup .- 1) end -# ----------------- -# HELPER FUNCTIONS -# ----------------- - function _fill!(mask, grid, val, triangle) v = vertices(triangle) diff --git a/src/transforms/crop.jl b/src/transforms/crop.jl index 9581790b9..fb193d0a2 100644 --- a/src/transforms/crop.jl +++ b/src/transforms/crop.jl @@ -33,15 +33,27 @@ function preprocess(t::Crop, d::Domain) bbox₁ = _overlaps(1, t.x, bbox) bbox₂ = _overlaps(2, t.y, bbox₁) bbox₃ = _overlaps(3, t.z, bbox₂) - indices(d, bbox₃) + bbox₃ end function apply(t::Crop, d::Domain) - inds = preprocess(t, d) - n = view(d, inds) + box = preprocess(t, d) + n = view(d, box) n, nothing end +function apply(t::Crop, g::CartesianGrid) + box = preprocess(t, g) + range = cartesianrange(g, box) + g[range], nothing +end + +function apply(t::Crop, g::RectilinearGrid) + box = preprocess(t, g) + range = cartesianrange(g, box) + g[range], nothing +end + _aslen(x::Len) = float(x) _aslen(x::Number) = float(x) * u"m" _aslen(::Quantity) = throw(ArgumentError("invalid units, please check the documentation")) diff --git a/test/transforms.jl b/test/transforms.jl index db0b2d694..310f9cac3 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1684,6 +1684,7 @@ end f = Crop(z=(T(1.5), T(4.5))) d = cartgrid(10, 10, 10) r, c = TB.apply(f, d) + @test r isa CartesianGrid @test r == CartesianGrid((10, 10, 4), cart(0, 0, 1), T.((1, 1, 1))) # ---------------- @@ -1693,6 +1694,7 @@ end f = Crop(y=(T(3.5), T(6.5))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) + @test r isa RectilinearGrid @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) # --------------- From 2ac024ca62a0acaa9849b546c6569660036b129a Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:25:19 -0300 Subject: [PATCH 312/423] `Crop`: Add support for latlon (#1040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 'Crop': Add support for latlon * Update the implementation * Update the implementation * Apply suggestions * Rename variables * Apply suggestions * Update docstring * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Fix 'withcrs' * Apply suggestions * Rename arguments * Fix tests * Update src/utils/crs.jl Co-authored-by: Júlio Hoffimann * Fix code * Add more tests * Update src/utils/crs.jl --------- Co-authored-by: Júlio Hoffimann --- src/boundary.jl | 28 +++++++------- src/transforms/crop.jl | 86 ++++++++++++++++++++++++++++-------------- src/units.jl | 1 + src/utils/crs.jl | 20 +++++++--- test/transforms.jl | 9 +++-- test/utils.jl | 10 +++++ 6 files changed, 103 insertions(+), 51 deletions(-) diff --git a/src/boundary.jl b/src/boundary.jl index f82996cd8..dfbd3cee9 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -28,24 +28,22 @@ boundary(b::Box{𝔼{1}}) = Multi([minimum(b), maximum(b)]) function boundary(b::Box{𝔼{2}}) A = convert(Cartesian, coords(minimum(b))) B = convert(Cartesian, coords(maximum(b))) - point(x, y) = Point{𝔼{2}}(convert(crs(b), Cartesian{datum(crs(b))}(x, y))) - v = [point(A.x, A.y), point(B.x, A.y), point(B.x, B.y), point(A.x, B.y)] + v = [withcrs(b, (A.x, A.y)), withcrs(b, (B.x, A.y)), withcrs(b, (B.x, B.y)), withcrs(b, (A.x, B.y))] Ring(v) end function boundary(b::Box{𝔼{3}}) A = convert(Cartesian, coords(minimum(b))) B = convert(Cartesian, coords(maximum(b))) - point(x, y, z) = Point{𝔼{3}}(convert(crs(b), Cartesian{datum(crs(b))}(x, y, z))) v = [ - point(A.x, A.y, A.z), - point(B.x, A.y, A.z), - point(B.x, B.y, A.z), - point(A.x, B.y, A.z), - point(A.x, A.y, B.z), - point(B.x, A.y, B.z), - point(B.x, B.y, B.z), - point(A.x, B.y, B.z) + withcrs(b, (A.x, A.y, A.z)), + withcrs(b, (B.x, A.y, A.z)), + withcrs(b, (B.x, B.y, A.z)), + withcrs(b, (A.x, B.y, A.z)), + withcrs(b, (A.x, A.y, B.z)), + withcrs(b, (B.x, A.y, B.z)), + withcrs(b, (B.x, B.y, B.z)), + withcrs(b, (A.x, B.y, B.z)) ] c = [(4, 3, 2, 1), (6, 5, 1, 2), (3, 7, 6, 2), (4, 8, 7, 3), (1, 5, 8, 4), (6, 7, 8, 5)] SimpleMesh(v, connect.(c)) @@ -54,8 +52,12 @@ end function boundary(b::Box{🌐}) A = convert(LatLon, coords(minimum(b))) B = convert(LatLon, coords(maximum(b))) - point(lat, lon) = Point{🌐}(convert(crs(b), LatLon{datum(crs(b))}(lat, lon))) - v = [point(A.lat, A.lon), point(A.lat, B.lon), point(B.lat, B.lon), point(B.lat, A.lon)] + v = [ + withcrs(b, (A.lat, A.lon), LatLon), + withcrs(b, (A.lat, B.lon), LatLon), + withcrs(b, (B.lat, B.lon), LatLon), + withcrs(b, (B.lat, A.lon), LatLon) + ] Ring(v) end diff --git a/src/transforms/crop.jl b/src/transforms/crop.jl index fb193d0a2..62116cfa6 100644 --- a/src/transforms/crop.jl +++ b/src/transforms/crop.jl @@ -4,10 +4,12 @@ """ Crop(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) + Crop(lat=(latmin, latmax), lon=(lonmin, lonmax)) -Retain the domain geometries that intersect with `x` limits [`xmax`,`xmax`], +Retain the domain geometries within `x` limits [`xmax`,`xmax`], `y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units -(default to meters). +(default to meters), or within latitude limits [`latmin`,`latmax`] +and longitude limits [`lonmin`,`lonmax`] in degree units. ## Examples @@ -15,26 +17,19 @@ Retain the domain geometries that intersect with `x` limits [`xmax`,`xmax`], Crop(x=(2, 4)) Crop(x=(1u"km", 3u"km")) Crop(y=(1.2, 1.8), z=(2.4, 3.0)) +Crop(lat=(30, 60)) +Crop(lon=(45u"°", 90u"°")) ``` """ -struct Crop{X,Y,Z} <: GeometricTransform - x::X - y::Y - z::Z +struct Crop{T} <: GeometricTransform + limits::T end -Crop(; x=nothing, y=nothing, z=nothing) = - Crop(isnothing(x) ? x : _aslen.(x), isnothing(y) ? y : _aslen.(y), isnothing(z) ? z : _aslen.(z)) +Crop(; kwargs...) = Crop(values(kwargs)) -parameters(t::Crop) = (; x=t.x, y=t.y, z=t.z) +parameters(t::Crop) = (; limits=t.limits) -function preprocess(t::Crop, d::Domain) - bbox = boundingbox(d) - bbox₁ = _overlaps(1, t.x, bbox) - bbox₂ = _overlaps(2, t.y, bbox₁) - bbox₃ = _overlaps(3, t.z, bbox₂) - bbox₃ -end +preprocess(t::Crop, d::Domain) = _crop(boundingbox(d), t.limits) function apply(t::Crop, d::Domain) box = preprocess(t, d) @@ -54,20 +49,53 @@ function apply(t::Crop, g::RectilinearGrid) g[range], nothing end +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function _crop(box::Box{<:𝔼}, limits) + lims = _xyzlimits(limits) + min = convert(Cartesian, coords(minimum(box))) + max = convert(Cartesian, coords(maximum(box))) + xyzmin, xyzmax = _xyzminmax(min, max, lims) + Box(withcrs(box, xyzmin), withcrs(box, xyzmax)) +end + +function _crop(box::Box{🌐}, limits) + lims = _latlonlimits(limits) + min = convert(LatLon, coords(minimum(box))) + max = convert(LatLon, coords(maximum(box))) + latmin, latmax = isnothing(lims.lat) ? (min.lat, max.lat) : lims.lat + lonmin, lonmax = isnothing(lims.lon) ? (min.lon, max.lon) : lims.lon + Box(withcrs(box, (latmin, lonmin), LatLon), withcrs(box, (latmax, lonmax), LatLon)) +end + +_xyzlimits(limits) = ( + x=haskey(limits, :x) ? _aslen.(limits.x) : nothing, + y=haskey(limits, :y) ? _aslen.(limits.y) : nothing, + z=haskey(limits, :z) ? _aslen.(limits.z) : nothing +) + +_latlonlimits(limits) = + (lat=haskey(limits, :lat) ? _asdeg.(limits.lat) : nothing, lon=haskey(limits, :lon) ? _asdeg.(limits.lon) : nothing) + +function _xyzminmax(min::Cartesian2D, max::Cartesian2D, lims) + xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x + ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y + (xmin, ymin), (xmax, ymax) +end + +function _xyzminmax(min::Cartesian3D, max::Cartesian3D, lims) + xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x + ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y + zmin, zmax = isnothing(lims.z) ? (min.z, max.z) : lims.z + (xmin, ymin, zmin), (xmax, ymax, zmax) +end + _aslen(x::Len) = float(x) _aslen(x::Number) = float(x) * u"m" _aslen(::Quantity) = throw(ArgumentError("invalid units, please check the documentation")) -function _overlaps(dim, lims, bbox) - Dim = embeddim(bbox) - if Dim < dim || isnothing(lims) - bbox - else - lmin, lmax = lims - min = to(minimum(bbox)) - max = to(maximum(bbox)) - nmin = Vec(ntuple(i -> i == dim ? lmin : min[i], Dim)) - nmax = Vec(ntuple(i -> i == dim ? lmax : max[i], Dim)) - Box(withcrs(bbox, nmin), withcrs(bbox, nmax)) - end -end +_asdeg(x::Deg) = float(x) +_asdeg(x::Number) = float(x) * u"°" +_asdeg(::Quantity) = throw(ArgumentError("invalid units, please check the documentation")) diff --git a/src/units.jl b/src/units.jl index 87a63afd3..ff22a61b8 100644 --- a/src/units.jl +++ b/src/units.jl @@ -7,6 +7,7 @@ const Len{T} = Quantity{T,u"𝐋"} const Area{T} = Quantity{T,u"𝐋^2"} const Vol{T} = Quantity{T,u"𝐋^3"} const Met{T} = Quantity{T,u"𝐋",typeof(u"m")} +const Deg{T} = Quantity{T,NoDims,typeof(u"°")} """ addunit(x, u) diff --git a/src/utils/crs.jl b/src/utils/crs.jl index 2ee66d369..19a64dc32 100644 --- a/src/utils/crs.jl +++ b/src/utils/crs.jl @@ -3,17 +3,27 @@ # ------------------------------------------------------------------ """ - withcrs(g, v) + withcrs(g, coords, CRS=Cartesian) -Point at the end of the vector `v` with the same CRS of `g`. +Point with the same CRS of `g` from another point with `coords` in given `CRS`. """ -function withcrs(g::GeometryOrDomain, v::StaticVector) +function withcrs(g::GeometryOrDomain, coords::Tuple, ::Type{CRS}) where {CRS} + M = manifold(g) C = crs(g) D = datum(C) - c = convert(C, Cartesian{D}(v...)) - Point(c) + c = convert(C, CRS{D}(coords...)) + Point{M}(c) end +withcrs(g::GeometryOrDomain, coords::Tuple) = withcrs(g, coords, Cartesian) + +""" + withcrs(g, v) + +Point at the end of the vector `v` with the same CRS of `g`. +""" +withcrs(g::GeometryOrDomain, v::StaticVector) = withcrs(g, Tuple(v), Cartesian) + """ flat(p) diff --git a/test/transforms.jl b/test/transforms.jl index 310f9cac3..9c22ae7c6 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1651,10 +1651,11 @@ end @test !isaffine(Crop(x=(T(2), T(4)))) @test !TB.isrevertible(Crop(x=(T(2), T(4)))) @test !TB.isinvertible(Crop(x=(T(2), T(4)))) - @test TB.parameters(Crop(x=(T(2), T(4)))) == (x=(T(2) * u"m", T(4) * u"m"), y=nothing, z=nothing) - @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (x=nothing, y=(T(2) * u"km", T(4) * u"km"), z=nothing) - @test TB.parameters(Crop(z=(2, 4))) == (x=nothing, y=nothing, z=(2.0u"m", 4.0u"m")) - @test_throws ArgumentError Crop(x=(T(2) * u"°", T(4) * u"°")) + @test TB.parameters(Crop(x=(T(2), T(4)))) == (; limits=(; x=(T(2), T(4)))) + @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (; limits=(; y=(T(2) * u"km", T(4) * u"km"))) + @test TB.parameters(Crop(z=(2, 4))) == (; limits=(; z=(2, 4))) + @test TB.parameters(Crop(lat=(30, 60))) == (; limits=(; lat=(30, 60))) + @test TB.parameters(Crop(lon=(45u"°", 90u"°"))) == (; limits=(; lon=(45u"°", 90u"°"))) # --------- # POINTSET diff --git a/test/utils.jl b/test/utils.jl index 480199714..825897847 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -44,4 +44,14 @@ @inferred Meshes.intersectparameters(p1, p2, p3, p4) @inferred Meshes.intersectparameters(p1, p3, p2, p4) @inferred Meshes.intersectparameters(p1, p2, p1, p2) + + # withcrs + c = (T(1), T(1)) + p = merc(c) + v = to(p) + @inferred Meshes.withcrs(p, v) + @inferred Meshes.withcrs(p, c) + c = (T(30), T(60)) + p = latlon(c) |> Proj(Cartesian) + @inferred Meshes.withcrs(p, c, LatLon) end From 9a48342068562df202ab1a593bfa4d0da69bf054 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:06:07 -0300 Subject: [PATCH 313/423] Add `Crop` method for `Grid` (#1041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'Crop' method for 'Grid' * Add more methods * Fix code * Add more tests * Apply suggestions * Apply suggestions * Use 'vsize' * Update src/domains/meshes.jl * cleanup * Handle out-of-bounds ranges * Handle invalid limits * Apply suggestions * Remove unused code * Update src/transforms/crop.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Adjust code order * Apply suggestions * Apply suggestions * Apply suggestions * Update src/utils/misc.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions * Update tests * Apply suggestions * Apply suggestions * Apply suggestions * Fix code * Apply suggestions * cleanup indices.jl * cleanup crop.jl * cleanup misc.jl * cleanup misc.jl --------- Co-authored-by: Júlio Hoffimann Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/coarsening/regular.jl | 4 +- src/domains/meshes.jl | 15 ++- src/indices.jl | 45 ++------- src/transforms/crop.jl | 54 +++++------ src/utils/misc.jl | 194 ++++++++++++++++++++++++++++++++++++++ test/transforms.jl | 56 ++++++----- 6 files changed, 271 insertions(+), 97 deletions(-) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 49c3af0e3..85800d4a5 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -27,7 +27,7 @@ end function coarsen(grid::RectilinearGrid{Datum}, method::RegularCoarsening) where {Datum} factors = fitdims(method.factors, embeddim(grid)) - dims = size(grid) .+ .!isperiodic(grid) + dims = vsize(grid) rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) xyzₛ = xyz(grid) xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], embeddim(grid)) @@ -36,7 +36,7 @@ end function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum} factors = fitdims(method.factors, embeddim(grid)) - dims = size(grid) .+ .!isperiodic(grid) + dims = vsize(grid) rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) XYZₛ = XYZ(grid) XYZₜ = ntuple(i -> XYZₛ[i][rngs...], embeddim(grid)) diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index 1b2f4b993..af769efe0 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -152,6 +152,13 @@ Convert Cartesian index `ijk` to vertex on `grid`. """ vertex(g::Grid, ijk::CartesianIndex) = vertex(g, ijk.I) +""" + vsize(grid) + +Number of vertices along each dimension of the `grid`. +""" +vsize(g::Grid) = size(g) .+ .!isperiodic(g) + """ xyz(grid) @@ -176,19 +183,19 @@ Base.size(g::Grid) = size(topology(g)) paramdim(g::Grid) = length(size(g)) -vertex(g::Grid, ind::Int) = vertex(g, CartesianIndices(size(g) .+ 1)[ind]) +vertex(g::Grid, ind::Int) = vertex(g, CartesianIndices(vsize(g))[ind]) -vertex(g::Grid, ijk::Dims) = vertex(g, LinearIndices(size(g) .+ 1)[ijk...]) +vertex(g::Grid, ijk::Dims) = vertex(g, LinearIndices(vsize(g))[ijk...]) Base.minimum(g::Grid) = vertex(g, ntuple(i -> 1, paramdim(g))) -Base.maximum(g::Grid) = vertex(g, size(g) .+ 1) +Base.maximum(g::Grid) = vertex(g, vsize(g)) Base.extrema(g::Grid) = minimum(g), maximum(g) function element(g::Grid, ind::Int) elem = element(topology(g), ind) type = pltype(elem) einds = indices(elem) - cinds = CartesianIndices(size(g) .+ 1) + cinds = CartesianIndices(vsize(g)) verts = ntuple(i -> vertex(g, cinds[einds[i]]), nvertices(type)) type(verts) end diff --git a/src/indices.jl b/src/indices.jl index 5bfee3f17..42251953e 100644 --- a/src/indices.jl +++ b/src/indices.jl @@ -54,7 +54,7 @@ end function indices(grid::CartesianGrid, box::Box) # cartesian range - range = cartesianrange(grid, box) + range = cartesianrange(grid, _boxlimits(box)) # convert to linear indices LinearIndices(size(grid))[range] |> vec @@ -64,7 +64,7 @@ indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geo function indices(grid::RectilinearGrid, box::Box) # cartesian range - range = cartesianrange(grid, box) + range = cartesianrange(grid, _boxlimits(box)) # convert to linear indices LinearIndices(size(grid))[range] |> vec @@ -74,43 +74,14 @@ end # HELPER FUNCTIONS # ----------------- -function cartesianrange(grid::CartesianGrid, box::Box) - # grid properties - or = minimum(grid) - sp = spacing(grid) - sz = size(grid) - - # intersection of boxes - lo, up = extrema(boundingbox(grid) ∩ box) - - # Cartesian indices of new corners - ilo = max.(ceil.(Int, (lo - or) ./ sp), 1) - iup = min.(floor.(Int, (up - or) ./ sp) .+ 1, sz) - - # Cartesian range from corner to corner - CartesianIndex(Tuple(ilo)):CartesianIndex(Tuple(iup)) +function _boxlimits(box::Box{𝔼{2}}) + min, max = convert.(Cartesian, coords.(extrema(box))) + (min.x, max.x), (min.y, max.y) end -function cartesianrange(grid::RectilinearGrid, box::Box) - # grid properties - sz = size(grid) - nd = length(sz) - - # intersection of boxes - lo, up = to.(extrema(boundingbox(grid) ∩ box)) - - # integer coordinates of lower point - ilo = ntuple(nd) do i - findlast(x -> x ≤ lo[i], xyz(grid)[i]) - end - - # integer coordinates of upper point - iup = ntuple(nd) do i - findfirst(x -> x ≥ up[i], xyz(grid)[i]) - end - - # integer coordinates of elements - CartesianIndex(ilo):CartesianIndex(iup .- 1) +function _boxlimits(box::Box{𝔼{3}}) + min, max = convert.(Cartesian, coords.(extrema(box))) + (min.x, max.x), (min.y, max.y), (min.z, max.z) end function _fill!(mask, grid, val, triangle) diff --git a/src/transforms/crop.jl b/src/transforms/crop.jl index 62116cfa6..11ffa8149 100644 --- a/src/transforms/crop.jl +++ b/src/transforms/crop.jl @@ -6,10 +6,11 @@ Crop(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) Crop(lat=(latmin, latmax), lon=(lonmin, lonmax)) -Retain the domain geometries within `x` limits [`xmax`,`xmax`], -`y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units -(default to meters), or within latitude limits [`latmin`,`latmax`] -and longitude limits [`lonmin`,`lonmax`] in degree units. +Retain the grid elements within `x` limits [`xmax`,`xmax`], +`y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] +in length units (default to meters), or within `lat` limits +[`latmin`,`latmax`] and `lon` limits [`lonmin`,`lonmax`] +in degree units. ## Examples @@ -29,45 +30,26 @@ Crop(; kwargs...) = Crop(values(kwargs)) parameters(t::Crop) = (; limits=t.limits) -preprocess(t::Crop, d::Domain) = _crop(boundingbox(d), t.limits) +preprocess(t::Crop, g::Grid) = cartesianrange(g, _fixlimits(boundingbox(g), t.limits)) -function apply(t::Crop, d::Domain) - box = preprocess(t, d) - n = view(d, box) - n, nothing -end - -function apply(t::Crop, g::CartesianGrid) - box = preprocess(t, g) - range = cartesianrange(g, box) - g[range], nothing -end - -function apply(t::Crop, g::RectilinearGrid) - box = preprocess(t, g) - range = cartesianrange(g, box) - g[range], nothing -end +apply(t::Crop, g::Grid) = g[preprocess(t, g)], nothing # ----------------- # HELPER FUNCTIONS # ----------------- -function _crop(box::Box{<:𝔼}, limits) +function _fixlimits(box::Box{<:𝔼}, limits) lims = _xyzlimits(limits) min = convert(Cartesian, coords(minimum(box))) max = convert(Cartesian, coords(maximum(box))) - xyzmin, xyzmax = _xyzminmax(min, max, lims) - Box(withcrs(box, xyzmin), withcrs(box, xyzmax)) + _minmax(min, max, lims) end -function _crop(box::Box{🌐}, limits) +function _fixlimits(box::Box{🌐}, limits) lims = _latlonlimits(limits) min = convert(LatLon, coords(minimum(box))) max = convert(LatLon, coords(maximum(box))) - latmin, latmax = isnothing(lims.lat) ? (min.lat, max.lat) : lims.lat - lonmin, lonmax = isnothing(lims.lon) ? (min.lon, max.lon) : lims.lon - Box(withcrs(box, (latmin, lonmin), LatLon), withcrs(box, (latmax, lonmax), LatLon)) + _minmax(min, max, lims) end _xyzlimits(limits) = ( @@ -79,17 +61,23 @@ _xyzlimits(limits) = ( _latlonlimits(limits) = (lat=haskey(limits, :lat) ? _asdeg.(limits.lat) : nothing, lon=haskey(limits, :lon) ? _asdeg.(limits.lon) : nothing) -function _xyzminmax(min::Cartesian2D, max::Cartesian2D, lims) +function _minmax(min::Cartesian2D, max::Cartesian2D, lims) xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y - (xmin, ymin), (xmax, ymax) + (xmin, xmax), (ymin, ymax) end -function _xyzminmax(min::Cartesian3D, max::Cartesian3D, lims) +function _minmax(min::Cartesian3D, max::Cartesian3D, lims) xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y zmin, zmax = isnothing(lims.z) ? (min.z, max.z) : lims.z - (xmin, ymin, zmin), (xmax, ymax, zmax) + (xmin, xmax), (ymin, ymax), (zmin, zmax) +end + +function _minmax(min::LatLon, max::LatLon, lims) + lonmin, lonmax = isnothing(lims.lon) ? (min.lon, max.lon) : lims.lon + latmin, latmax = isnothing(lims.lat) ? (min.lat, max.lat) : lims.lat + (lonmin, lonmax), (latmin, latmax) end _aslen(x::Len) = float(x) diff --git a/src/utils/misc.jl b/src/utils/misc.jl index f10a3d786..7e4545766 100644 --- a/src/utils/misc.jl +++ b/src/utils/misc.jl @@ -104,3 +104,197 @@ function intersectparameters(a::Point, b::Point, c::Point, d::Point) λ₁, λ₂, r, rₐ end + +""" + cartesianrange(grid, limits) + +Return the Cartesian range for the elements of the +`grid` within given `limits` along each dimension. +""" +function cartesianrange(grid::CartesianGrid, limits) + # grid properties + or = minimum(grid) + sp = spacing(grid) + sz = size(grid) + nd = length(sz) + + # box from limits + bmin = withcrs(grid, ntuple(i -> first(limits[i]), nd)) + bmax = withcrs(grid, ntuple(i -> last(limits[i]), nd)) + bbox = Box(bmin, bmax) + + # intersection of boxes + lo, up = extrema(boundingbox(grid) ∩ bbox) + + # Cartesian indices of new corners + ijkₛ = max.(ceil.(Int, (lo - or) ./ sp), 1) + ijkₑ = min.(floor.(Int, (up - or) ./ sp) .+ 1, sz) + + # Cartesian range from corner to corner + CartesianIndex(Tuple(ijkₛ)):CartesianIndex(Tuple(ijkₑ)) +end + +function cartesianrange(grid::RectilinearGrid, limits) + # grid properties + sz = size(grid) + nd = length(sz) + + # box from limits + bmin = withcrs(grid, ntuple(i -> first(limits[i]), nd)) + bmax = withcrs(grid, ntuple(i -> last(limits[i]), nd)) + bbox = Box(bmin, bmax) + + # intersection of boxes + lo, up = to.(extrema(boundingbox(grid) ∩ bbox)) + + # integer coordinates of lower point + ijkₛ = ntuple(nd) do i + findlast(x -> x ≤ lo[i], xyz(grid)[i]) + end + + # integer coordinates of upper point + ijkₑ = ntuple(nd) do i + findfirst(x -> x ≥ up[i], xyz(grid)[i]) + end + + # integer coordinates of elements + CartesianIndex(ijkₛ):CartesianIndex(ijkₑ .- 1) +end + +function cartesianrange(grid::Grid{𝔼{2}}, limits) + nx, ny = vsize(grid) + + (xₛ, xₑ), (yₛ, yₑ) = limits + + a = convert(Cartesian, coords(vertex(grid, (1, 1)))) + b = convert(Cartesian, coords(vertex(grid, (nx, 1)))) + c = convert(Cartesian, coords(vertex(grid, (1, ny)))) + + xmin = max(xₛ, a.x) + ymin = max(yₛ, a.y) + xmax = min(xₑ, b.x) + ymax = min(yₑ, c.y) + + iₛ = findlast(1:nx) do i + p = vertex(grid, (i, 1)) + c = convert(Cartesian, coords(p)) + c.x ≤ xmin + end + iₑ = findfirst(1:nx) do i + p = vertex(grid, (i, 1)) + c = convert(Cartesian, coords(p)) + c.x ≥ xmax + end + jₛ = findlast(1:ny) do i + p = vertex(grid, (1, i)) + c = convert(Cartesian, coords(p)) + c.y ≤ ymin + end + jₑ = findfirst(1:ny) do i + p = vertex(grid, (1, i)) + c = convert(Cartesian, coords(p)) + c.y ≥ ymax + end + + if iₛ == iₑ || jₛ == jₑ + throw(ArgumentError("the passed limits are not valid for the grid")) + end + + CartesianIndex(iₛ, jₛ):CartesianIndex(iₑ - 1, jₑ - 1) +end + +function cartesianrange(grid::Grid{𝔼{3}}, limits) + nx, ny, nz = vsize(grid) + + (xₛ, xₑ), (yₛ, yₑ), (zₛ, zₑ) = limits + + a = convert(Cartesian, coords(vertex(grid, (1, 1, 1)))) + b = convert(Cartesian, coords(vertex(grid, (nx, 1, 1)))) + c = convert(Cartesian, coords(vertex(grid, (1, ny, 1)))) + d = convert(Cartesian, coords(vertex(grid, (1, 1, nz)))) + + xmin = max(xₛ, a.x) + ymin = max(yₛ, a.y) + zmin = max(zₛ, a.z) + xmax = min(xₑ, b.x) + ymax = min(yₑ, c.y) + zmax = min(zₑ, d.z) + + iₛ = findlast(1:nx) do i + p = vertex(grid, (i, 1, 1)) + c = convert(Cartesian, coords(p)) + c.x ≤ xmin + end + iₑ = findfirst(1:nx) do i + p = vertex(grid, (i, 1, 1)) + c = convert(Cartesian, coords(p)) + c.x ≥ xmax + end + jₛ = findlast(1:ny) do i + p = vertex(grid, (1, i, 1)) + c = convert(Cartesian, coords(p)) + c.y ≤ ymin + end + jₑ = findfirst(1:ny) do i + p = vertex(grid, (1, i, 1)) + c = convert(Cartesian, coords(p)) + c.y ≥ ymax + end + kₛ = findlast(1:nz) do i + p = vertex(grid, (1, 1, i)) + c = convert(Cartesian, coords(p)) + c.z ≤ zmin + end + kₑ = findfirst(1:nz) do i + p = vertex(grid, (1, 1, i)) + c = convert(Cartesian, coords(p)) + c.z ≥ zmax + end + + if iₛ == iₑ || jₛ == jₑ || kₛ == kₑ + throw(ArgumentError("the passed limits are not valid for the grid")) + end + + CartesianIndex(iₛ, jₛ, kₛ):CartesianIndex(iₑ - 1, jₑ - 1, kₑ - 1) +end + +function cartesianrange(grid::Grid{🌐}, limits) + nlon, nlat = vsize(grid) + (llonmin, llonmax), (llatmin, llatmax) = limits + + a = convert(Cartesian, coords(vertex(grid, (1, 1)))) + b = convert(Cartesian, coords(vertex(grid, (nlon, 1)))) + c = convert(Cartesian, coords(vertex(grid, (1, nlat)))) + + lonmin = max(llonmin, a.lon) + latmin = max(llatmin, a.lat) + lonmax = min(llonmax, b.lon) + latmax = min(llatmax, c.lat) + + iₛ = findlast(1:nlon) do i + p = vertex(grid, (i, 1)) + c = convert(LatLon, coords(p)) + c.lon ≤ lonmin + end + iₑ = findfirst(1:nlon) do i + p = vertex(grid, (i, 1)) + c = convert(LatLon, coords(p)) + c.lon ≥ lonmax + end + jₛ = findlast(1:nlat) do i + p = vertex(grid, (1, i)) + c = convert(LatLon, coords(p)) + c.lat ≤ latmin + end + jₑ = findfirst(1:nlat) do i + p = vertex(grid, (1, i)) + c = convert(LatLon, coords(p)) + c.lat ≥ latmax + end + + if iₛ == iₑ || jₛ == jₑ + throw(ArgumentError("the passed limits are not valid for the grid")) + end + + CartesianIndex(iₛ, jₛ):CartesianIndex(iₑ - 1, jₑ - 1) +end diff --git a/test/transforms.jl b/test/transforms.jl index 9c22ae7c6..b14cacf2e 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1657,27 +1657,6 @@ end @test TB.parameters(Crop(lat=(30, 60))) == (; limits=(; lat=(30, 60))) @test TB.parameters(Crop(lon=(45u"°", 90u"°"))) == (; limits=(; lon=(45u"°", 90u"°"))) - # --------- - # POINTSET - # --------- - - f = Crop(x=(T(1.5), T(3.5))) - d = PointSet([cart(1, 0), cart(2, 1), cart(3, 1), cart(4, 0)]) - r, c = TB.apply(f, d) - @test r == PointSet([cart(2, 1), cart(3, 1)]) - - # ------------ - # GEOMETRYSET - # ------------ - - f = Crop(x=(T(1.5), T(3.5))) - t1 = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) - t2 = t1 |> Translate(T(2), T(2)) - t3 = t2 |> Translate(T(2), T(2)) - d = GeometrySet([t1, t2, t3]) - r, c = TB.apply(f, d) - @test r == GeometrySet([t2]) - # -------------- # CARTESIANGRID # -------------- @@ -1705,8 +1684,31 @@ end f = Crop(x=(T(5.5), T(8.5))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) + @test r isa StructuredGrid @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) + # ---------------- + # TRANSFORMEDGRID + # ---------------- + + f = Crop(x=(T(1), T(5)), y=(T(2), T(6))) + d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r == TransformedGrid(CartesianGrid((7, 7), cart(1, 2), T.((1, 1))), Rotate(T(π / 4))) + + f = Crop(x=(T(3), T(5)), y=(T(5), T(8))) + d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r == TransformedGrid(CartesianGrid((4, 3), cart(4, 7), T.((1, 1))), Rotate(T(π / 4))) + + f = Crop(x=(T(0), T(14)), y=(T(0), T(14)), z=(T(0), T(14))) + d = TransformedGrid(cartgrid(10, 10, 10), Translate(T(2), T(2), T(2))) + r, c = TB.apply(f, d) + @test r isa TransformedGrid + @test r == d + # ----------- # SIMPLEMESH # ----------- @@ -1714,7 +1716,19 @@ end f = Crop(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) d = convert(SimpleMesh, cartgrid(10, 10)) r, c = TB.apply(f, d) + @test r isa SimpleMesh @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) + + # ------- + # ERRORS + # ------- + + # error: invalid limits + f = Crop(x=(T(-5), T(-1)), y=(T(2), T(6))) + d = TransformedGrid(cartgrid(10, 10), Identity()) + @test_throws ArgumentError TB.apply(f, d) + f = Crop(x=(T(1), T(5)), y=(T(12), T(16))) + @test_throws ArgumentError TB.apply(f, d) end @testitem "Repair(0)" setup = [Setup] begin From e39c23c40b2cab49adf06be107a862fc231e5344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 5 Sep 2024 19:27:45 -0300 Subject: [PATCH 314/423] Rename Crop --> Slice (#1042) --- docs/src/transforms.md | 6 ++--- src/Meshes.jl | 2 +- src/transforms.jl | 2 +- src/transforms/{crop.jl => slice.jl} | 24 +++++++++---------- test/transforms.jl | 36 ++++++++++++++-------------- 5 files changed, 35 insertions(+), 35 deletions(-) rename src/transforms/{crop.jl => slice.jl} (82%) diff --git a/docs/src/transforms.md b/docs/src/transforms.md index faf8de5a0..45f690a7a 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -176,15 +176,15 @@ viz(fig[1,2], disk) fig ``` -## Crop +## Slice ```@docs -Crop +Slice ``` ```@example transforms grid = CartesianGrid(10, 10) -subgrid = grid |> Crop(x=(1.5, 6.5), y=(3.5, 8.5)) +subgrid = grid |> Slice(x=(1.5, 6.5), y=(3.5, 8.5)) fig = Mke.Figure(size = (800, 400)) viz(fig[1,1], grid) diff --git a/src/Meshes.jl b/src/Meshes.jl index 13260951b..95e64faa2 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -534,7 +534,7 @@ export Proj, LengthUnit, Shadow, - Crop, + Slice, Repair, Bridge, LambdaMuSmoothing, diff --git a/src/transforms.jl b/src/transforms.jl index 25e421103..895e84678 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -96,7 +96,7 @@ include("transforms/stdcoords.jl") include("transforms/proj.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") -include("transforms/crop.jl") +include("transforms/slice.jl") include("transforms/repair.jl") include("transforms/bridge.jl") include("transforms/smoothing.jl") diff --git a/src/transforms/crop.jl b/src/transforms/slice.jl similarity index 82% rename from src/transforms/crop.jl rename to src/transforms/slice.jl index 11ffa8149..b65447431 100644 --- a/src/transforms/crop.jl +++ b/src/transforms/slice.jl @@ -3,8 +3,8 @@ # ------------------------------------------------------------------ """ - Crop(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) - Crop(lat=(latmin, latmax), lon=(lonmin, lonmax)) + Slice(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) + Slice(lat=(latmin, latmax), lon=(lonmin, lonmax)) Retain the grid elements within `x` limits [`xmax`,`xmax`], `y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] @@ -15,24 +15,24 @@ in degree units. ## Examples ```julia -Crop(x=(2, 4)) -Crop(x=(1u"km", 3u"km")) -Crop(y=(1.2, 1.8), z=(2.4, 3.0)) -Crop(lat=(30, 60)) -Crop(lon=(45u"°", 90u"°")) +Slice(x=(2, 4)) +Slice(x=(1u"km", 3u"km")) +Slice(y=(1.2, 1.8), z=(2.4, 3.0)) +Slice(lat=(30, 60)) +Slice(lon=(45u"°", 90u"°")) ``` """ -struct Crop{T} <: GeometricTransform +struct Slice{T} <: GeometricTransform limits::T end -Crop(; kwargs...) = Crop(values(kwargs)) +Slice(; kwargs...) = Slice(values(kwargs)) -parameters(t::Crop) = (; limits=t.limits) +parameters(t::Slice) = (; limits=t.limits) -preprocess(t::Crop, g::Grid) = cartesianrange(g, _fixlimits(boundingbox(g), t.limits)) +preprocess(t::Slice, g::Grid) = cartesianrange(g, _fixlimits(boundingbox(g), t.limits)) -apply(t::Crop, g::Grid) = g[preprocess(t, g)], nothing +apply(t::Slice, g::Grid) = g[preprocess(t, g)], nothing # ----------------- # HELPER FUNCTIONS diff --git a/test/transforms.jl b/test/transforms.jl index b14cacf2e..78fe886e4 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1647,21 +1647,21 @@ end @test r == SimpleMesh(f.(vertices(d)), topology(d)) end -@testitem "Crop" setup = [Setup] begin - @test !isaffine(Crop(x=(T(2), T(4)))) - @test !TB.isrevertible(Crop(x=(T(2), T(4)))) - @test !TB.isinvertible(Crop(x=(T(2), T(4)))) - @test TB.parameters(Crop(x=(T(2), T(4)))) == (; limits=(; x=(T(2), T(4)))) - @test TB.parameters(Crop(y=(T(2) * u"km", T(4) * u"km"))) == (; limits=(; y=(T(2) * u"km", T(4) * u"km"))) - @test TB.parameters(Crop(z=(2, 4))) == (; limits=(; z=(2, 4))) - @test TB.parameters(Crop(lat=(30, 60))) == (; limits=(; lat=(30, 60))) - @test TB.parameters(Crop(lon=(45u"°", 90u"°"))) == (; limits=(; lon=(45u"°", 90u"°"))) +@testitem "Slice" setup = [Setup] begin + @test !isaffine(Slice(x=(T(2), T(4)))) + @test !TB.isrevertible(Slice(x=(T(2), T(4)))) + @test !TB.isinvertible(Slice(x=(T(2), T(4)))) + @test TB.parameters(Slice(x=(T(2), T(4)))) == (; limits=(; x=(T(2), T(4)))) + @test TB.parameters(Slice(y=(T(2) * u"km", T(4) * u"km"))) == (; limits=(; y=(T(2) * u"km", T(4) * u"km"))) + @test TB.parameters(Slice(z=(2, 4))) == (; limits=(; z=(2, 4))) + @test TB.parameters(Slice(lat=(30, 60))) == (; limits=(; lat=(30, 60))) + @test TB.parameters(Slice(lon=(45u"°", 90u"°"))) == (; limits=(; lon=(45u"°", 90u"°"))) # -------------- # CARTESIANGRID # -------------- - f = Crop(z=(T(1.5), T(4.5))) + f = Slice(z=(T(1.5), T(4.5))) d = cartgrid(10, 10, 10) r, c = TB.apply(f, d) @test r isa CartesianGrid @@ -1671,7 +1671,7 @@ end # RECTILINEARGRID # ---------------- - f = Crop(y=(T(3.5), T(6.5))) + f = Slice(y=(T(3.5), T(6.5))) d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa RectilinearGrid @@ -1681,7 +1681,7 @@ end # STRUCTUREDGRID # --------------- - f = Crop(x=(T(5.5), T(8.5))) + f = Slice(x=(T(5.5), T(8.5))) d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa StructuredGrid @@ -1691,19 +1691,19 @@ end # TRANSFORMEDGRID # ---------------- - f = Crop(x=(T(1), T(5)), y=(T(2), T(6))) + f = Slice(x=(T(1), T(5)), y=(T(2), T(6))) d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r == TransformedGrid(CartesianGrid((7, 7), cart(1, 2), T.((1, 1))), Rotate(T(π / 4))) - f = Crop(x=(T(3), T(5)), y=(T(5), T(8))) + f = Slice(x=(T(3), T(5)), y=(T(5), T(8))) d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) r, c = TB.apply(f, d) @test r isa TransformedGrid @test r == TransformedGrid(CartesianGrid((4, 3), cart(4, 7), T.((1, 1))), Rotate(T(π / 4))) - f = Crop(x=(T(0), T(14)), y=(T(0), T(14)), z=(T(0), T(14))) + f = Slice(x=(T(0), T(14)), y=(T(0), T(14)), z=(T(0), T(14))) d = TransformedGrid(cartgrid(10, 10, 10), Translate(T(2), T(2), T(2))) r, c = TB.apply(f, d) @test r isa TransformedGrid @@ -1713,7 +1713,7 @@ end # SIMPLEMESH # ----------- - f = Crop(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) + f = Slice(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) d = convert(SimpleMesh, cartgrid(10, 10)) r, c = TB.apply(f, d) @test r isa SimpleMesh @@ -1724,10 +1724,10 @@ end # ------- # error: invalid limits - f = Crop(x=(T(-5), T(-1)), y=(T(2), T(6))) + f = Slice(x=(T(-5), T(-1)), y=(T(2), T(6))) d = TransformedGrid(cartgrid(10, 10), Identity()) @test_throws ArgumentError TB.apply(f, d) - f = Crop(x=(T(1), T(5)), y=(T(12), T(16))) + f = Slice(x=(T(1), T(5)), y=(T(12), T(16))) @test_throws ArgumentError TB.apply(f, d) end From 4ff47ad16e3ca31c6b5541798bbebd2e9fb334ef Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:34:51 -0300 Subject: [PATCH 315/423] Add support for `LatLon` in `boundingbox` (#1043) * Add support for 'LatLon' in 'boundingbox' * Update tests * Update tests --- src/boundingboxes.jl | 51 ++++++++++++++++++++++++++++++++++++------- src/utils/misc.jl | 6 ++--- test/boundingboxes.jl | 22 +++++++++++++++++-- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 8f5d1753b..579d55562 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -85,16 +85,51 @@ boundingbox(m::Mesh) = _pboxes(vertices(m)) _bboxes(boxes) = _pboxes(point for box in boxes for point in extrema(box)) -function _pboxes(points) +_pboxes(points) = _pboxes(manifold(first(points)), points) + +function _pboxes(::Type{𝔼{2}}, points) + p = first(points) + ℒ = lentype(p) + xmin, ymin = typemax(ℒ), typemax(ℒ) + xmax, ymax = typemin(ℒ), typemin(ℒ) + for p in points + c = convert(Cartesian, coords(p)) + xmin = min(c.x, xmin) + ymin = min(c.y, ymin) + xmax = max(c.x, xmax) + ymax = max(c.y, ymax) + end + Box(withcrs(p, (xmin, ymin)), withcrs(p, (xmax, ymax))) +end + +function _pboxes(::Type{𝔼{3}}, points) p = first(points) ℒ = lentype(p) - Dim = embeddim(p) - xmin = MVector(ntuple(i -> typemax(ℒ), Dim)) - xmax = MVector(ntuple(i -> typemin(ℒ), Dim)) + xmin, ymin, zmin = typemax(ℒ), typemax(ℒ), typemax(ℒ) + xmax, ymax, zmax = typemin(ℒ), typemin(ℒ), typemin(ℒ) + for p in points + c = convert(Cartesian, coords(p)) + xmin = min(c.x, xmin) + ymin = min(c.y, ymin) + zmin = min(c.z, zmin) + xmax = max(c.x, xmax) + ymax = max(c.y, ymax) + zmax = max(c.z, zmax) + end + Box(withcrs(p, (xmin, ymin, zmin)), withcrs(p, (xmax, ymax, zmax))) +end + +function _pboxes(::Type{🌐}, points) + p = first(points) + T = numtype(lentype(p)) + lonmin, latmin = T(180) * u"°", T(90) * u"°" + lonmax, latmax = T(-180) * u"°", T(-90) * u"°" for p in points - x = to(p) - @. xmin = min(x, xmin) - @. xmax = max(x, xmax) + c = convert(LatLon, coords(p)) + lonmin = min(c.lon, lonmin) + latmin = min(c.lat, latmin) + lonmax = max(c.lon, lonmax) + latmax = max(c.lat, latmax) end - Box(withcrs(p, xmin), withcrs(p, xmax)) + Box(withcrs(p, (latmin, lonmin), LatLon), withcrs(p, (latmax, lonmax), LatLon)) end diff --git a/src/utils/misc.jl b/src/utils/misc.jl index 7e4545766..ed189f83d 100644 --- a/src/utils/misc.jl +++ b/src/utils/misc.jl @@ -262,9 +262,9 @@ function cartesianrange(grid::Grid{🌐}, limits) nlon, nlat = vsize(grid) (llonmin, llonmax), (llatmin, llatmax) = limits - a = convert(Cartesian, coords(vertex(grid, (1, 1)))) - b = convert(Cartesian, coords(vertex(grid, (nlon, 1)))) - c = convert(Cartesian, coords(vertex(grid, (1, nlat)))) + a = convert(LatLon, coords(vertex(grid, (1, 1)))) + b = convert(LatLon, coords(vertex(grid, (nlon, 1)))) + c = convert(LatLon, coords(vertex(grid, (1, nlat)))) lonmin = max(llonmin, a.lon) latmin = max(llatmin, a.lat) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 4d34cd371..5fa59f501 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -56,8 +56,13 @@ d = GeometrySet([b, s]) @test boundingbox(m) == Box(cart(-3, -2), cart(2, 2)) @test boundingbox(d) == Box(cart(-3, -2), cart(2, 2)) - @test @allocated(boundingbox(m)) < 2500 - @test @allocated(boundingbox(d)) < 2500 + if Sys.iswindows() && VERSION < v"1.10" + @test @allocated(boundingbox(m)) < 4100 + @test @allocated(boundingbox(d)) < 4100 + else + @test @allocated(boundingbox(m)) < 2600 + @test @allocated(boundingbox(d)) < 2600 + end b1 = Box(cart(0, 0), cart(1, 1)) b2 = Box(cart(-1, -1), cart(0.5, 0.5)) @@ -119,6 +124,19 @@ p = ParaboloidSurface(cart(1, 2, 3), T(5), T(4)) @test boundingbox(p) ≈ Box(cart(-4, -3, 3), cart(6, 7, 73 / 16)) + # latlon coordinates + t = Triangle(latlon(0, 0), latlon(0, 2), latlon(1, 1)) + @test boundingbox(t) == Box(latlon(0, 0), latlon(1, 2)) + @test @allocated(boundingbox(t)) < 50 + + p = Pentagon(latlon(1, 6), latlon(10, 2), latlon(16, 10), latlon(10, 18), latlon(1, 14)) + @test boundingbox(p) == Box(latlon(1, 2), latlon(16, 18)) + @test @allocated(boundingbox(p)) < 50 + + d = PointSet(latlon(0, 0), latlon(2, 1), latlon(1, 2)) + @test boundingbox(d) == Box(latlon(0, 0), latlon(2, 2)) + @test @allocated(boundingbox(d)) < 50 + # CRS propagation r = Ray(merc(-1, 1), vector(1, -1)) @test crs(boundingbox(r)) === crs(r) From b6532e4f389ada2b14d4652ae8511622f7d333ec Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:49:49 -0300 Subject: [PATCH 316/423] Fix `cartesianrange` of `Grid` (#1044) * Fix 'cartesianrange' of 'Grid' * Update code * Fix typo * Fix typo * Rename variables --- src/utils/misc.jl | 109 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 32 deletions(-) diff --git a/src/utils/misc.jl b/src/utils/misc.jl index ed189f83d..1acc33ff6 100644 --- a/src/utils/misc.jl +++ b/src/utils/misc.jl @@ -170,27 +170,37 @@ function cartesianrange(grid::Grid{𝔼{2}}, limits) b = convert(Cartesian, coords(vertex(grid, (nx, 1)))) c = convert(Cartesian, coords(vertex(grid, (1, ny)))) - xmin = max(xₛ, a.x) - ymin = max(yₛ, a.y) - xmax = min(xₑ, b.x) - ymax = min(yₑ, c.y) + swapx = a.x > b.x + swapy = a.y > c.y - iₛ = findlast(1:nx) do i + xinds = swapx ? (nx:-1:1) : (1:1:nx) + yinds = swapy ? (ny:-1:1) : (1:1:ny) + + gridxₛ, gridxₑ = swapx ? (b.x, a.x) : (a.x, b.x) + gridyₛ, gridyₑ = swapy ? (c.y, a.y) : (a.y, c.y) + + xmin = max(xₛ, gridxₛ) + ymin = max(yₛ, gridyₛ) + xmax = min(xₑ, gridxₑ) + ymax = min(yₑ, gridyₑ) + + iₛ = findlast(xinds) do i p = vertex(grid, (i, 1)) c = convert(Cartesian, coords(p)) c.x ≤ xmin end - iₑ = findfirst(1:nx) do i + iₑ = findfirst(xinds) do i p = vertex(grid, (i, 1)) c = convert(Cartesian, coords(p)) c.x ≥ xmax end - jₛ = findlast(1:ny) do i + + jₛ = findlast(yinds) do i p = vertex(grid, (1, i)) c = convert(Cartesian, coords(p)) c.y ≤ ymin end - jₑ = findfirst(1:ny) do i + jₑ = findfirst(yinds) do i p = vertex(grid, (1, i)) c = convert(Cartesian, coords(p)) c.y ≥ ymax @@ -200,7 +210,10 @@ function cartesianrange(grid::Grid{𝔼{2}}, limits) throw(ArgumentError("the passed limits are not valid for the grid")) end - CartesianIndex(iₛ, jₛ):CartesianIndex(iₑ - 1, jₑ - 1) + iₛ, iₑ = swapx ? (iₑ, iₛ) : (iₛ, iₑ) + jₛ, jₑ = swapy ? (jₑ, jₛ) : (jₛ, jₑ) + + CartesianIndex(xinds[iₛ], yinds[jₛ]):CartesianIndex(xinds[iₑ] - 1, yinds[jₑ] - 1) end function cartesianrange(grid::Grid{𝔼{3}}, limits) @@ -213,39 +226,53 @@ function cartesianrange(grid::Grid{𝔼{3}}, limits) c = convert(Cartesian, coords(vertex(grid, (1, ny, 1)))) d = convert(Cartesian, coords(vertex(grid, (1, 1, nz)))) - xmin = max(xₛ, a.x) - ymin = max(yₛ, a.y) - zmin = max(zₛ, a.z) - xmax = min(xₑ, b.x) - ymax = min(yₑ, c.y) - zmax = min(zₑ, d.z) + swapx = a.x > b.x + swapy = a.y > c.y + swapz = a.z > d.z - iₛ = findlast(1:nx) do i + xinds = swapx ? (nx:-1:1) : (1:1:nx) + yinds = swapy ? (ny:-1:1) : (1:1:ny) + zinds = swapz ? (nz:-1:1) : (1:1:nz) + + gridxₛ, gridxₑ = swapx ? (b.x, a.x) : (a.x, b.x) + gridyₛ, gridyₑ = swapy ? (c.y, a.y) : (a.y, c.y) + gridzₛ, gridzₑ = swapz ? (d.z, a.z) : (a.z, d.z) + + xmin = max(xₛ, gridxₛ) + ymin = max(yₛ, gridyₛ) + zmin = max(zₛ, gridzₛ) + xmax = min(xₑ, gridxₑ) + ymax = min(yₑ, gridyₑ) + zmax = min(zₑ, gridzₑ) + + iₛ = findlast(xinds) do i p = vertex(grid, (i, 1, 1)) c = convert(Cartesian, coords(p)) c.x ≤ xmin end - iₑ = findfirst(1:nx) do i + iₑ = findfirst(xinds) do i p = vertex(grid, (i, 1, 1)) c = convert(Cartesian, coords(p)) c.x ≥ xmax end - jₛ = findlast(1:ny) do i + + jₛ = findlast(yinds) do i p = vertex(grid, (1, i, 1)) c = convert(Cartesian, coords(p)) c.y ≤ ymin end - jₑ = findfirst(1:ny) do i + jₑ = findfirst(yinds) do i p = vertex(grid, (1, i, 1)) c = convert(Cartesian, coords(p)) c.y ≥ ymax end - kₛ = findlast(1:nz) do i + + kₛ = findlast(zinds) do i p = vertex(grid, (1, 1, i)) c = convert(Cartesian, coords(p)) c.z ≤ zmin end - kₑ = findfirst(1:nz) do i + kₑ = findfirst(zinds) do i p = vertex(grid, (1, 1, i)) c = convert(Cartesian, coords(p)) c.z ≥ zmax @@ -255,38 +282,53 @@ function cartesianrange(grid::Grid{𝔼{3}}, limits) throw(ArgumentError("the passed limits are not valid for the grid")) end - CartesianIndex(iₛ, jₛ, kₛ):CartesianIndex(iₑ - 1, jₑ - 1, kₑ - 1) + iₛ, iₑ = swapx ? (iₑ, iₛ) : (iₛ, iₑ) + jₛ, jₑ = swapy ? (jₑ, jₛ) : (jₛ, jₑ) + kₛ, kₑ = swapz ? (kₑ, kₛ) : (kₛ, kₑ) + + CartesianIndex(xinds[iₛ], yinds[jₛ], zinds[kₛ]):CartesianIndex(xinds[iₑ] - 1, yinds[jₑ] - 1, zinds[kₑ] - 1) end function cartesianrange(grid::Grid{🌐}, limits) nlon, nlat = vsize(grid) - (llonmin, llonmax), (llatmin, llatmax) = limits + + (lonₛ, lonₑ), (latₛ, latₑ) = limits a = convert(LatLon, coords(vertex(grid, (1, 1)))) b = convert(LatLon, coords(vertex(grid, (nlon, 1)))) c = convert(LatLon, coords(vertex(grid, (1, nlat)))) - lonmin = max(llonmin, a.lon) - latmin = max(llatmin, a.lat) - lonmax = min(llonmax, b.lon) - latmax = min(llatmax, c.lat) + swaplon = a.lon > b.lon + swaplat = a.lat > c.lat + + loninds = swaplon ? (nlon:-1:1) : (1:1:nlon) + latinds = swaplat ? (nlat:-1:1) : (1:1:nlat) - iₛ = findlast(1:nlon) do i + gridlonₛ, gridlonₑ = swaplon ? (b.lon, a.lon) : (a.lon, b.lon) + gridlatₛ, gridlatₑ = swaplat ? (c.lat, a.lat) : (a.lat, c.lat) + + lonmin = max(lonₛ, gridlonₛ) + latmin = max(latₛ, gridlatₛ) + lonmax = min(lonₑ, gridlonₑ) + latmax = min(latₑ, gridlatₑ) + + iₛ = findlast(loninds) do i p = vertex(grid, (i, 1)) c = convert(LatLon, coords(p)) c.lon ≤ lonmin end - iₑ = findfirst(1:nlon) do i + iₑ = findfirst(loninds) do i p = vertex(grid, (i, 1)) c = convert(LatLon, coords(p)) c.lon ≥ lonmax end - jₛ = findlast(1:nlat) do i + + jₛ = findlast(latinds) do i p = vertex(grid, (1, i)) c = convert(LatLon, coords(p)) c.lat ≤ latmin end - jₑ = findfirst(1:nlat) do i + jₑ = findfirst(latinds) do i p = vertex(grid, (1, i)) c = convert(LatLon, coords(p)) c.lat ≥ latmax @@ -296,5 +338,8 @@ function cartesianrange(grid::Grid{🌐}, limits) throw(ArgumentError("the passed limits are not valid for the grid")) end - CartesianIndex(iₛ, jₛ):CartesianIndex(iₑ - 1, jₑ - 1) + iₛ, iₑ = swaplon ? (iₑ, iₛ) : (iₛ, iₑ) + jₛ, jₑ = swaplat ? (jₑ, jₛ) : (jₛ, jₑ) + + CartesianIndex(loninds[iₛ], latinds[jₛ]):CartesianIndex(loninds[iₑ] - 1, latinds[jₑ] - 1) end From dcf7d2d1210534299c5855222b4a80913a5cbf17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 6 Sep 2024 15:50:08 -0300 Subject: [PATCH 317/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 61d2d1966..0a019edd4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.50.4" +version = "0.51.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From cfa799c52f2391cb838704689f7f6c9c50f00cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sun, 8 Sep 2024 16:58:11 -0300 Subject: [PATCH 318/423] Fix cartesianrange/slice --- src/indices.jl | 125 +++++++++++++++++++-- src/transforms/slice.jl | 80 +++++++------- src/utils/misc.jl | 239 ---------------------------------------- test/transforms.jl | 53 --------- 4 files changed, 152 insertions(+), 345 deletions(-) diff --git a/src/indices.jl b/src/indices.jl index 42251953e..523491a4b 100644 --- a/src/indices.jl +++ b/src/indices.jl @@ -2,6 +2,10 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ +# --------------- +# LINEAR INDICES +# --------------- + """ indices(domain, geometry) @@ -54,7 +58,7 @@ end function indices(grid::CartesianGrid, box::Box) # cartesian range - range = cartesianrange(grid, _boxlimits(box)) + range = cartesianrange(grid, box) # convert to linear indices LinearIndices(size(grid))[range] |> vec @@ -64,26 +68,125 @@ indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geo function indices(grid::RectilinearGrid, box::Box) # cartesian range - range = cartesianrange(grid, _boxlimits(box)) + range = cartesianrange(grid, box) # convert to linear indices LinearIndices(size(grid))[range] |> vec end -# ----------------- -# HELPER FUNCTIONS -# ----------------- +# ---------------- +# CARTESIAN RANGE +# ---------------- + +""" + cartesianrange(grid, box) + +Return the Cartesian range of the elements of the `grid` that intersect with the `box`. +""" +cartesianrange(grid::Grid{M}, box::Box{M}) where {M} = _manifoldrange(M, grid, box) + +_manifoldrange(::Type{<:𝔼}, grid::Grid, box::Box) = _euclideanrange(grid, box) + +_manifoldrange(::Type{<:🌐}, grid::Grid, box::Box) = _geodesicrange(grid, box) + +function _euclideanrange(grid::CartesianGrid, box::Box) + # grid properties + or = minimum(grid) + sp = spacing(grid) + sz = size(grid) + + # intersection of boxes + lo, up = extrema(boundingbox(grid) ∩ box) + + # Cartesian indices of new corners + ijkₛ = max.(ceil.(Int, (lo - or) ./ sp), 1) + ijkₑ = min.(floor.(Int, (up - or) ./ sp) .+ 1, sz) -function _boxlimits(box::Box{𝔼{2}}) - min, max = convert.(Cartesian, coords.(extrema(box))) - (min.x, max.x), (min.y, max.y) + # Cartesian range from corner to corner + CartesianIndex(Tuple(ijkₛ)):CartesianIndex(Tuple(ijkₑ)) end -function _boxlimits(box::Box{𝔼{3}}) - min, max = convert.(Cartesian, coords.(extrema(box))) - (min.x, max.x), (min.y, max.y), (min.z, max.z) +function _euclideanrange(grid::RectilinearGrid, box::Box) + # grid properties + nd = paramdim(grid) + + # intersection of boxes + lo, up = to.(extrema(boundingbox(grid) ∩ box)) + + # integer coordinates of lower point + ijkₛ = ntuple(nd) do i + findlast(x -> x ≤ lo[i], xyz(grid)[i]) + end + + # integer coordinates of upper point + ijkₑ = ntuple(nd) do i + findfirst(x -> x ≥ up[i], xyz(grid)[i]) + end + + # integer coordinates of elements + CartesianIndex(ijkₛ):CartesianIndex(ijkₑ .- 1) end +function _geodesicrange(grid::Grid, box::Box) + nlon, nlat = vsize(grid) + + boxmin = convert(LatLon, coords(minimum(box))) + boxmax = convert(LatLon, coords(maximum(box))) + + a = convert(LatLon, coords(vertex(grid, (1, 1)))) + b = convert(LatLon, coords(vertex(grid, (nlon, 1)))) + c = convert(LatLon, coords(vertex(grid, (1, nlat)))) + + swaplon = a.lon > b.lon + swaplat = a.lat > c.lat + + loninds = swaplon ? (nlon:-1:1) : (1:1:nlon) + latinds = swaplat ? (nlat:-1:1) : (1:1:nlat) + + gridlonₛ, gridlonₑ = swaplon ? (b.lon, a.lon) : (a.lon, b.lon) + gridlatₛ, gridlatₑ = swaplat ? (c.lat, a.lat) : (a.lat, c.lat) + + lonmin = max(boxmin.lon, gridlonₛ) + latmin = max(boxmin.lat, gridlatₛ) + lonmax = min(boxmax.lon, gridlonₑ) + latmax = min(boxmax.lat, gridlatₑ) + + iₛ = findlast(loninds) do i + p = vertex(grid, (i, 1)) + c = convert(LatLon, coords(p)) + c.lon ≤ lonmin + end + iₑ = findfirst(loninds) do i + p = vertex(grid, (i, 1)) + c = convert(LatLon, coords(p)) + c.lon ≥ lonmax + end + + jₛ = findlast(latinds) do i + p = vertex(grid, (1, i)) + c = convert(LatLon, coords(p)) + c.lat ≤ latmin + end + jₑ = findfirst(latinds) do i + p = vertex(grid, (1, i)) + c = convert(LatLon, coords(p)) + c.lat ≥ latmax + end + + if iₛ == iₑ || jₛ == jₑ + throw(ArgumentError("the passed limits are not valid for the grid")) + end + + iₛ, iₑ = swaplon ? (iₑ, iₛ) : (iₛ, iₑ) + jₛ, jₑ = swaplat ? (jₑ, jₛ) : (jₛ, jₑ) + + CartesianIndex(loninds[iₛ], latinds[jₛ]):CartesianIndex(loninds[iₑ] - 1, latinds[jₑ] - 1) +end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + function _fill!(mask, grid, val, triangle) v = vertices(triangle) diff --git a/src/transforms/slice.jl b/src/transforms/slice.jl index b65447431..57556b349 100644 --- a/src/transforms/slice.jl +++ b/src/transforms/slice.jl @@ -6,7 +6,7 @@ Slice(x=(xmin, xmax), y=(ymin, ymax), z=(zmin, zmax)) Slice(lat=(latmin, latmax), lon=(lonmin, lonmax)) -Retain the grid elements within `x` limits [`xmax`,`xmax`], +Retain the domain elements within `x` limits [`xmax`,`xmax`], `y` limits [`ymax`,`ymax`] and `z` limits [`zmin`,`zmax`] in length units (default to meters), or within `lat` limits [`latmin`,`latmax`] and `lon` limits [`lonmin`,`lonmax`] @@ -15,11 +15,10 @@ in degree units. ## Examples ```julia -Slice(x=(2, 4)) -Slice(x=(1u"km", 3u"km")) -Slice(y=(1.2, 1.8), z=(2.4, 3.0)) -Slice(lat=(30, 60)) -Slice(lon=(45u"°", 90u"°")) +Slice(x=(1000km, 3000km)) +Slice(x=(1000km, 2000km), y=(2000km, 5000km)) +Slice(lon=(0°, 90°)) +Slice(lon=(0°, 45°), lat=(0°, 45°)) ``` """ struct Slice{T} <: GeometricTransform @@ -30,54 +29,51 @@ Slice(; kwargs...) = Slice(values(kwargs)) parameters(t::Slice) = (; limits=t.limits) -preprocess(t::Slice, g::Grid) = cartesianrange(g, _fixlimits(boundingbox(g), t.limits)) +preprocess(t::Slice, d::Domain) = _sliceinds(d, _slicebox(boundingbox(d), t.limits)) -apply(t::Slice, g::Grid) = g[preprocess(t, g)], nothing +apply(t::Slice, d::Domain) = _slice(d, preprocess(t, d)), nothing # ----------------- # HELPER FUNCTIONS # ----------------- -function _fixlimits(box::Box{<:𝔼}, limits) - lims = _xyzlimits(limits) - min = convert(Cartesian, coords(minimum(box))) - max = convert(Cartesian, coords(maximum(box))) - _minmax(min, max, lims) -end - -function _fixlimits(box::Box{🌐}, limits) - lims = _latlonlimits(limits) - min = convert(LatLon, coords(minimum(box))) - max = convert(LatLon, coords(maximum(box))) - _minmax(min, max, lims) -end +_slice(d::Domain, inds) = view(d, inds) +_slice(g::Grid, inds::CartesianIndices) = getindex(g, inds) -_xyzlimits(limits) = ( - x=haskey(limits, :x) ? _aslen.(limits.x) : nothing, - y=haskey(limits, :y) ? _aslen.(limits.y) : nothing, - z=haskey(limits, :z) ? _aslen.(limits.z) : nothing -) +_sliceinds(d::Domain, b) = indices(d, b) +_sliceinds(g::CartesianGrid, b) = cartesianrange(g, b) +_sliceinds(g::RectilinearGrid, b) = cartesianrange(g, b) +_sliceinds(g::Grid{🌐}, b::Box{🌐}) = cartesianrange(g, b) -_latlonlimits(limits) = - (lat=haskey(limits, :lat) ? _asdeg.(limits.lat) : nothing, lon=haskey(limits, :lon) ? _asdeg.(limits.lon) : nothing) - -function _minmax(min::Cartesian2D, max::Cartesian2D, lims) - xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x - ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y - (xmin, xmax), (ymin, ymax) +function _slicebox(box::Box{𝔼{2}}, limits) + min = convert(Cartesian, coords(minimum(box))) + max = convert(Cartesian, coords(maximum(box))) + xmin, xmax = get(limits, :x, (min.x, max.x)) + ymin, ymax = get(limits, :y, (min.y, max.y)) + bmin = _aslen.((xmin, ymin)) + bmax = _aslen.((xmax, ymax)) + Box(withcrs(box, bmin), withcrs(box, bmax)) end -function _minmax(min::Cartesian3D, max::Cartesian3D, lims) - xmin, xmax = isnothing(lims.x) ? (min.x, max.x) : lims.x - ymin, ymax = isnothing(lims.y) ? (min.y, max.y) : lims.y - zmin, zmax = isnothing(lims.z) ? (min.z, max.z) : lims.z - (xmin, xmax), (ymin, ymax), (zmin, zmax) +function _slicebox(box::Box{𝔼{3}}, limits) + min = convert(Cartesian, coords(minimum(box))) + max = convert(Cartesian, coords(maximum(box))) + xmin, xmax = get(limits, :x, (min.x, max.x)) + ymin, ymax = get(limits, :y, (min.y, max.y)) + zmin, zmax = get(limits, :z, (min.z, max.z)) + bmin = _aslen.((xmin, ymin, zmin)) + bmax = _aslen.((xmax, ymax, zmax)) + Box(withcrs(box, bmin), withcrs(box, bmax)) end -function _minmax(min::LatLon, max::LatLon, lims) - lonmin, lonmax = isnothing(lims.lon) ? (min.lon, max.lon) : lims.lon - latmin, latmax = isnothing(lims.lat) ? (min.lat, max.lat) : lims.lat - (lonmin, lonmax), (latmin, latmax) +function _slicebox(box::Box{🌐}, limits) + min = convert(LatLon, coords(minimum(box))) + max = convert(LatLon, coords(maximum(box))) + latmin, latmax = get(limits, :lat, (min.lat, max.lat)) + lonmin, lonmax = get(limits, :lon, (min.lon, max.lon)) + bmin = _asdeg.((latmin, lonmin)) + bmax = _asdeg.((latmax, lonmax)) + Box(withcrs(box, bmin, LatLon), withcrs(box, bmax, LatLon)) end _aslen(x::Len) = float(x) diff --git a/src/utils/misc.jl b/src/utils/misc.jl index 1acc33ff6..f10a3d786 100644 --- a/src/utils/misc.jl +++ b/src/utils/misc.jl @@ -104,242 +104,3 @@ function intersectparameters(a::Point, b::Point, c::Point, d::Point) λ₁, λ₂, r, rₐ end - -""" - cartesianrange(grid, limits) - -Return the Cartesian range for the elements of the -`grid` within given `limits` along each dimension. -""" -function cartesianrange(grid::CartesianGrid, limits) - # grid properties - or = minimum(grid) - sp = spacing(grid) - sz = size(grid) - nd = length(sz) - - # box from limits - bmin = withcrs(grid, ntuple(i -> first(limits[i]), nd)) - bmax = withcrs(grid, ntuple(i -> last(limits[i]), nd)) - bbox = Box(bmin, bmax) - - # intersection of boxes - lo, up = extrema(boundingbox(grid) ∩ bbox) - - # Cartesian indices of new corners - ijkₛ = max.(ceil.(Int, (lo - or) ./ sp), 1) - ijkₑ = min.(floor.(Int, (up - or) ./ sp) .+ 1, sz) - - # Cartesian range from corner to corner - CartesianIndex(Tuple(ijkₛ)):CartesianIndex(Tuple(ijkₑ)) -end - -function cartesianrange(grid::RectilinearGrid, limits) - # grid properties - sz = size(grid) - nd = length(sz) - - # box from limits - bmin = withcrs(grid, ntuple(i -> first(limits[i]), nd)) - bmax = withcrs(grid, ntuple(i -> last(limits[i]), nd)) - bbox = Box(bmin, bmax) - - # intersection of boxes - lo, up = to.(extrema(boundingbox(grid) ∩ bbox)) - - # integer coordinates of lower point - ijkₛ = ntuple(nd) do i - findlast(x -> x ≤ lo[i], xyz(grid)[i]) - end - - # integer coordinates of upper point - ijkₑ = ntuple(nd) do i - findfirst(x -> x ≥ up[i], xyz(grid)[i]) - end - - # integer coordinates of elements - CartesianIndex(ijkₛ):CartesianIndex(ijkₑ .- 1) -end - -function cartesianrange(grid::Grid{𝔼{2}}, limits) - nx, ny = vsize(grid) - - (xₛ, xₑ), (yₛ, yₑ) = limits - - a = convert(Cartesian, coords(vertex(grid, (1, 1)))) - b = convert(Cartesian, coords(vertex(grid, (nx, 1)))) - c = convert(Cartesian, coords(vertex(grid, (1, ny)))) - - swapx = a.x > b.x - swapy = a.y > c.y - - xinds = swapx ? (nx:-1:1) : (1:1:nx) - yinds = swapy ? (ny:-1:1) : (1:1:ny) - - gridxₛ, gridxₑ = swapx ? (b.x, a.x) : (a.x, b.x) - gridyₛ, gridyₑ = swapy ? (c.y, a.y) : (a.y, c.y) - - xmin = max(xₛ, gridxₛ) - ymin = max(yₛ, gridyₛ) - xmax = min(xₑ, gridxₑ) - ymax = min(yₑ, gridyₑ) - - iₛ = findlast(xinds) do i - p = vertex(grid, (i, 1)) - c = convert(Cartesian, coords(p)) - c.x ≤ xmin - end - iₑ = findfirst(xinds) do i - p = vertex(grid, (i, 1)) - c = convert(Cartesian, coords(p)) - c.x ≥ xmax - end - - jₛ = findlast(yinds) do i - p = vertex(grid, (1, i)) - c = convert(Cartesian, coords(p)) - c.y ≤ ymin - end - jₑ = findfirst(yinds) do i - p = vertex(grid, (1, i)) - c = convert(Cartesian, coords(p)) - c.y ≥ ymax - end - - if iₛ == iₑ || jₛ == jₑ - throw(ArgumentError("the passed limits are not valid for the grid")) - end - - iₛ, iₑ = swapx ? (iₑ, iₛ) : (iₛ, iₑ) - jₛ, jₑ = swapy ? (jₑ, jₛ) : (jₛ, jₑ) - - CartesianIndex(xinds[iₛ], yinds[jₛ]):CartesianIndex(xinds[iₑ] - 1, yinds[jₑ] - 1) -end - -function cartesianrange(grid::Grid{𝔼{3}}, limits) - nx, ny, nz = vsize(grid) - - (xₛ, xₑ), (yₛ, yₑ), (zₛ, zₑ) = limits - - a = convert(Cartesian, coords(vertex(grid, (1, 1, 1)))) - b = convert(Cartesian, coords(vertex(grid, (nx, 1, 1)))) - c = convert(Cartesian, coords(vertex(grid, (1, ny, 1)))) - d = convert(Cartesian, coords(vertex(grid, (1, 1, nz)))) - - swapx = a.x > b.x - swapy = a.y > c.y - swapz = a.z > d.z - - xinds = swapx ? (nx:-1:1) : (1:1:nx) - yinds = swapy ? (ny:-1:1) : (1:1:ny) - zinds = swapz ? (nz:-1:1) : (1:1:nz) - - gridxₛ, gridxₑ = swapx ? (b.x, a.x) : (a.x, b.x) - gridyₛ, gridyₑ = swapy ? (c.y, a.y) : (a.y, c.y) - gridzₛ, gridzₑ = swapz ? (d.z, a.z) : (a.z, d.z) - - xmin = max(xₛ, gridxₛ) - ymin = max(yₛ, gridyₛ) - zmin = max(zₛ, gridzₛ) - xmax = min(xₑ, gridxₑ) - ymax = min(yₑ, gridyₑ) - zmax = min(zₑ, gridzₑ) - - iₛ = findlast(xinds) do i - p = vertex(grid, (i, 1, 1)) - c = convert(Cartesian, coords(p)) - c.x ≤ xmin - end - iₑ = findfirst(xinds) do i - p = vertex(grid, (i, 1, 1)) - c = convert(Cartesian, coords(p)) - c.x ≥ xmax - end - - jₛ = findlast(yinds) do i - p = vertex(grid, (1, i, 1)) - c = convert(Cartesian, coords(p)) - c.y ≤ ymin - end - jₑ = findfirst(yinds) do i - p = vertex(grid, (1, i, 1)) - c = convert(Cartesian, coords(p)) - c.y ≥ ymax - end - - kₛ = findlast(zinds) do i - p = vertex(grid, (1, 1, i)) - c = convert(Cartesian, coords(p)) - c.z ≤ zmin - end - kₑ = findfirst(zinds) do i - p = vertex(grid, (1, 1, i)) - c = convert(Cartesian, coords(p)) - c.z ≥ zmax - end - - if iₛ == iₑ || jₛ == jₑ || kₛ == kₑ - throw(ArgumentError("the passed limits are not valid for the grid")) - end - - iₛ, iₑ = swapx ? (iₑ, iₛ) : (iₛ, iₑ) - jₛ, jₑ = swapy ? (jₑ, jₛ) : (jₛ, jₑ) - kₛ, kₑ = swapz ? (kₑ, kₛ) : (kₛ, kₑ) - - CartesianIndex(xinds[iₛ], yinds[jₛ], zinds[kₛ]):CartesianIndex(xinds[iₑ] - 1, yinds[jₑ] - 1, zinds[kₑ] - 1) -end - -function cartesianrange(grid::Grid{🌐}, limits) - nlon, nlat = vsize(grid) - - (lonₛ, lonₑ), (latₛ, latₑ) = limits - - a = convert(LatLon, coords(vertex(grid, (1, 1)))) - b = convert(LatLon, coords(vertex(grid, (nlon, 1)))) - c = convert(LatLon, coords(vertex(grid, (1, nlat)))) - - swaplon = a.lon > b.lon - swaplat = a.lat > c.lat - - loninds = swaplon ? (nlon:-1:1) : (1:1:nlon) - latinds = swaplat ? (nlat:-1:1) : (1:1:nlat) - - gridlonₛ, gridlonₑ = swaplon ? (b.lon, a.lon) : (a.lon, b.lon) - gridlatₛ, gridlatₑ = swaplat ? (c.lat, a.lat) : (a.lat, c.lat) - - lonmin = max(lonₛ, gridlonₛ) - latmin = max(latₛ, gridlatₛ) - lonmax = min(lonₑ, gridlonₑ) - latmax = min(latₑ, gridlatₑ) - - iₛ = findlast(loninds) do i - p = vertex(grid, (i, 1)) - c = convert(LatLon, coords(p)) - c.lon ≤ lonmin - end - iₑ = findfirst(loninds) do i - p = vertex(grid, (i, 1)) - c = convert(LatLon, coords(p)) - c.lon ≥ lonmax - end - - jₛ = findlast(latinds) do i - p = vertex(grid, (1, i)) - c = convert(LatLon, coords(p)) - c.lat ≤ latmin - end - jₑ = findfirst(latinds) do i - p = vertex(grid, (1, i)) - c = convert(LatLon, coords(p)) - c.lat ≥ latmax - end - - if iₛ == iₑ || jₛ == jₑ - throw(ArgumentError("the passed limits are not valid for the grid")) - end - - iₛ, iₑ = swaplon ? (iₑ, iₛ) : (iₛ, iₑ) - jₛ, jₑ = swaplat ? (jₑ, jₛ) : (jₛ, jₑ) - - CartesianIndex(loninds[iₛ], latinds[jₛ]):CartesianIndex(loninds[iₑ] - 1, latinds[jₑ] - 1) -end diff --git a/test/transforms.jl b/test/transforms.jl index 78fe886e4..96dee2932 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1676,59 +1676,6 @@ end r, c = TB.apply(f, d) @test r isa RectilinearGrid @test r == convert(RectilinearGrid, CartesianGrid((10, 4), cart(0, 3), T.((1, 1)))) - - # --------------- - # STRUCTUREDGRID - # --------------- - - f = Slice(x=(T(5.5), T(8.5))) - d = convert(StructuredGrid, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r == convert(StructuredGrid, CartesianGrid((4, 10), cart(5, 0), T.((1, 1)))) - - # ---------------- - # TRANSFORMEDGRID - # ---------------- - - f = Slice(x=(T(1), T(5)), y=(T(2), T(6))) - d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) - r, c = TB.apply(f, d) - @test r isa TransformedGrid - @test r == TransformedGrid(CartesianGrid((7, 7), cart(1, 2), T.((1, 1))), Rotate(T(π / 4))) - - f = Slice(x=(T(3), T(5)), y=(T(5), T(8))) - d = TransformedGrid(cartgrid(10, 10), Rotate(T(π / 4))) - r, c = TB.apply(f, d) - @test r isa TransformedGrid - @test r == TransformedGrid(CartesianGrid((4, 3), cart(4, 7), T.((1, 1))), Rotate(T(π / 4))) - - f = Slice(x=(T(0), T(14)), y=(T(0), T(14)), z=(T(0), T(14))) - d = TransformedGrid(cartgrid(10, 10, 10), Translate(T(2), T(2), T(2))) - r, c = TB.apply(f, d) - @test r isa TransformedGrid - @test r == d - - # ----------- - # SIMPLEMESH - # ----------- - - f = Slice(x=(T(1.5), T(4.5)), y=(T(3.5), T(6.5))) - d = convert(SimpleMesh, cartgrid(10, 10)) - r, c = TB.apply(f, d) - @test r isa SimpleMesh - @test r == convert(SimpleMesh, CartesianGrid((4, 4), cart(1, 3), T.((1, 1)))) - - # ------- - # ERRORS - # ------- - - # error: invalid limits - f = Slice(x=(T(-5), T(-1)), y=(T(2), T(6))) - d = TransformedGrid(cartgrid(10, 10), Identity()) - @test_throws ArgumentError TB.apply(f, d) - f = Slice(x=(T(1), T(5)), y=(T(12), T(16))) - @test_throws ArgumentError TB.apply(f, d) end @testitem "Repair(0)" setup = [Setup] begin From ff6062a7e47fddb023318b2a134cb7a6ef1f13ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sun, 8 Sep 2024 17:05:41 -0300 Subject: [PATCH 319/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0a019edd4..70f569bf5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.0" +version = "0.51.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 44cd64307683186a1e618b7d5ed1d60e71444b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sun, 8 Sep 2024 17:39:37 -0300 Subject: [PATCH 320/423] Fix #1046 --- ext/grid/transformed.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index fdea09552..0bc05758c 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -30,7 +30,7 @@ function vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Va nverts = Makie.@lift length($verts) nquads = Makie.@lift length($quads) - ncolor = Makie.@lift length($colorant) + ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 dims = Makie.@lift size($tgrid) texture = if ncolor[] == 1 From d0404a5048bc1353b8f1c100863f0e48b911426c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sun, 8 Sep 2024 17:40:13 -0300 Subject: [PATCH 321/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 70f569bf5..e27c2a28e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.1" +version = "0.51.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From c41c51da4dce6a1ef9073450e2aafeba45c9ec26 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:58:03 -0300 Subject: [PATCH 322/423] CompatHelper: bump compat for Colorfy to 1, (keep existing compat) (#1048) Co-authored-by: CompatHelper Julia --- Project.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index e27c2a28e..8b1eefbaf 100644 --- a/Project.toml +++ b/Project.toml @@ -21,16 +21,10 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" TransformsBase = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" -[weakdeps] -Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" - -[extensions] -MeshesMakieExt = "Makie" - [compat] Bessels = "0.2" CircularArrays = "1.3" -Colorfy = "0.1" +Colorfy = "0.1, 1" CoordRefSystems = "0.13.4" DelaunayTriangulation = "1.0" Distances = "0.10" @@ -46,3 +40,9 @@ StatsBase = "0.33, 0.34" TransformsBase = "1.6" Unitful = "1.17" julia = "1.9" + +[extensions] +MeshesMakieExt = "Makie" + +[weakdeps] +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" From bfd2d52e3148a1d443fa175ce99d29650e54a628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 9 Sep 2024 11:30:37 -0300 Subject: [PATCH 323/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8b1eefbaf..c470a0a86 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.2" +version = "0.51.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 132669ae489aca60fc6a6842e1b50c0b0cba1b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 10 Sep 2024 12:47:32 -0300 Subject: [PATCH 324/423] Fix #1050 --- src/boundingboxes.jl | 13 +++++++++++++ test/boundingboxes.jl | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 579d55562..d9f4f263d 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -87,6 +87,19 @@ _bboxes(boxes) = _pboxes(point for box in boxes for point in extrema(box)) _pboxes(points) = _pboxes(manifold(first(points)), points) +function _pboxes(::Type{𝔼{1}}, points) + p = first(points) + ℒ = lentype(p) + xmin = typemax(ℒ) + xmax = typemin(ℒ) + for p in points + c = convert(Cartesian, coords(p)) + xmin = min(c.x, xmin) + xmax = max(c.x, xmax) + end + Box(withcrs(p, (xmin,)), withcrs(p, (xmax,))) +end + function _pboxes(::Type{𝔼{2}}, points) p = first(points) ℒ = lentype(p) diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 5fa59f501..4ade2d1d4 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -143,4 +143,9 @@ g = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) m = convert(SimpleMesh, g) @test crs(boundingbox(m)) === crs(m) + + # 1D segment + s = Segment(cart(0), cart(1)) + b = boundingbox(s) + @test b == Box(cart(0), cart(1)) end From 405e04635687710bc6af5d17c1369772c3b348e3 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:06:06 -0300 Subject: [PATCH 325/423] Add `RegularGrid` (#1047) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'RegularGrid' * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Rename variables * Add more checks * Add more methods * Make 'CartesianGrid' an alias of 'RegularGrid' * More adjustments * More adjustments * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Apply suggestions * Fix 'Proj' tests * Fix 'CRS' tests * Add docstring * Add tests * Add more tests * Apply suggestions * More adjustments * Adjust test order * Update docstring * Update docs * Update docs * Update docstrings * Fix type instability --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Júlio Hoffimann --- docs/src/domains/meshes.md | 12 +++ src/Meshes.jl | 1 + src/domains/meshes.jl | 1 + src/domains/meshes/cartesiangrid.jl | 93 +++++------------- src/domains/meshes/regulargrid.jl | 144 ++++++++++++++++++++++++++++ src/transforms/proj.jl | 6 +- src/units.jl | 7 ++ src/utils/basic.jl | 4 +- test/crs.jl | 4 +- test/meshes.jl | 136 ++++++++++++++++++++++++++ test/transforms.jl | 3 +- 11 files changed, 335 insertions(+), 76 deletions(-) create mode 100644 src/domains/meshes/regulargrid.jl diff --git a/docs/src/domains/meshes.md b/docs/src/domains/meshes.md index ace1bed3f..b58606fc3 100644 --- a/docs/src/domains/meshes.md +++ b/docs/src/domains/meshes.md @@ -2,6 +2,7 @@ ```@example meshes using Meshes # hide +using CoordRefSystems # hide import CairoMakie as Mke # hide ``` @@ -15,6 +16,17 @@ Mesh Grid ``` +```@docs +RegularGrid +``` + +```@example meshes +# 2D regular grid +grid = RegularGrid((8, 8), Point(Polar(0, 0)), (1, π/4)) + +viz(grid, showsegments = true) +``` + ```@docs CartesianGrid ``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 95e64faa2..2637c9d7c 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -302,6 +302,7 @@ export Grid, SimpleMesh, TransformedMesh, + RegularGrid, CartesianGrid, RectilinearGrid, StructuredGrid, diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index af769efe0..d084da93e 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -240,6 +240,7 @@ end # IMPLEMENTATIONS # ---------------- +include("meshes/regulargrid.jl") include("meshes/cartesiangrid.jl") include("meshes/rectilineargrid.jl") include("meshes/structuredgrid.jl") diff --git a/src/domains/meshes/cartesiangrid.jl b/src/domains/meshes/cartesiangrid.jl index daaf968ec..15c0fc582 100644 --- a/src/domains/meshes/cartesiangrid.jl +++ b/src/domains/meshes/cartesiangrid.jl @@ -30,6 +30,8 @@ Finally, a Cartesian grid can be constructed by only passing the dimensions `dims` as a tuple, or by passing each dimension `dim1`, `dim2`, ... separately. In this case, the origin and spacing default to (0,0,...) and (1,1,...). +`CartesianGrid` is an alias to [`RegularGrid`](@ref) with `Cartesian` CRS. + ## Examples Create a 3D grid with 100x100x50 hexahedrons: @@ -49,31 +51,34 @@ Create a 1D grid from -1 to 1 with 100 segments: ```julia julia> CartesianGrid((-1.0,), (1.0,), dims=(100,)) ``` + +See also [`RegularGrid`](@ref). """ -struct CartesianGrid{C<:CRS,Mₚ<:Manifold,Dim,ℒ<:Len} <: Grid{𝔼{Dim},C,Dim} - origin::Point{Mₚ,C} - spacing::NTuple{Dim,ℒ} - offset::Dims{Dim} - topology::GridTopology{Dim} +const CartesianGrid{M<:𝔼,C<:Cartesian} = RegularGrid{M,C} - function CartesianGrid( - origin::Point{Mₚ,C}, - spacing::NTuple{Dim,ℒ}, - offset::Dims{Dim}, - topology::GridTopology{Dim} - ) where {C<:CRS,Mₚ<:Manifold,Dim,ℒ<:Len} - if !all(>(zero(ℒ)), spacing) - throw(ArgumentError("spacing must be positive")) - end - new{C,Mₚ,Dim,float(ℒ)}(origin, spacing, offset, topology) - end +function CartesianGrid( + origin::Point{<:𝔼}, + spacing::NTuple{Dim,ℒ}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim,ℒ<:Len} + orig = Point(convert(Cartesian, coords(origin))) + RegularGrid(orig, spacing, offset, topology) end -CartesianGrid(origin::Point, spacing::NTuple{Dim,Len}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = - CartesianGrid(origin, promote(spacing...), offset, topology) +CartesianGrid( + origin::Point{<:𝔼}, + spacing::NTuple{Dim,Len}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim} = CartesianGrid(origin, promote(spacing...), offset, topology) -CartesianGrid(origin::Point, spacing::NTuple{Dim,Number}, offset::Dims{Dim}, topology::GridTopology{Dim}) where {Dim} = - CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) +CartesianGrid( + origin::Point{<:𝔼}, + spacing::NTuple{Dim,Number}, + offset::Dims{Dim}, + topology::GridTopology{Dim} +) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) function CartesianGrid( dims::Dims{Dim}, @@ -131,51 +136,3 @@ function CartesianGrid(dims::Dims{Dim}) where {Dim} end CartesianGrid(dims::Int...) = CartesianGrid(dims) - -spacing(g::CartesianGrid) = g.spacing - -offset(g::CartesianGrid) = g.offset - -vertex(g::CartesianGrid, ijk::Dims) = g.origin + Vec((ijk .- g.offset) .* g.spacing) - -function xyz(g::CartesianGrid) - dims = size(g) - spac = spacing(g) - orig = to(minimum(g)) - ntuple(embeddim(g)) do i - o, s, d = orig[i], spac[i], dims[i] - range(start=o, step=s, length=(d + 1)) - end -end - -XYZ(g::CartesianGrid) = XYZ(xyz(g)) - -function Base.getindex(g::CartesianGrid, I::CartesianIndices) - @boundscheck _checkbounds(g, I) - dims = size(I) - offset = g.offset .- Tuple(first(I)) .+ 1 - CartesianGrid(dims, g.origin, g.spacing, offset) -end - -==(g₁::CartesianGrid, g₂::CartesianGrid) = - g₁.topology == g₂.topology && - g₁.spacing == g₂.spacing && - Tuple(g₁.origin - g₂.origin) == (g₁.offset .- g₂.offset) .* g₁.spacing - -# ----------- -# IO METHODS -# ----------- - -function Base.summary(io::IO, g::CartesianGrid) - dims = join(size(g.topology), "×") - print(io, "$dims CartesianGrid") -end - -Base.show(io::IO, g::CartesianGrid) = summary(io, g) - -function Base.show(io::IO, ::MIME"text/plain", g::CartesianGrid) - println(io, g) - println(io, "├─ minimum: ", minimum(g)) - println(io, "├─ maximum: ", maximum(g)) - print(io, "└─ spacing: ", spacing(g)) -end diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl new file mode 100644 index 000000000..71ec3552b --- /dev/null +++ b/src/domains/meshes/regulargrid.jl @@ -0,0 +1,144 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + RegularGrid(dims, origin, spacing) + +A regular grid with dimensions `dims`, lower left corner at `origin` +and cell spacing `spacing`. The three arguments must have the same length. + + RegularGrid(dims, origin, spacing, offset) + +A regular grid with dimensions `dims`, with lower left corner of element +`offset` at `origin` and cell spacing `spacing`. + +## Examples + +``` +RegularGrid((10, 20), Point(LatLon(30.0°, 60.0°)), (1.0, 1.0)) # add coordinate units to spacing +RegularGrid((10, 20), Point(Polar(0.0mm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units +RegularGrid((10, 20), Point(Marcator(0.0, 0.0)), (1.5, 1.5)) +RegularGrid((10, 20, 30), Point(Cylindrical(0.0, 0.0, 0.0)), (3.0, 2.0, 1.0)) +``` + +See also [`CartesianGrid`](@ref). +""" +struct RegularGrid{M<:Manifold,C<:CRS,S<:Tuple,N} <: Grid{M,C,N} + origin::Point{M,C} + spacing::S + offset::Dims{N} + topology::GridTopology{N} + + function RegularGrid{M,C,S,N}(origin, spacing, offset, topology) where {M<:Manifold,C<:CRS,S<:Tuple,N} + if !all(s -> s > zero(s), spacing) + throw(ArgumentError("spacing must be positive")) + end + new(origin, spacing, offset, topology) + end +end + +function RegularGrid( + origin::Point{M,C}, + spacing::Tuple, + offset::Dims{N}, + topology::GridTopology{N} +) where {M<:Manifold,C<:CRS,N} + if manifold(origin) <: 🌐 && !(crs(origin) <: LatLon) + throw(ArgumentError("regular spacing on `🌐` requires `LatLon` coordinates")) + end + + T = CoordRefSystems.mactype(C) + nc = CoordRefSystems.ncoords(C) + us = CoordRefSystems.units(C) + ns = length(spacing) + + if N ≠ nc + throw(ArgumentError(""" + A $N-dimensional regular grid requires an origin with $N coordinates. + The provided origin has $nc coordinates. + """)) + end + + if ns ≠ nc + throw(ArgumentError(""" + A $N-dimensional regular grid requires $N spacing values. + The provided spacing has $ns values. + """)) + end + + sp = ntuple(i -> numconvert(T, _withunit(spacing[i], us[i])), nc) + + RegularGrid{M,C,typeof(sp),N}(origin, sp, offset, topology) +end + +function RegularGrid(dims::Dims{N}, origin::Point, spacing::Tuple, offset::Dims{N}=ntuple(i -> 1, N)) where {N} + if !all(>(0), dims) + throw(ArgumentError("dimensions must be positive")) + end + RegularGrid(origin, spacing, offset, GridTopology(dims)) +end + +spacing(g::RegularGrid) = g.spacing + +offset(g::RegularGrid) = g.offset + +function vertex(g::RegularGrid, ijk::Dims) + ctor = CoordRefSystems.constructor(crs(g)) + orig = CoordRefSystems.values(coords(g.origin)) + vals = orig .+ (ijk .- g.offset) .* g.spacing + Point(ctor(vals...)) +end + +@generated function xyz(g::RegularGrid{M,C,S,N}) where {M,C,S,N} + exprs = ntuple(N) do i + :(range(start=orig[$i], step=spac[$i], length=(dims[$i] + 1))) + end + + quote + dims = size(g) + spac = spacing(g) + orig = CoordRefSystems.values(coords(g.origin)) + ($(exprs...),) + end +end + +XYZ(g::RegularGrid) = XYZ(xyz(g)) + +function Base.getindex(g::RegularGrid, I::CartesianIndices) + @boundscheck _checkbounds(g, I) + dims = size(I) + offset = g.offset .- Tuple(first(I)) .+ 1 + RegularGrid(dims, g.origin, g.spacing, offset) +end + +function ==(g₁::RegularGrid, g₂::RegularGrid) + orig₁ = CoordRefSystems.values(coords(g₁.origin)) + orig₂ = CoordRefSystems.values(coords(g₂.origin)) + g₁.topology == g₂.topology && g₁.spacing == g₂.spacing && orig₁ .- orig₂ == (g₁.offset .- g₂.offset) .* g₁.spacing +end + +# ----------- +# IO METHODS +# ----------- + +function Base.summary(io::IO, g::RegularGrid) + dims = join(size(g.topology), "×") + name = prettyname(g) + print(io, "$dims $name") +end + +function Base.show(io::IO, ::MIME"text/plain", g::RegularGrid) + summary(io, g) + println(io) + println(io, "├─ minimum: ", minimum(g)) + println(io, "├─ maximum: ", maximum(g)) + print(io, "└─ spacing: ", spacing(g)) +end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_withunit(x::Number, u) = x * u +_withunit(x::Quantity, u) = uconvert(u, x) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index a8e6bd03c..aca9b65b1 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -49,9 +49,11 @@ applycoord(t::Proj{<:Projected}, g::Primitive{<:🌐}) = TransformedGeometry(g, applycoord(t::Proj{<:Geographic}, g::Primitive{<:𝔼}) = TransformedGeometry(g, t) -applycoord(t::Proj, g::RectilinearGrid) = applycoord(t, convert(SimpleMesh, g)) +applycoord(t::Proj, g::RegularGrid) = TransformedGrid(g, t) -applycoord(t::Proj, g::StructuredGrid) = applycoord(t, convert(SimpleMesh, g)) +applycoord(t::Proj, g::RectilinearGrid) = TransformedGrid(g, t) + +applycoord(t::Proj, g::StructuredGrid) = TransformedGrid(g, t) # ----------- # IO METHODS diff --git a/src/units.jl b/src/units.jl index ff22a61b8..36067a8e7 100644 --- a/src/units.jl +++ b/src/units.jl @@ -18,3 +18,10 @@ addunit(x::Number, u) = x * u addunit(x::AbstractArray{<:Number}, u) = x * u addunit(::Quantity, _) = throw(ArgumentError("invalid units, please check the documentation")) addunit(::AbstractArray{<:Quantity}, _) = throw(ArgumentError("invalid units, please check the documentation")) + +""" + numconvert(T, x) + +Converts the number type of quantity `x` to `T`. +""" +numconvert(::Type{T}, x::Quantity{S,D,U}) where {T,S,D,U} = convert(Quantity{T,D,U}, x) diff --git a/src/utils/basic.jl b/src/utils/basic.jl index d80298a07..e1b015914 100644 --- a/src/utils/basic.jl +++ b/src/utils/basic.jl @@ -42,11 +42,11 @@ collectat(vec::AbstractVector, inds) = vec[inds] Generate the coordinate arrays `XYZ` from the coordinate vectors `xyz`. """ -@generated function XYZ(xyz::NTuple{Dim,<:AbstractVector{T}}) where {Dim,T} +@generated function XYZ(xyz::NTuple{Dim,AbstractVector}) where {Dim} exprs = ntuple(Dim) do d quote a = xyz[$d] - A = Array{T,Dim}(undef, length.(xyz)) + A = Array{eltype(a),Dim}(undef, length.(xyz)) @nloops $Dim i A begin @nref($Dim, A, i) = a[$(Symbol(:i_, d))] end diff --git a/test/crs.jl b/test/crs.jl index 0eaba0281..8b3445686 100644 --- a/test/crs.jl +++ b/test/crs.jl @@ -33,7 +33,7 @@ @test crs(d) <: Mercator{WGS84Latest} d = PointSet([merc(0, 0), merc(1, 1)]) @test crs(d) <: Mercator{WGS84Latest} - d = CartesianGrid((10, 10), merc(0, 0), (T(1), T(1))) + d = RegularGrid((10, 10), merc(0, 0), (T(1), T(1))) @test crs(d) <: Mercator{WGS84Latest} p = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) @@ -137,7 +137,7 @@ end @test crs(d) <: LatLon{WGS84Latest} d = PointSet([latlon(0, 0), latlon(1, 1)]) @test crs(d) <: LatLon{WGS84Latest} - d = CartesianGrid((10, 10, 10), latlon(0, 0), (T(1), T(1), T(1))) + d = RegularGrid((10, 10), latlon(0, 0), (T(1), T(1))) @test crs(d) <: LatLon{WGS84Latest} p = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) diff --git a/test/meshes.jl b/test/meshes.jl index 30f94da94..2ae1b56be 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -1,3 +1,139 @@ +@testitem "RegularGrid" setup = [Setup] begin + grid = RegularGrid((10, 20), merc(0, 0), T.((1, 1))) + @test embeddim(grid) == 2 + @test paramdim(grid) == 2 + @test crs(grid) <: Mercator + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 20) + @test minimum(grid) == merc(0, 0) + @test maximum(grid) == merc(10, 20) + @test extrema(grid) == (merc(0, 0), merc(10, 20)) + @test spacing(grid) == (T(1) * u"m", T(1) * u"m") + @test nelements(grid) == 10 * 20 + @test eltype(grid) <: Quadrangle + @test vertex(grid, 1) == vertex(grid, (1, 1)) + @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test grid[1, 1] == grid[1] + @test grid[10, 20] == grid[200] + + grid = RegularGrid((10, 20), latlon(0, 0), T.((1, 1))) + @test embeddim(grid) == 3 + @test paramdim(grid) == 2 + @test crs(grid) <: LatLon + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 20) + @test minimum(grid) == latlon(0, 0) + @test maximum(grid) == latlon(10, 20) + @test extrema(grid) == (latlon(0, 0), latlon(10, 20)) + @test spacing(grid) == (T(1) * u"°", T(1) * u"°") + @test nelements(grid) == 10 * 20 + @test eltype(grid) <: Quadrangle + @test vertex(grid, 1) == vertex(grid, (1, 1)) + @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test grid[1, 1] == grid[1] + @test grid[10, 20] == grid[200] + + grid = RegularGrid((10, 20), Point(Polar(T(0), T(0))), T.((1, 1))) + @test embeddim(grid) == 2 + @test paramdim(grid) == 2 + @test crs(grid) <: Polar + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 20) + @test minimum(grid) == Point(Polar(T(0), T(0))) + @test maximum(grid) == Point(Polar(T(10), T(20))) + @test extrema(grid) == (Point(Polar(T(0), T(0))), Point(Polar(T(10), T(20)))) + @test spacing(grid) == (T(1) * u"m", T(1) * u"rad") + @test nelements(grid) == 10 * 20 + @test eltype(grid) <: Quadrangle + @test vertex(grid, 1) == vertex(grid, (1, 1)) + @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test grid[1, 1] == grid[1] + @test grid[10, 20] == grid[200] + + grid = RegularGrid((10, 20, 30), Point(Cylindrical(T(0), T(0), T(0))), T.((1, 1, 1))) + @test embeddim(grid) == 3 + @test paramdim(grid) == 3 + @test crs(grid) <: Cylindrical + @test Meshes.lentype(grid) == ℳ + @test size(grid) == (10, 20, 30) + @test minimum(grid) == Point(Cylindrical(T(0), T(0), T(0))) + @test maximum(grid) == Point(Cylindrical(T(10), T(20), T(30))) + @test extrema(grid) == (Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(10), T(20), T(30)))) + @test spacing(grid) == (T(1) * u"m", T(1) * u"rad", T(1) * u"m") + @test nelements(grid) == 10 * 20 * 30 + @test eltype(grid) <: Hexahedron + @test vertex(grid, 1) == vertex(grid, (1, 1, 1)) + @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21, 31)) + @test grid[1, 1, 1] == grid[1] + @test grid[10, 20, 30] == grid[6000] + + # spacing unit and numtype + grid = RegularGrid((10, 20), Point(Polar(T(0) * u"cm", T(0) * u"rad")), (10.0 * u"mm", 1.0f0 * u"rad")) + @test unit.(spacing(grid)) == (u"cm", u"rad") + @test Unitful.numtype.(spacing(grid)) == (T, T) + + # xyz & XYZ + grid = RegularGrid((10, 10), latlon(0, 0), T.((1, 1))) + @test Meshes.xyz(grid) == (T.(0:10) * u"°", T.(0:10) * u"°") + x = T.(0:10) * u"°" + y = T.(0:10)' * u"°" + @test Meshes.XYZ(grid) == (repeat(x, 1, 11), repeat(y, 11, 1)) + grid = RegularGrid((10, 10), Point(Polar(T(0), T(0))), T.((1, 1))) + @test Meshes.xyz(grid) == (T.(0:10) * u"m", T.(0:10) * u"rad") + x = T.(0:10) * u"m" + y = T.(0:10)' * u"rad" + @test Meshes.XYZ(grid) == (repeat(x, 1, 11), repeat(y, 11, 1)) + + # indexing into a subgrid + grid = RegularGrid((10, 10), latlon(0, 0), T.((1, 1))) + sub = grid[1:2, 1:2] + @test size(sub) == (2, 2) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == minimum(grid) + @test maximum(sub) == latlon(2, 2) + grid = RegularGrid((10, 10), Point(Polar(T(0), T(0))), T.((1, 1))) + sub = grid[2:4, 3:7] + @test size(sub) == (3, 5) + @test spacing(sub) == spacing(grid) + @test minimum(sub) == Point(Polar(T(1), T(2))) + @test maximum(sub) == Point(Polar(T(4), T(7))) + + # type stability + grid = RegularGrid((10, 20), Point(Polar(T(0), T(0))), T.((1, 1))) + @inferred vertex(grid, (1, 1)) + @inferred grid[1, 1] + @inferred grid[1:2, 1:2] + @inferred Meshes.xyz(grid) + @inferred Meshes.XYZ(grid) + + # error: dimensions must be positive + @test_throws ArgumentError RegularGrid((-10, -10), latlon(0, 0), T.((1, 1))) + # error: spacing must be positive + @test_throws ArgumentError RegularGrid((10, 10), latlon(0, 0), T.((-1, -1))) + # error: regular spacing on `🌐` requires `LatLon` coordinates + p = latlon(0, 0) |> Proj(Cartesian) + @test_throws ArgumentError RegularGrid((10, 10), p, T.((1, 1))) + # error: the number of dimensions must be equal to the number of coordinates + @test_throws ArgumentError RegularGrid((10, 10, 10), latlon(0, 0), T.((1, 1))) + # error: the number of spacings must be equal to the number of coordinates + @test_throws ArgumentError RegularGrid((10, 10), latlon(0, 0), T.((1, 1, 1))) + + grid = RegularGrid((10, 10), latlon(0, 0), T.((1, 1))) + if T == Float32 + @test sprint(show, MIME"text/plain"(), grid) == """ + 10×10 RegularGrid + ├─ minimum: Point(lat: 0.0f0°, lon: 0.0f0°) + ├─ maximum: Point(lat: 10.0f0°, lon: 10.0f0°) + └─ spacing: (1.0f0°, 1.0f0°)""" + elseif T == Float64 + @test sprint(show, MIME"text/plain"(), grid) == """ + 10×10 RegularGrid + ├─ minimum: Point(lat: 0.0°, lon: 0.0°) + ├─ maximum: Point(lat: 10.0°, lon: 10.0°) + └─ spacing: (1.0°, 1.0°)""" + end +end + @testitem "CartesianGrid" setup = [Setup] begin grid = cartgrid(100) @test embeddim(grid) == 1 diff --git a/test/transforms.jl b/test/transforms.jl index 96dee2932..e854a132f 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1206,8 +1206,7 @@ end f = Proj(Polar) d = CartesianGrid((10, 10), cart(1, 1), T.((1, 1))) r, c = TB.apply(f, d) - @test r isa CartesianGrid - @test r ≈ CartesianGrid((10, 10), Point(Polar(T(√2), T(π / 4))), T.((1, 1))) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) # ---------------- # RECTILINEARGRID From 54be1d2604063c9e95ac0f48d6fdf44c1a6ec25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 10 Sep 2024 12:56:23 -0300 Subject: [PATCH 326/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c470a0a86..020241efe 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.3" +version = "0.51.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 838eca43e485347117e5c033d1a4e49cd8855bd9 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:00:34 -0300 Subject: [PATCH 327/423] Add support for custom CRS in `RectilinearGrid` (#1049) * Add support for custom CRS in 'RectilinearGrid' * Update tests * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Fix typo * Rename variables * Apply suggestions * Fix LengthUnit * Fix tests * Fix more tests * Remove unused code --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/coarsening/regular.jl | 10 +-- src/domains.jl | 2 +- src/domains/meshes/rectilineargrid.jl | 95 ++++++++++++++++++--------- src/domains/meshes/regulargrid.jl | 2 +- src/refinement/regular.jl | 8 +-- src/transforms/lengthunit.jl | 2 +- src/transforms/scale.jl | 4 +- src/transforms/shadow.jl | 4 +- src/transforms/translate.jl | 4 +- test/meshes.jl | 43 ++++++++++-- test/transforms.jl | 4 +- 11 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 85800d4a5..d1453ce3b 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -25,13 +25,13 @@ function coarsen(grid::CartesianGrid, method::RegularCoarsening) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end -function coarsen(grid::RectilinearGrid{Datum}, method::RegularCoarsening) where {Datum} - factors = fitdims(method.factors, embeddim(grid)) +function coarsen(grid::RectilinearGrid, method::RegularCoarsening) + factors = fitdims(method.factors, paramdim(grid)) dims = vsize(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) + rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid)) xyzₛ = xyz(grid) - xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], embeddim(grid)) - RectilinearGrid{Datum}(xyzₜ) + xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], paramdim(grid)) + RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum} diff --git a/src/domains.jl b/src/domains.jl index ca4eaa8c5..24730fa6c 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -154,4 +154,4 @@ Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m)) Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{datum(crs(g))}(XYZ(g)) -Base.convert(::Type{RectilinearGrid}, g::CartesianGrid) = RectilinearGrid{datum(crs(g))}(xyz(g)) +Base.convert(::Type{RectilinearGrid}, g::RegularGrid) = RectilinearGrid{manifold(g),crs(g)}(xyz(g)) diff --git a/src/domains/meshes/rectilineargrid.jl b/src/domains/meshes/rectilineargrid.jl index d5e55d22c..ddfc4de42 100644 --- a/src/domains/meshes/rectilineargrid.jl +++ b/src/domains/meshes/rectilineargrid.jl @@ -4,10 +4,10 @@ """ RectilinearGrid(x, y, z, ...) - RectilinearGrid{Datum}(x, y, z, ...) + RectilinearGrid{M,C}(x, y, z, ...) A rectilinear grid with vertices at sorted coordinates `x`, `y`, `z`, ..., -and a given `Datum` (default to `NoDatum`). +manifold `M` (default to `𝔼`) and CRS type `C` (default to `Cartesian`). ## Examples @@ -20,49 +20,84 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0] julia> RectilinearGrid(x, y) ``` """ -struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim} - xyz::NTuple{Dim,V} - topology::GridTopology{Dim} - - function RectilinearGrid{Datum}( - xyz::NTuple{Dim,<:AbstractVector{<:Len}}, - topology::GridTopology{Dim} - ) where {Datum,Dim} - coords = float.(xyz) - V = eltype(coords) - new{Datum,Dim,eltype(V),V}(coords, topology) - end +struct RectilinearGrid{M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractVector}} <: Grid{M,C,N} + xyz::X + topology::GridTopology{N} + RectilinearGrid{M,C,N,X}(xyz, topology) where {M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractVector}} = new(xyz, topology) end -RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector}, topology::GridTopology{Dim}) where {Datum,Dim} = - RectilinearGrid{Datum}(addunit.(xyz, u"m"), topology) +function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}, topology::GridTopology{N}) where {M<:Manifold,C<:CRS,N} + if M <: 🌐 && !(C <: LatLon) + throw(ArgumentError("rectilinear grid on `🌐` requires `LatLon` coordinates")) + end + + T = CoordRefSystems.mactype(C) + nc = CoordRefSystems.ncoords(C) + us = CoordRefSystems.units(C) + + if N ≠ nc + throw(ArgumentError(""" + A $N-dimensional rectilinear grid requires a CRS with $N coordinates. + The provided CRS has $nc coordinates. + """)) + end + + xyz′ = ntuple(i -> numconvert.(T, _withunit.(xyz[i], us[i])), nc) + RectilinearGrid{M,C,N,typeof(xyz′)}(xyz′, topology) +end -function RectilinearGrid{Datum}(xyz::Tuple) where {Datum} - coords = promote(collect.(xyz)...) - topology = GridTopology(length.(coords) .- 1) - RectilinearGrid{Datum}(coords, topology) +function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}) where {M<:Manifold,C<:CRS,N} + topology = GridTopology(length.(xyz) .- 1) + RectilinearGrid{M,C}(xyz, topology) end -RectilinearGrid{Datum}(xyz...) where {Datum} = RectilinearGrid{Datum}(xyz) +RectilinearGrid{M,C}(xyz::AbstractVector...) where {M<:Manifold,C<:CRS} = RectilinearGrid{M,C}(xyz) + +function RectilinearGrid(xyz::NTuple{N,AbstractVector}) where {N} + try + L = promote_type(ntuple(i -> _lentype(eltype(xyz[i])), N)...) + M = 𝔼{N} + C = Cartesian{NoDatum,N,L} + RectilinearGrid{M,C}(xyz) + catch + throw(ArgumentError("invalid units for cartesian coordinates")) + end +end -RectilinearGrid(args...) = RectilinearGrid{NoDatum}(args...) +RectilinearGrid(xyz::AbstractVector...) = RectilinearGrid(xyz) -vertex(g::RectilinearGrid{Datum}, ijk::Dims) where {Datum} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk))) +function vertex(g::RectilinearGrid, ijk::Dims) + ctor = CoordRefSystems.constructor(crs(g)) + Point(ctor(getindex.(g.xyz, ijk)...)) +end xyz(g::RectilinearGrid) = g.xyz XYZ(g::RectilinearGrid) = XYZ(xyz(g)) -function Base.getindex(g::RectilinearGrid{Datum}, I::CartesianIndices) where {Datum} - @boundscheck _checkbounds(g, I) - dims = size(I) - start = Tuple(first(I)) - stop = Tuple(last(I)) .+ 1 - xyz = ntuple(i -> g.xyz[i][start[i]:stop[i]], embeddim(g)) - RectilinearGrid{Datum}(xyz, GridTopology(dims)) +@generated function Base.getindex(g::RectilinearGrid{M,C,N}, I::CartesianIndices) where {M,C,N} + exprs = ntuple(N) do i + :(g.xyz[$i][start[$i]:stop[$i]]) + end + + quote + @boundscheck _checkbounds(g, I) + dims = size(I) + start = Tuple(first(I)) + stop = Tuple(last(I)) .+ 1 + xyz = ($(exprs...),) + RectilinearGrid{M,C}(xyz, GridTopology(dims)) + end end function Base.summary(io::IO, g::RectilinearGrid) join(io, size(g), "×") print(io, " RectilinearGrid") end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_lentype(::Type{T}) where {T<:Len} = T +_lentype(::Type{T}) where {T<:Number} = Met{T} diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl index 71ec3552b..eb63f0174 100644 --- a/src/domains/meshes/regulargrid.jl +++ b/src/domains/meshes/regulargrid.jl @@ -17,7 +17,7 @@ A regular grid with dimensions `dims`, with lower left corner of element ``` RegularGrid((10, 20), Point(LatLon(30.0°, 60.0°)), (1.0, 1.0)) # add coordinate units to spacing -RegularGrid((10, 20), Point(Polar(0.0mm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units +RegularGrid((10, 20), Point(Polar(0.0cm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units RegularGrid((10, 20), Point(Marcator(0.0, 0.0)), (1.5, 1.5)) RegularGrid((10, 20, 30), Point(Cylindrical(0.0, 0.0, 0.0)), (3.0, 2.0, 1.0)) ``` diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index e7d8fba2e..76c58a445 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -25,11 +25,11 @@ function refine(grid::CartesianGrid, method::RegularRefinement) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end -function refine(grid::RectilinearGrid{Datum}, method::RegularRefinement) where {Datum} - factors = fitdims(method.factors, embeddim(grid)) +function refine(grid::RectilinearGrid, method::RegularRefinement) + factors = fitdims(method.factors, paramdim(grid)) xyzₛ = xyz(grid) - xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), embeddim(grid)) - RectilinearGrid{Datum}(xyzₜ) + xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), paramdim(grid)) + RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {Datum} diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index a2c78bb59..b66e4d3b0 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -32,7 +32,7 @@ applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len) applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens) -applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g))) +applycoord(t::LengthUnit, g::RectilinearGrid) = TransformedGrid(g, t) applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index c734c4142..091732cce 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -76,8 +76,8 @@ function applycoord(t::Scale, g::CartesianGrid) CartesianGrid(dims, orig, spac, offs) end -applycoord(t::Scale{Dim}, g::RectilinearGrid{Datum,Dim}) where {Datum,Dim} = - RectilinearGrid{Datum}(ntuple(i -> t.factors[i] * xyz(g)[i], Dim)) +applycoord(t::Scale, g::RectilinearGrid) = + RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g))) applycoord(t::Scale{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} = StructuredGrid{Datum}(ntuple(i -> t.factors[i] * XYZ(g)[i], Dim)) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index b7d5842a0..821296664 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -63,6 +63,8 @@ apply(t::Shadow, tr::Torus) = TransformedGeometry(tr, t), nothing apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))), nothing +apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing + # ----------------- # HELPER FUNCTIONS # ----------------- @@ -97,8 +99,6 @@ function _shadow(g::CartesianGrid, dims) CartesianGrid(sz, or, sp, of) end -_shadow(g::RectilinearGrid, dims) = RectilinearGrid{datum(crs(g))}(xyz(g)[dims]) - function _shadow(g::StructuredGrid, dims) ndims = length(size(g)) inds = ntuple(i -> ifelse(i ∈ dims, :, 1), ndims) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index 73aee8486..72a26ea92 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -37,8 +37,8 @@ applycoord(::Translate, v::Vec) = v # SPECIAL CASES # -------------- -apply(t::Translate, g::RectilinearGrid{Datum}) where {Datum} = - RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], embeddim(g))), nothing +apply(t::Translate, g::RectilinearGrid) = + RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], paramdim(g))), nothing revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g)) diff --git a/test/meshes.jl b/test/meshes.jl index 2ae1b56be..2feb166b2 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -431,10 +431,31 @@ end @test vertex(grid, 1) == cart(0, 0) @test vertex(grid, 121) == cart(10, 10) - # constructor with datum & datum propagation - grid = RectilinearGrid{WGS84Latest}(x, y) - @test datum(crs(grid)) === WGS84Latest - @test datum(crs(centroid(grid))) === WGS84Latest + # constructor with manifold and CRS + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + C = typeof(Mercator(T(0), T(0))) + grid = RectilinearGrid{𝔼{2},C}(x, y) + @test manifold(grid) === 𝔼{2} + @test crs(grid) === C + @test crs(grid[1, 1]) === C + @test crs(centroid(grid)) === C + C = typeof(LatLon(T(0), T(0))) + grid = RectilinearGrid{🌐,C}(x, y) + @test manifold(grid) === 🌐 + @test crs(grid) === C + @test crs(grid[1, 1]) === C + @test crs(centroid(grid)) === C + + # units + x = range(zero(T), stop=one(T), length=6) * u"mm" + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"cm" + grid = RectilinearGrid(x, y) + @test unit(Meshes.lentype(grid)) == u"m" + # error: invalid units for cartesian coordinates + x = range(zero(T), stop=one(T), length=6) * u"m" + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"°" + @test_throws ArgumentError RectilinearGrid(x, y) # conversion cg = cartgrid(10, 10) @@ -453,6 +474,20 @@ end @test topology(rg) == topology(cg) @test vertices(rg) == vertices(cg) + # type stability + x = range(zero(T), stop=one(T), length=6) * u"mm" + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"cm" + ρ = range(zero(T), stop=one(T), length=6) + ϕ = range(zero(T), stop=T(2π), length=6) + C = typeof(Polar(T(0), T(0))) + grid = RectilinearGrid{𝔼{2},C}(ρ, ϕ) + @inferred RectilinearGrid(x, y) + @inferred RectilinearGrid{𝔼{2},C}(ρ, ϕ) + @inferred vertex(grid, (1, 1)) + @inferred grid[1, 1] + @inferred grid[1:2, 1:2] + @inferred Meshes.XYZ(grid) + x = range(zero(T), stop=one(T), length=6) y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) diff --git a/test/transforms.jl b/test/transforms.jl index e854a132f..e9b43c399 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1400,7 +1400,6 @@ end f = LengthUnit(u"cm") d = convert(RectilinearGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa RectilinearGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) # --------------- @@ -1621,8 +1620,7 @@ end f = Shadow(:xy) d = convert(RectilinearGrid, cartgrid(10, 11, 12)) r, c = TB.apply(f, d) - @test r isa RectilinearGrid - @test r == RectilinearGrid(Meshes.xyz(d)[1], Meshes.xyz(d)[2]) + @test r == SimpleMesh(f.(vertices(d)), topology(d)) # --------------- # STRUCTUREDGRID From e194db30cb09efe3afd95b328d5a4130a2bb5683 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:36:49 -0300 Subject: [PATCH 328/423] Add support for custom CRS in `StructuredGrid` (#1051) * Add support for custom CRS in 'StructuredGrid' * More adjustments * Fix tests * Add more tests * Move helpers to units.jl --- src/coarsening/regular.jl | 12 ++-- src/domains.jl | 2 +- src/domains/meshes/rectilineargrid.jl | 21 ++----- src/domains/meshes/regulargrid.jl | 9 +-- src/domains/meshes/structuredgrid.jl | 89 +++++++++++++++++++-------- src/refinement/regular.jl | 10 +-- src/transforms/lengthunit.jl | 2 +- src/transforms/scale.jl | 4 +- src/transforms/shadow.jl | 8 +-- src/transforms/translate.jl | 4 +- src/units.jl | 19 ++++++ test/meshes.jl | 70 ++++++++++++++++++++- test/transforms.jl | 4 +- 13 files changed, 175 insertions(+), 79 deletions(-) diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index d1453ce3b..716c41e0f 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -21,7 +21,7 @@ end RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) function coarsen(grid::CartesianGrid, method::RegularCoarsening) - factors = fitdims(method.factors, embeddim(grid)) + factors = fitdims(method.factors, paramdim(grid)) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end @@ -34,13 +34,13 @@ function coarsen(grid::RectilinearGrid, method::RegularCoarsening) RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end -function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum} - factors = fitdims(method.factors, embeddim(grid)) +function coarsen(grid::StructuredGrid, method::RegularCoarsening) + factors = fitdims(method.factors, paramdim(grid)) dims = vsize(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid)) + rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid)) XYZₛ = XYZ(grid) - XYZₜ = ntuple(i -> XYZₛ[i][rngs...], embeddim(grid)) - StructuredGrid{Datum}(XYZₜ) + XYZₜ = ntuple(i -> XYZₛ[i][rngs...], paramdim(grid)) + StructuredGrid{manifold(grid),crs(grid)}(XYZₜ) end coarsen(grid::TransformedGrid, method::RegularCoarsening) = diff --git a/src/domains.jl b/src/domains.jl index 24730fa6c..be6d4d729 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -152,6 +152,6 @@ Base.convert(::Type{GeometrySet}, d::Domain) = GeometrySet(collect(d)) Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m)) -Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{datum(crs(g))}(XYZ(g)) +Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{manifold(g),crs(g)}(XYZ(g)) Base.convert(::Type{RectilinearGrid}, g::RegularGrid) = RectilinearGrid{manifold(g),crs(g)}(xyz(g)) diff --git a/src/domains/meshes/rectilineargrid.jl b/src/domains/meshes/rectilineargrid.jl index ddfc4de42..6952763d8 100644 --- a/src/domains/meshes/rectilineargrid.jl +++ b/src/domains/meshes/rectilineargrid.jl @@ -42,7 +42,7 @@ function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}, topology::GridTopol """)) end - xyz′ = ntuple(i -> numconvert.(T, _withunit.(xyz[i], us[i])), nc) + xyz′ = ntuple(i -> numconvert.(T, withunit.(xyz[i], us[i])), nc) RectilinearGrid{M,C,N,typeof(xyz′)}(xyz′, topology) end @@ -54,14 +54,10 @@ end RectilinearGrid{M,C}(xyz::AbstractVector...) where {M<:Manifold,C<:CRS} = RectilinearGrid{M,C}(xyz) function RectilinearGrid(xyz::NTuple{N,AbstractVector}) where {N} - try - L = promote_type(ntuple(i -> _lentype(eltype(xyz[i])), N)...) - M = 𝔼{N} - C = Cartesian{NoDatum,N,L} - RectilinearGrid{M,C}(xyz) - catch - throw(ArgumentError("invalid units for cartesian coordinates")) - end + L = promote_type(ntuple(i -> aslentype(eltype(xyz[i])), N)...) + M = 𝔼{N} + C = Cartesian{NoDatum,N,L} + RectilinearGrid{M,C}(xyz) end RectilinearGrid(xyz::AbstractVector...) = RectilinearGrid(xyz) @@ -94,10 +90,3 @@ function Base.summary(io::IO, g::RectilinearGrid) join(io, size(g), "×") print(io, " RectilinearGrid") end - -# ----------------- -# HELPER FUNCTIONS -# ----------------- - -_lentype(::Type{T}) where {T<:Len} = T -_lentype(::Type{T}) where {T<:Number} = Met{T} diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl index eb63f0174..f382bfe31 100644 --- a/src/domains/meshes/regulargrid.jl +++ b/src/domains/meshes/regulargrid.jl @@ -67,7 +67,7 @@ function RegularGrid( """)) end - sp = ntuple(i -> numconvert(T, _withunit(spacing[i], us[i])), nc) + sp = ntuple(i -> numconvert(T, withunit(spacing[i], us[i])), nc) RegularGrid{M,C,typeof(sp),N}(origin, sp, offset, topology) end @@ -135,10 +135,3 @@ function Base.show(io::IO, ::MIME"text/plain", g::RegularGrid) println(io, "├─ maximum: ", maximum(g)) print(io, "└─ spacing: ", spacing(g)) end - -# ----------------- -# HELPER FUNCTIONS -# ----------------- - -_withunit(x::Number, u) = x * u -_withunit(x::Quantity, u) = uconvert(u, x) diff --git a/src/domains/meshes/structuredgrid.jl b/src/domains/meshes/structuredgrid.jl index 24e0ecfc3..747b5a375 100644 --- a/src/domains/meshes/structuredgrid.jl +++ b/src/domains/meshes/structuredgrid.jl @@ -4,10 +4,10 @@ """ StructuredGrid(X, Y, Z, ...) - StructuredGrid{Datum}(X, Y, Z, ...) + StructuredGrid{M,C}(X, Y, Z, ...) A structured grid with vertices at sorted coordinates `X`, `Y`, `Z`, ..., -and a given `Datum` (default to `NoDatum`). +manifold `M` (default to `𝔼`) and CRS type `C` (default to `Cartesian`). ## Examples @@ -20,41 +20,78 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) julia> StructuredGrid(X, Y) ``` """ -struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim} - XYZ::NTuple{Dim,A} - topology::GridTopology{Dim} - - function StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim} - coords = float.(XYZ) - A = eltype(coords) - new{Datum,Dim,eltype(A),A}(coords, topology) +struct StructuredGrid{M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractArray}} <: Grid{M,C,N} + XYZ::X + topology::GridTopology{N} + StructuredGrid{M,C,N,X}(XYZ, topology) where {M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractArray}} = new(XYZ, topology) +end + +function StructuredGrid{M,C}(XYZ::NTuple{N,AbstractArray}, topology::GridTopology{N}) where {M<:Manifold,C<:CRS,N} + if M <: 🌐 && !(C <: LatLon) + throw(ArgumentError("rectilinear grid on `🌐` requires `LatLon` coordinates")) + end + + T = CoordRefSystems.mactype(C) + nc = CoordRefSystems.ncoords(C) + us = CoordRefSystems.units(C) + + if N ≠ nc + throw(ArgumentError(""" + A $N-dimensional structured grid requires a CRS with $N coordinates. + The provided CRS has $nc coordinates. + """)) end + + XYZ′ = ntuple(i -> numconvert.(T, withunit.(XYZ[i], us[i])), nc) + StructuredGrid{M,C,N,typeof(XYZ′)}(XYZ′, topology) end -StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray}, topology::GridTopology{Dim}) where {Datum,Dim} = - StructuredGrid{Datum}(addunit.(XYZ, u"m"), topology) +function StructuredGrid{M,C}(XYZ::NTuple{N,AbstractArray}) where {M<:Manifold,C<:CRS,N} + if !allequal(size(X) for X in XYZ) + throw(ArgumentError("all coordinate arrays must be the same size")) + end + + nd = ndims(first(XYZ)) -function StructuredGrid{Datum}(XYZ::Tuple) where {Datum} - coords = promote(XYZ...) - topology = GridTopology(size(first(coords)) .- 1) - StructuredGrid{Datum}(coords, topology) + if nd ≠ N + throw(ArgumentError(""" + A $N-dimensional structured grid requires coordinate arrays with $N dimensions. + The provided coordinate arrays have $nd dimensions. + """)) + end + + topology = GridTopology(size(first(XYZ)) .- 1) + StructuredGrid{M,C}(XYZ, topology) end -StructuredGrid{Datum}(XYZ...) where {Datum} = StructuredGrid{Datum}(XYZ) +StructuredGrid{M,C}(XYZ::AbstractArray...) where {M<:Manifold,C<:CRS} = StructuredGrid{M,C}(XYZ) -StructuredGrid(args...) = StructuredGrid{NoDatum}(args...) +function StructuredGrid(XYZ::NTuple{N,AbstractArray}) where {N} + L = promote_type(ntuple(i -> aslentype(eltype(XYZ[i])), N)...) + M = 𝔼{N} + C = Cartesian{NoDatum,N,L} + StructuredGrid{M,C}(XYZ) +end -vertex(g::StructuredGrid{Datum}, ijk::Dims) where {Datum} = - Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], embeddim(g)))) +StructuredGrid(XYZ::AbstractArray...) = StructuredGrid(XYZ) + +function vertex(g::StructuredGrid, ijk::Dims) + ctor = CoordRefSystems.constructor(crs(g)) + Point(ctor(ntuple(d -> g.XYZ[d][ijk...], paramdim(g))...)) +end XYZ(g::StructuredGrid) = g.XYZ -function Base.getindex(g::StructuredGrid{Datum}, I::CartesianIndices) where {Datum} - @boundscheck _checkbounds(g, I) - dims = size(I) - cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) - XYZ = ntuple(i -> g.XYZ[i][cinds], embeddim(g)) - StructuredGrid{Datum}(XYZ, GridTopology(dims)) +@generated function Base.getindex(g::StructuredGrid{M,C,N}, I::CartesianIndices) where {M,C,N} + exprs = ntuple(i -> :(g.XYZ[$i][cinds]), N) + + quote + @boundscheck _checkbounds(g, I) + dims = size(I) + cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) + XYZ = ($(exprs...),) + StructuredGrid{M,C}(XYZ, GridTopology(dims)) + end end function Base.summary(io::IO, g::StructuredGrid) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index 76c58a445..d4f0ebb5e 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -21,7 +21,7 @@ end RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors) function refine(grid::CartesianGrid, method::RegularRefinement) - factors = fitdims(method.factors, embeddim(grid)) + factors = fitdims(method.factors, paramdim(grid)) CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end @@ -32,10 +32,10 @@ function refine(grid::RectilinearGrid, method::RegularRefinement) RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end -function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {Datum} - factors = fitdims(method.factors, embeddim(grid)) +function refine(grid::StructuredGrid, method::RegularRefinement) + factors = fitdims(method.factors, paramdim(grid)) XYZ′ = _XYZ(grid, factors) - StructuredGrid{Datum}(XYZ′) + StructuredGrid{manifold(grid),crs(grid)}(XYZ′) end refine(grid::TransformedGrid, method::RegularRefinement) = @@ -49,7 +49,7 @@ function _refinedims(x, f) x′ end -_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(embeddim(grid)), factors) +_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(paramdim(grid)), factors) function _XYZ(grid::StructuredGrid, ::Val{2}, factors::Dims{2}) T = numtype(lentype(grid)) diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index b66e4d3b0..30bdf2069 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -34,7 +34,7 @@ applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, applycoord(t::LengthUnit, g::RectilinearGrid) = TransformedGrid(g, t) -applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g))) +applycoord(t::LengthUnit, g::StructuredGrid) = TransformedGrid(g, t) # ----------------- # HELPER FUNCTIONS diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 091732cce..d1241bdda 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -79,5 +79,5 @@ end applycoord(t::Scale, g::RectilinearGrid) = RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g))) -applycoord(t::Scale{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} = - StructuredGrid{Datum}(ntuple(i -> t.factors[i] * XYZ(g)[i], Dim)) +applycoord(t::Scale, g::StructuredGrid) = + StructuredGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * XYZ(g)[i], paramdim(g))) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index 821296664..ce37270ae 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -65,6 +65,8 @@ apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))) apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing +apply(t::Shadow, g::StructuredGrid) = TransformedGrid(g, t), nothing + # ----------------- # HELPER FUNCTIONS # ----------------- @@ -99,12 +101,6 @@ function _shadow(g::CartesianGrid, dims) CartesianGrid(sz, or, sp, of) end -function _shadow(g::StructuredGrid, dims) - ndims = length(size(g)) - inds = ntuple(i -> ifelse(i ∈ dims, :, 1), ndims) - StructuredGrid{datum(crs(g))}(map(X -> X[inds...], XYZ(g)[dims])) -end - # apply shadow transform recursively @generated function _shadow(g::G, dims) where {G<:GeometryOrDomain} ctor = constructor(G) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index 72a26ea92..c563d9536 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -42,7 +42,7 @@ apply(t::Translate, g::RectilinearGrid) = revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g)) -apply(t::Translate, g::StructuredGrid{Datum}) where {Datum} = - StructuredGrid{Datum}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], embeddim(g))), nothing +apply(t::Translate, g::StructuredGrid) = + StructuredGrid{manifold(g),crs(g)}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], paramdim(g))), nothing revert(t::Translate, g::StructuredGrid, c) = first(apply(inverse(t), g)) diff --git a/src/units.jl b/src/units.jl index 36067a8e7..bf5126b30 100644 --- a/src/units.jl +++ b/src/units.jl @@ -25,3 +25,22 @@ addunit(::AbstractArray{<:Quantity}, _) = throw(ArgumentError("invalid units, pl Converts the number type of quantity `x` to `T`. """ numconvert(::Type{T}, x::Quantity{S,D,U}) where {T,S,D,U} = convert(Quantity{T,D,U}, x) + +""" + withunit(x, u) + +Adds the unit if the argument is not a quantity, +otherwise, converts the unit of `x` to `u`. +""" +withunit(x::Number, u) = x * u +withunit(x::Quantity, u) = uconvert(u, x) + +""" + aslentype(T) + +If `T` has length unit, return it. If `T` is number, return it with meter unit. +Otherwise, throw an error. +""" +aslentype(::Type{T}) where {T<:Len} = T +aslentype(::Type{T}) where {T<:Number} = Met{T} +aslentype(::Type{<:Quantity}) = throw(ArgumentError("invalid units, please use a valid length unit")) diff --git a/test/meshes.jl b/test/meshes.jl index 2feb166b2..994f494c7 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -488,6 +488,16 @@ end @inferred grid[1:2, 1:2] @inferred Meshes.XYZ(grid) + # error: regular spacing on `🌐` requires `LatLon` coordinates + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + z = T[0.0, 0.15, 0.35, 0.65, 0.85, 1.0] + C = typeof(Cartesian(T(0), T(0), T(0))) + @test_throws ArgumentError RectilinearGrid{🌐,C}(x, y, z) + # error: the number of dimensions must be equal to the number of coordinates + C = typeof(LatLon(T(0), T(0))) + @test_throws ArgumentError RectilinearGrid{🌐,C}(x, y, z) + x = range(zero(T), stop=one(T), length=6) y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] grid = RectilinearGrid(x, y) @@ -586,9 +596,31 @@ end @test_throws BoundsError grid[2:6, :] @test Meshes.XYZ(grid) == (X * u"m", Y * u"m") - # constructor with datum - grid = StructuredGrid{WGS84Latest}(X, Y) - @test datum(crs(grid)) === WGS84Latest + # constructor with manifold and CRS + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) + C = typeof(Mercator(T(0), T(0))) + grid = StructuredGrid{𝔼{2},C}(X, Y) + @test manifold(grid) === 𝔼{2} + @test crs(grid) === C + @test crs(grid[1, 1]) === C + @test crs(centroid(grid)) === C + C = typeof(LatLon(T(0), T(0))) + grid = StructuredGrid{🌐,C}(X, Y) + @test manifold(grid) === 🌐 + @test crs(grid) === C + @test crs(grid[1, 1]) === C + @test crs(centroid(grid)) === C + + # units + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) * u"mm" + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) * u"cm" + grid = StructuredGrid(X, Y) + @test unit(Meshes.lentype(grid)) == u"m" + # error: invalid units for cartesian coordinates + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) * u"m" + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) * u"°" + @test_throws ArgumentError StructuredGrid(X, Y) # conversion cg = cartgrid(10, 10) @@ -623,6 +655,38 @@ end @test topology(sg) == topology(rg) @test vertices(sg) == vertices(rg) + # type stability + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) * u"mm" + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) * u"cm" + ρ = repeat(range(zero(T), stop=one(T), length=6), 1, 6) + ϕ = repeat(range(zero(T), stop=T(2π), length=6)', 6, 1) + C = typeof(Polar(T(0), T(0))) + grid = StructuredGrid{𝔼{2},C}(ρ, ϕ) + @inferred StructuredGrid(X, Y) + @inferred StructuredGrid{𝔼{2},C}(ρ, ϕ) + @inferred vertex(grid, (1, 1)) + @inferred grid[1, 1] + @inferred grid[1:2, 1:2] + + # error: regular spacing on `🌐` requires `LatLon` coordinates + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6, 6) + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1, 6) + Z = repeat(reshape(T[0.0, 0.15, 0.35, 0.65, 0.85, 1.0], 1, 1, 6), 6, 6, 1) + C = typeof(Cartesian(T(0), T(0), T(0))) + @test_throws ArgumentError StructuredGrid{🌐,C}(X, Y, Z) + # error: the number of dimensions must be equal to the number of coordinates + C = typeof(LatLon(T(0), T(0))) + @test_throws ArgumentError StructuredGrid{🌐,C}(X, Y, Z) + # error: all coordinate arrays must be the same size + X = rand(T, 6, 6) + Y = rand(T, 5, 5) + @test_throws ArgumentError StructuredGrid(X, Y) + # error: the number of array dimensions must be equal to the number of grid dimensions + X = rand(T, 6, 6) + Y = rand(T, 6, 6) + Z = rand(T, 6, 6) + @test_throws ArgumentError StructuredGrid(X, Y, Z) + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) grid = StructuredGrid(X, Y) diff --git a/test/transforms.jl b/test/transforms.jl index e9b43c399..5f4114ded 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1409,7 +1409,6 @@ end f = LengthUnit(u"km") d = convert(StructuredGrid, cartgrid(10, 10)) r, c = TB.apply(f, d) - @test r isa StructuredGrid @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) # ----------- @@ -1629,8 +1628,7 @@ end f = Shadow(:xz) d = convert(StructuredGrid, cartgrid(10, 11, 12)) r, c = TB.apply(f, d) - @test r isa StructuredGrid - @test r == StructuredGrid(Meshes.XYZ(d)[1][:, 1, :], Meshes.XYZ(d)[3][:, 1, :]) + @test r == SimpleMesh(f.(vertices(d)), topology(d)) # ----------- # SIMPLEMESH From a3c9ee988a4646649eeda408ee8f1a5641d4e20c Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:23:47 -0300 Subject: [PATCH 329/423] Update 'RegularGrid' implementation (#1053) --- src/domains/meshes/rectilineargrid.jl | 1 + src/domains/meshes/regulargrid.jl | 27 ++++++++++++--------------- src/domains/meshes/structuredgrid.jl | 1 + test/meshes.jl | 4 +--- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/domains/meshes/rectilineargrid.jl b/src/domains/meshes/rectilineargrid.jl index 6952763d8..310947f02 100644 --- a/src/domains/meshes/rectilineargrid.jl +++ b/src/domains/meshes/rectilineargrid.jl @@ -43,6 +43,7 @@ function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}, topology::GridTopol end xyz′ = ntuple(i -> numconvert.(T, withunit.(xyz[i], us[i])), nc) + RectilinearGrid{M,C,N,typeof(xyz′)}(xyz′, topology) end diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl index f382bfe31..16c5ef3ef 100644 --- a/src/domains/meshes/regulargrid.jl +++ b/src/domains/meshes/regulargrid.jl @@ -24,13 +24,13 @@ RegularGrid((10, 20, 30), Point(Cylindrical(0.0, 0.0, 0.0)), (3.0, 2.0, 1.0)) See also [`CartesianGrid`](@ref). """ -struct RegularGrid{M<:Manifold,C<:CRS,S<:Tuple,N} <: Grid{M,C,N} +struct RegularGrid{M<:Manifold,C<:CRS,N,S<:NTuple{N,Quantity}} <: Grid{M,C,N} origin::Point{M,C} spacing::S offset::Dims{N} topology::GridTopology{N} - function RegularGrid{M,C,S,N}(origin, spacing, offset, topology) where {M<:Manifold,C<:CRS,S<:Tuple,N} + function RegularGrid{M,C,N,S}(origin, spacing, offset, topology) where {M<:Manifold,C<:CRS,N,S<:NTuple{N,Quantity}} if !all(s -> s > zero(s), spacing) throw(ArgumentError("spacing must be positive")) end @@ -40,18 +40,17 @@ end function RegularGrid( origin::Point{M,C}, - spacing::Tuple, + spacing::NTuple{N,Number}, offset::Dims{N}, topology::GridTopology{N} ) where {M<:Manifold,C<:CRS,N} - if manifold(origin) <: 🌐 && !(crs(origin) <: LatLon) + if M <: 🌐 && !(C <: LatLon) throw(ArgumentError("regular spacing on `🌐` requires `LatLon` coordinates")) end T = CoordRefSystems.mactype(C) nc = CoordRefSystems.ncoords(C) us = CoordRefSystems.units(C) - ns = length(spacing) if N ≠ nc throw(ArgumentError(""" @@ -60,19 +59,17 @@ function RegularGrid( """)) end - if ns ≠ nc - throw(ArgumentError(""" - A $N-dimensional regular grid requires $N spacing values. - The provided spacing has $ns values. - """)) - end - sp = ntuple(i -> numconvert(T, withunit(spacing[i], us[i])), nc) - RegularGrid{M,C,typeof(sp),N}(origin, sp, offset, topology) + RegularGrid{M,C,N,typeof(sp)}(origin, sp, offset, topology) end -function RegularGrid(dims::Dims{N}, origin::Point, spacing::Tuple, offset::Dims{N}=ntuple(i -> 1, N)) where {N} +function RegularGrid( + dims::Dims{N}, + origin::Point, + spacing::NTuple{N,Number}, + offset::Dims{N}=ntuple(i -> 1, N) +) where {N} if !all(>(0), dims) throw(ArgumentError("dimensions must be positive")) end @@ -90,7 +87,7 @@ function vertex(g::RegularGrid, ijk::Dims) Point(ctor(vals...)) end -@generated function xyz(g::RegularGrid{M,C,S,N}) where {M,C,S,N} +@generated function xyz(g::RegularGrid{M,C,N}) where {M,C,N} exprs = ntuple(N) do i :(range(start=orig[$i], step=spac[$i], length=(dims[$i] + 1))) end diff --git a/src/domains/meshes/structuredgrid.jl b/src/domains/meshes/structuredgrid.jl index 747b5a375..501b8df17 100644 --- a/src/domains/meshes/structuredgrid.jl +++ b/src/domains/meshes/structuredgrid.jl @@ -43,6 +43,7 @@ function StructuredGrid{M,C}(XYZ::NTuple{N,AbstractArray}, topology::GridTopolog end XYZ′ = ntuple(i -> numconvert.(T, withunit.(XYZ[i], us[i])), nc) + StructuredGrid{M,C,N,typeof(XYZ′)}(XYZ′, topology) end diff --git a/test/meshes.jl b/test/meshes.jl index 994f494c7..1e3907f66 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -114,9 +114,7 @@ p = latlon(0, 0) |> Proj(Cartesian) @test_throws ArgumentError RegularGrid((10, 10), p, T.((1, 1))) # error: the number of dimensions must be equal to the number of coordinates - @test_throws ArgumentError RegularGrid((10, 10, 10), latlon(0, 0), T.((1, 1))) - # error: the number of spacings must be equal to the number of coordinates - @test_throws ArgumentError RegularGrid((10, 10), latlon(0, 0), T.((1, 1, 1))) + @test_throws ArgumentError RegularGrid((10, 10, 10), latlon(0, 0), T.((1, 1, 1))) grid = RegularGrid((10, 10), latlon(0, 0), T.((1, 1))) if T == Float32 From 2f0cf15a83f656834c0c9729914fc4a265a84809 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:25:46 -0300 Subject: [PATCH 330/423] Refactor viz of grids (#1055) * Refactor viz of grids * Fix code --- ext/grid.jl | 52 ++++++++++++++++++++++++++++++++++- ext/grid/rectilinear.jl | 60 ++++++++++++++++++++++------------------- ext/grid/structured.jl | 42 ++++++++++++++++------------- ext/grid/transformed.jl | 40 +++------------------------ 4 files changed, 109 insertions(+), 85 deletions(-) diff --git a/ext/grid.jl b/ext/grid.jl index 509a851b6..409995ea6 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -14,7 +14,7 @@ function vizgrid!(plot, ::Type{<:🌐}, pdim::Val, edim::Val) vizgrid!(plot, 𝔼, pdim, edim) end -vizgrid!(plot, M::Type{<:𝔼}, pdim::Val, edim::Val) = vizmesh!(plot, M, pdim, edim) +vizgrid!(plot, M::Type{<:𝔼}, pdim::Val, edim::Val) = vizgridfallback!(plot, M, pdim, edim) # ---------------- # SPECIALIZATIONS @@ -25,6 +25,56 @@ include("grid/rectilinear.jl") include("grid/structured.jl") include("grid/transformed.jl") +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function vizgridfallback!(plot, M, pdim, edim) + grid = plot[:object] + color = plot[:color] + alpha = plot[:alpha] + colormap = plot[:colormap] + colorrange = plot[:colorrange] + + if pdim == Val(2) # visualize quadrangle mesh with texture using uv coords + # decide whether or not to reverse connectivity list + rfunc = Makie.@lift _reverse(crs($grid)) + + verts = Makie.@lift map(asmakie, vertices($grid)) + quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($grid))] + + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + + nverts = Makie.@lift length($verts) + nquads = Makie.@lift length($quads) + ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 + + dims = Makie.@lift size($grid) + texture = if ncolor[] == 1 + Makie.@lift fill($colorant, $dims) + elseif ncolor[] == nquads[] + Makie.@lift reshape($colorant, $dims) + elseif ncolor[] == nverts[] + Makie.@lift reshape($colorant, $dims .+ 1) + else + throw(ArgumentError("invalid number of colors")) + end + + uv = Makie.@lift [Makie.Vec2f(v, 1 - u) for v in range(0, 1, $dims[2] + 1) for u in range(0, 1, $dims[1] + 1)] + + mesh = Makie.@lift GB.Mesh(Makie.meta($verts, uv=$uv), $quads) + + shading = edim == Val(3) ? Makie.FastShading : Makie.NoShading + + Makie.mesh!(plot, mesh, color=texture, shading=shading) + else # fallback to triangle mesh visualization + vizmesh!(plot, M, pdim, edim) + end +end + +_reverse(::Type{<:CRS}) = identity +_reverse(::Type{<:LatLon}) = reverse + # helper functions to create a minimum number # of line segments within Cartesian/Rectilinear grid function xysegments(xs, ys) diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 32b862c48..62c1979b9 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -12,39 +12,43 @@ function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, M::Type{<:𝔼}, pdim::Va segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + if crs(grid[]) <: Cartesian + # process color spec into colorant + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - # number of vertices and colors - nv = Makie.@lift nvertices($grid) - nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 + # number of vertices and colors + nv = Makie.@lift nvertices($grid) + nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 - # grid coordinates - xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) - xs = Makie.@lift $xyz[1] - ys = Makie.@lift $xyz[2] + # grid coordinates + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) + xs = Makie.@lift $xyz[1] + ys = Makie.@lift $xyz[2] - if nc[] == 1 - # visualize bounding box with a single - # color for maximum performance - bbox = Makie.@lift boundingbox($grid) - viz!(plot, bbox, color=colorant) - else - if nc[] == nv[] - # visualize as a simple mesh so that - # colors can be specified at vertices - vizmesh!(plot, M, pdim, edim) + if nc[] == 1 + # visualize bounding box with a single + # color for maximum performance + bbox = Makie.@lift boundingbox($grid) + viz!(plot, bbox, color=colorant) else - # visualize as built-in heatmap - sz = Makie.@lift size($grid) - C = Makie.@lift reshape($colorant, $sz) - Makie.heatmap!(plot, xs, ys, C) + if nc[] == nv[] + # visualize as a simple mesh so that + # colors can be specified at vertices + vizmesh!(plot, M, pdim, edim) + else + # visualize as built-in heatmap + sz = Makie.@lift size($grid) + C = Makie.@lift reshape($colorant, $sz) + Makie.heatmap!(plot, xs, ys, C) + end end - end - if showsegments[] - tup = Makie.@lift xysegments($xs, $ys) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + if showsegments[] + tup = Makie.@lift xysegments($xs, $ys) + x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + end + else + vizgridfallback!(plot, M, pdim, edim) end end diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 912a96176..f9db1d024 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -12,31 +12,35 @@ function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, M::Type{<:𝔼}, pdim::Val segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + if crs(grid[]) <: Cartesian + # process color spec into colorant + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - # number of vertices and colors - nv = Makie.@lift nvertices($grid) - nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 + # number of vertices and colors + nv = Makie.@lift nvertices($grid) + nc = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 - if nc[] == nv[] - # size and coordinates - sz = Makie.@lift size($grid) .+ 1 - XYZ = Makie.@lift map(X -> ustrip.(X), Meshes.XYZ($grid)) - X = Makie.@lift $XYZ[1] - Y = Makie.@lift $XYZ[2] + if nc[] == nv[] + # size and coordinates + sz = Makie.@lift size($grid) .+ 1 + XYZ = Makie.@lift map(X -> ustrip.(X), Meshes.XYZ($grid)) + X = Makie.@lift $XYZ[1] + Y = Makie.@lift $XYZ[2] - # visualize as built-in surface - C = Makie.@lift reshape($colorant, $sz) - Makie.surface!(plot, X, Y, color=C) + # visualize as built-in surface + C = Makie.@lift reshape($colorant, $sz) + Makie.surface!(plot, X, Y, color=C) - if showsegments[] - tup = Makie.@lift structuredsegments($grid) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + if showsegments[] + tup = Makie.@lift structuredsegments($grid) + x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + end + else + vizmesh!(plot, M, pdim, edim) end else - vizmesh!(plot, M, pdim, edim) + vizgridfallback!(plot, M, pdim, edim) end end diff --git a/ext/grid/transformed.jl b/ext/grid/transformed.jl index 0bc05758c..c45175329 100644 --- a/ext/grid/transformed.jl +++ b/ext/grid/transformed.jl @@ -17,47 +17,13 @@ function vizgrid!(plot::Viz{<:Tuple{TransformedGrid}}, M::Type{<:𝔼}, pdim::Va if isoptimized(trans[]) # visualize parent grid and transform visualization grid = Makie.@lift parent($tgrid) - viz!(plot, grid; color, alpha, colormap, showsegments, segmentcolor, segmentsize) + viz!(plot, grid; color, alpha, colormap, colorrange, showsegments, segmentcolor, segmentsize) makietransform!(plot, trans) - elseif pdim == Val(2) # visualize quadrangle mesh with texture using uv coords - # decide whether or not to reverse connectivity list - rfunc = Makie.@lift _reverse(crs($tgrid)) - - verts = Makie.@lift map(asmakie, vertices($tgrid)) - quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($tgrid))] - - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - - nverts = Makie.@lift length($verts) - nquads = Makie.@lift length($quads) - ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 - - dims = Makie.@lift size($tgrid) - texture = if ncolor[] == 1 - Makie.@lift fill($colorant, $dims) - elseif ncolor[] == nquads[] - Makie.@lift reshape($colorant, $dims) - elseif ncolor[] == nverts[] - Makie.@lift reshape($colorant, $dims .+ 1) - else - throw(ArgumentError("invalid number of colors")) - end - - uv = Makie.@lift [Makie.Vec2f(v, 1 - u) for v in range(0, 1, $dims[2] + 1) for u in range(0, 1, $dims[1] + 1)] - - mesh = Makie.@lift GB.Mesh(Makie.meta($verts, uv=$uv), $quads) - - shading = edim == Val(3) ? Makie.FastShading : Makie.NoShading - - Makie.mesh!(plot, mesh, color=texture, shading=shading) - else # fallback to triangle mesh visualization - vizmesh!(plot, M, pdim, edim) + else # fallback to full grid visualization + vizgridfallback!(plot, M, pdim, edim) end end -_reverse(::Type{<:CRS}) = identity -_reverse(::Type{<:LatLon}) = reverse - # -------------- # OPTIMIZATIONS # -------------- From ef1fb34e9de0271a80a1d88ecf4d623c32a5dd97 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:18:09 -0300 Subject: [PATCH 331/423] Add `showsegments` support for `vizgridfallback!` (#1056) * Add 'showsegments' support for 'vizgridfallback!' * Fix 'vizgridfallback!' * Apply suggestions * Apply suggestions * Apply suggestions * Fix code * Apply suggestions * Apply suggestions * Fix typo --- ext/geometryset.jl | 110 ++++++++++++++++++++++++---------------- ext/grid.jl | 20 +++++++- ext/grid/cartesian.jl | 37 +++++++++----- ext/grid/rectilinear.jl | 15 ++++-- ext/grid/structured.jl | 14 +++-- ext/mesh.jl | 92 +++++++++++++++++---------------- 6 files changed, 181 insertions(+), 107 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index df43b1585..35ff30de6 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -2,32 +2,7 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -function Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) - gset = plot[:object] - color = plot[:color] - alpha = plot[:alpha] - colormap = plot[:colormap] - colorrange = plot[:colorrange] - - # process color spec into colorant - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - - # get geometries - geoms = Makie.@lift parent($gset) - - # get geometry types - types = Makie.@lift unique(typeof.($geoms)) - - for G in types[] - inds = Makie.@lift findall(g -> g isa G, $geoms) - gvec = Makie.@lift collect(G, $geoms[$inds]) - colors = Makie.@lift $colorant isa AbstractVector ? $colorant[$inds] : $colorant - M = Makie.@lift manifold(first($gvec)) - pdim = Makie.@lift paramdim(first($gvec)) - edim = Makie.@lift embeddim(first($gvec)) - vizgset!(plot, M[], Val(pdim[]), Val(edim[]), gvec, colors) - end -end +Makie.plot!(plot::Viz{<:Tuple{GeometrySet}}) = vizgeoms!(plot) const ObservableVector{T} = Makie.Observable{<:AbstractVector{T}} @@ -52,14 +27,20 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms::ObservableVector end function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms, colorant) + showpoints = plot[:showpoints] + meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) - showfacets1D!(plot, geoms) + + if showpoints[] + vizfacets!(plot, geoms) + end end function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms::ObservableVector{<:Ray}, colorant) rset = plot[:object] segmentsize = plot[:segmentsize] + showpoints = plot[:showpoints] Dim = embeddim(rset[]) @@ -71,13 +52,20 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms::ObservableVector size = Makie.@lift 0.1 * $segmentsize Makie.arrows!(plot, orig, dirs, color=colorant, arrowsize=size) - showfacets1D!(plot, geoms) + if showpoints[] + vizfacets!(plot, geoms) + end end function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val, geoms, colorant) + showsegments = plot[:showsegments] + meshes = Makie.@lift discretize.($geoms) vizmany!(plot, meshes, colorant) - showfacets2D!(plot, geoms) + + if showsegments[] + vizfacets!(plot, geoms) + end end const PolygonLike = Union{Polygon,MultiPolygon} @@ -104,29 +92,63 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val, geoms, colorant) vizmany!(plot, meshes, colorant) end -function showfacets1D!(plot, geoms) - showpoints = plot[:showpoints] +vizfacets!(plot::Viz{<:Tuple{GeometrySet}}) = vizgeoms!(plot, facets=false) + +function vizfacets!(plot::Viz{<:Tuple{GeometrySet}}, geoms) + M = Makie.@lift manifold(first($geoms)) + pdim = Makie.@lift paramdim(first($geoms)) + edim = Makie.@lift embeddim(first($geoms)) + vizgsetfacets!(plot, M[], Val(pdim[]), Val(edim[]), geoms) +end + +function vizgsetfacets!(plot, ::Type, ::Val{1}, ::Val, geoms) pointmarker = plot[:pointmarker] pointcolor = plot[:pointcolor] pointsize = plot[:pointsize] - if showpoints[] - # all boundaries are points or multipoints - points = Makie.@lift filter(!isnothing, boundary.($geoms)) - pset = Makie.@lift GeometrySet($points) - viz!(plot, pset, color=pointcolor, pointmarker=pointmarker, pointsize=pointsize) - end + # all boundaries are points or multipoints + points = Makie.@lift filter(!isnothing, boundary.($geoms)) + pset = Makie.@lift GeometrySet($points) + viz!(plot, pset, color=pointcolor, pointmarker=pointmarker, pointsize=pointsize) end -function showfacets2D!(plot, geoms) - showsegments = plot[:showsegments] +function vizgsetfacets!(plot, ::Type, ::Val{2}, ::Val, geoms) segmentcolor = plot[:segmentcolor] segmentsize = plot[:segmentsize] - if showsegments[] - # all boundaries are 1D geometries - bounds = Makie.@lift filter(!isnothing, boundary.($geoms)) - bset = Makie.@lift GeometrySet($bounds) - viz!(plot, bset, color=segmentcolor, segmentsize=segmentsize) + # all boundaries are 1D geometries + bounds = Makie.@lift filter(!isnothing, boundary.($geoms)) + bset = Makie.@lift GeometrySet($bounds) + viz!(plot, bset, color=segmentcolor, segmentsize=segmentsize) +end + +function vizgeoms!(plot; facets=false) + gset = plot[:object] + color = plot[:color] + alpha = plot[:alpha] + colormap = plot[:colormap] + colorrange = plot[:colorrange] + + # process color spec into colorant + colorant = facets ? nothing : Makie.@lift(process($color, $colormap, $colorrange, $alpha)) + + # get geometries + geoms = Makie.@lift parent($gset) + + # get geometry types + types = Makie.@lift unique(typeof.($geoms)) + + for G in types[] + inds = Makie.@lift findall(g -> g isa G, $geoms) + gvec = Makie.@lift collect(G, $geoms[$inds]) + M = Makie.@lift manifold(first($gvec)) + pdim = Makie.@lift paramdim(first($gvec)) + edim = Makie.@lift embeddim(first($gvec)) + if facets + vizgsetfacets!(plot, M[], Val(pdim[]), Val(edim[]), gvec) + else + cvec = Makie.@lift $colorant isa AbstractVector ? $colorant[$inds] : $colorant + vizgset!(plot, M[], Val(pdim[]), Val(edim[]), gvec, cvec) + end end end diff --git a/ext/grid.jl b/ext/grid.jl index 409995ea6..633365921 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -16,6 +16,16 @@ end vizgrid!(plot, M::Type{<:𝔼}, pdim::Val, edim::Val) = vizgridfallback!(plot, M, pdim, edim) +function vizfacets!(plot::Viz{<:Tuple{Grid}}) + grid = plot[:object] + M = Makie.@lift manifold($grid) + pdim = Makie.@lift paramdim($grid) + edim = Makie.@lift embeddim($grid) + vizgridfacets!(plot, M[], Val(pdim[]), Val(edim[])) +end + +vizgridfacets!(plot, M::Type, pdim::Val, edim::Val) = vizmeshfacets!(plot, M, pdim, edim) + # ---------------- # SPECIALIZATIONS # ---------------- @@ -35,6 +45,7 @@ function vizgridfallback!(plot, M, pdim, edim) alpha = plot[:alpha] colormap = plot[:colormap] colorrange = plot[:colorrange] + showsegments = plot[:showsegments] if pdim == Val(2) # visualize quadrangle mesh with texture using uv coords # decide whether or not to reverse connectivity list @@ -50,23 +61,28 @@ function vizgridfallback!(plot, M, pdim, edim) ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 dims = Makie.@lift size($grid) + vdims = Makie.@lift Meshes.vsize($grid) texture = if ncolor[] == 1 Makie.@lift fill($colorant, $dims) elseif ncolor[] == nquads[] Makie.@lift reshape($colorant, $dims) elseif ncolor[] == nverts[] - Makie.@lift reshape($colorant, $dims .+ 1) + Makie.@lift reshape($colorant, $vdims) else throw(ArgumentError("invalid number of colors")) end - uv = Makie.@lift [Makie.Vec2f(v, 1 - u) for v in range(0, 1, $dims[2] + 1) for u in range(0, 1, $dims[1] + 1)] + uv = Makie.@lift [Makie.Vec2f(v, 1 - u) for v in range(0, 1, $vdims[2]) for u in range(0, 1, $vdims[1])] mesh = Makie.@lift GB.Mesh(Makie.meta($verts, uv=$uv), $quads) shading = edim == Val(3) ? Makie.FastShading : Makie.NoShading Makie.mesh!(plot, mesh, color=texture, shading=shading) + + if showsegments[] + vizfacets!(plot) + end else # fallback to triangle mesh visualization vizmesh!(plot, M, pdim, edim) end diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index 8120c50c4..2a7b8ca6c 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -31,10 +31,7 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{2}, : viz!(plot, bbox, color=colorant) if showsegments[] - xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) - tup = Makie.@lift xysegments($xyz...) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end else if nc[] == nv[] @@ -48,9 +45,7 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{2}, : end if showsegments[] - tup = Makie.@lift xysegments(0:$sz[1], 0:$sz[2]) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end # adjust spacing and origin @@ -69,8 +64,6 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{3}, : colormap = plot[:colormap] colorrange = plot[:colorrange] showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -103,8 +96,28 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{3}, : end if showsegments[] - tup = Makie.@lift xyzsegments($xyz...) - x, y, z = Makie.@lift($tup[1]), Makie.@lift($tup[2]), Makie.@lift($tup[3]) - Makie.lines!(plot, x, y, z, color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end end + +function vizgridfacets!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{2}, ::Val{2}) + grid = plot[:object] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) + tup = Makie.@lift xysegments($xyz...) + x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) +end + +function vizgridfacets!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{3}, ::Val{3}) + grid = plot[:object] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) + tup = Makie.@lift xyzsegments($xyz...) + x, y, z = Makie.@lift($tup[1]), Makie.@lift($tup[2]), Makie.@lift($tup[3]) + Makie.lines!(plot, x, y, z, color=segmentcolor, linewidth=segmentsize) +end diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 62c1979b9..4c9be43b2 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -44,11 +44,20 @@ function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, M::Type{<:𝔼}, pdim::Va end if showsegments[] - tup = Makie.@lift xysegments($xs, $ys) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end else vizgridfallback!(plot, M, pdim, edim) end end + +function vizgridfacets!(plot::Viz{<:Tuple{RectilinearGrid}}, ::Type{<:𝔼}, ::Val{2}, ::Val{2}) + grid = plot[:object] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + xyz = Makie.@lift map(x -> ustrip.(x), Meshes.xyz($grid)) + tup = Makie.@lift xysegments($xyz...) + x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) +end diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index f9db1d024..27c1dae85 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -32,9 +32,7 @@ function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, M::Type{<:𝔼}, pdim::Val Makie.surface!(plot, X, Y, color=C) if showsegments[] - tup = Makie.@lift structuredsegments($grid) - x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) - Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end else vizmesh!(plot, M, pdim, edim) @@ -44,6 +42,16 @@ function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, M::Type{<:𝔼}, pdim::Val end end +function vizgridfacets!(plot::Viz{<:Tuple{StructuredGrid}}, ::Type{<:𝔼}, ::Val{2}, ::Val{2}) + grid = plot[:object] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + tup = Makie.@lift structuredsegments($grid) + x, y = Makie.@lift($tup[1]), Makie.@lift($tup[2]) + Makie.lines!(plot, x, y, color=segmentcolor, linewidth=segmentsize) +end + function structuredsegments(grid) cinds = CartesianIndices(size(grid) .+ 1) coords = [] diff --git a/ext/mesh.jl b/ext/mesh.jl index 7dfa3cfa1..dd4a208e6 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -54,8 +54,6 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) colormap = plot[:colormap] colorrange = plot[:colorrange] showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) @@ -148,47 +146,7 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) Makie.mesh!(plot, mkemesh, color=tcolors, shading=tshading) if showsegments[] - # retrieve coordinates parameters - xparams = Makie.@lift let - # relevant settings - T = Unitful.numtype(Meshes.lentype($mesh)) - dim = embeddim($mesh) - topo = topology($mesh) - nvert = nvertices($mesh) - verts = vertices($mesh) - coords = map(p -> ustrip.(to(p)), verts) - - # use a sophisticated data structure - # to extract the edges from the n-gons - t = convert(HalfEdgeTopology, topo) - ∂ = Boundary{1,0}(t) - - # append indices of incident vertices - # interleaved with a sentinel index - inds = Int[] - for i in 1:nfacets(t) - for j in ∂(i) - push!(inds, j) - end - push!(inds, nvert + 1) - end - - # fill sentinel index with NaN coordinates - push!(coords, SVector(ntuple(i -> T(NaN), dim))) - - # extract incident vertices - coords = coords[inds] - - # split coordinates to match signature - [getindex.(coords, j) for j in 1:dim] - end - - # unpack observable of paramaters - xyz = map(1:embeddim(mesh[])) do i - Makie.@lift $xparams[i] - end - - Makie.lines!(plot, xyz..., color=segmentcolor, linewidth=segmentsize) + vizfacets!(plot) end end @@ -203,6 +161,54 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val) vizmany!(plot, meshes, color) end +function vizfacets!(plot::Viz{<:Tuple{Mesh}}) + mesh = plot[:object] + M = Makie.@lift manifold($mesh) + pdim = Makie.@lift paramdim($mesh) + edim = Makie.@lift embeddim($mesh) + vizmeshfacets!(plot, M[], Val(pdim[]), Val(edim[])) +end + +function vizmeshfacets!(plot, ::Type, ::Val{2}, ::Val) + mesh = plot[:object] + segmentcolor = plot[:segmentcolor] + segmentsize = plot[:segmentsize] + + # retrieve coordinates parameters + coords = Makie.@lift let + # relevant settings + T = Unitful.numtype(Meshes.lentype($mesh)) + dim = embeddim($mesh) + topo = topology($mesh) + nvert = nvertices($mesh) + verts = vertices($mesh) + coords = map(p -> ustrip.(to(p)), verts) + + # use a sophisticated data structure + # to extract the edges from the n-gons + t = convert(HalfEdgeTopology, topo) + ∂ = Boundary{1,0}(t) + + # append indices of incident vertices + # interleaved with a sentinel index + inds = Int[] + for i in 1:nfacets(t) + for j in ∂(i) + push!(inds, j) + end + push!(inds, nvert + 1) + end + + # fill sentinel index with NaN coordinates + push!(coords, SVector(ntuple(i -> T(NaN), dim))) + + # extract incident vertices + coords[inds] + end + + Makie.lines!(plot, coords, color=segmentcolor, linewidth=segmentsize) +end + function segmentsof(topo, vert) p = first(vert) T = Unitful.numtype(Meshes.lentype(p)) From 6450e7f7bc844ce62475b518b1bc34541e0dd336 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:52:00 -0300 Subject: [PATCH 332/423] Add a heuristic to use uv coords in `vizgridfallback!` (#1057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a heuristic to use uv coords in 'vizgridfallback!' * Apply suggestions from code review --------- Co-authored-by: Júlio Hoffimann --- ext/grid.jl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ext/grid.jl b/ext/grid.jl index 633365921..29dd2ee76 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -47,24 +47,30 @@ function vizgridfallback!(plot, M, pdim, edim) colorrange = plot[:colorrange] showsegments = plot[:showsegments] - if pdim == Val(2) # visualize quadrangle mesh with texture using uv coords + # process color spec into colorant + colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) + + # number of vertices, elements and colors + nverts = Makie.@lift nvertices($grid) + nelems = Makie.@lift nelements($grid) + ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 + + # visualize quadrangle mesh with texture using uv coords + # plots with uv coords are always interpolated, + # so it is only used in the case ncolor == nverts + # or when there is a large number of elements + if pdim == Val(2) && (ncolor[] == 1 || ncolor[] == nverts[] || nelems[] ≥ 1000) # decide whether or not to reverse connectivity list rfunc = Makie.@lift _reverse(crs($grid)) verts = Makie.@lift map(asmakie, vertices($grid)) quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($grid))] - colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) - - nverts = Makie.@lift length($verts) - nquads = Makie.@lift length($quads) - ncolor = Makie.@lift $colorant isa AbstractVector ? length($colorant) : 1 - dims = Makie.@lift size($grid) vdims = Makie.@lift Meshes.vsize($grid) texture = if ncolor[] == 1 Makie.@lift fill($colorant, $dims) - elseif ncolor[] == nquads[] + elseif ncolor[] == nelems[] Makie.@lift reshape($colorant, $dims) elseif ncolor[] == nverts[] Makie.@lift reshape($colorant, $vdims) From c5cf83e89af05c1e7eb112223f5d7123b725903c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 12 Sep 2024 08:52:25 -0300 Subject: [PATCH 333/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 020241efe..9484711d9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.4" +version = "0.51.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From e8a6d2904b0f5371dbb43542a2c19f7989df2129 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:35:11 -0300 Subject: [PATCH 334/423] `GeometrySet`: handle heterogeneous `CRS` (#1058) * 'GeometrySet': handle heterogeneous 'CRS' * Update comments * Apply suggestions * Update docstrings --- src/domains/sets.jl | 15 ++++++++++++++- test/sets.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/domains/sets.jl b/src/domains/sets.jl index 8b6b13ebf..c8688ebc9 100644 --- a/src/domains/sets.jl +++ b/src/domains/sets.jl @@ -14,13 +14,26 @@ Set containing two balls centered at `(0.0, 0.0)` and `(1.0, 1.0)`: ```julia julia> GeometrySet([Ball((0.0, 0.0)), Ball((1.0, 1.0))]) ``` + +### Notes + +* Geometries with different CRS will be projected to the CRS of the first geometry. """ struct GeometrySet{M<:Manifold,C<:CRS,G<:Geometry{M,C}} <: Domain{M,C} geoms::Vector{G} end # constructor with iterator of geometries -GeometrySet(geoms) = GeometrySet(map(identity, geoms)) +function GeometrySet(geoms) + # project all geometries to the same CRS if necessary + fun = if allequal(crs(g) for g in geoms) + identity # narrow types + else + Proj(crs(first(geoms))) + end + + GeometrySet(map(fun, geoms)) +end element(d::GeometrySet, ind::Int) = d.geoms[ind] diff --git a/test/sets.jl b/test/sets.jl index a5d9853f5..d253653ea 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -24,6 +24,32 @@ gset = GeometrySet(geoms) @test eltype(gset) <: Segment + # different CRS + s = Segment(latlon(0, 0), latlon(1, 1)) + t = Triangle(latlon(0, 0), latlon(0, 1), latlon(1, 0)) |> Proj(PlateCarree) + q = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) |> Proj(WebMercator) + geoms = [s, t, q] + gset = GeometrySet(geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: LatLon + gset = GeometrySet(g for g in geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: LatLon + geoms = [t, s, q] + gset = GeometrySet(geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: PlateCarree + gset = GeometrySet(g for g in geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: PlateCarree + geoms = [q, s, t] + gset = GeometrySet(geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: WebMercator + gset = GeometrySet(g for g in geoms) + @test eltype(gset) <: Polytope + @test crs(gset) <: WebMercator + # conversion grid = cartgrid(10, 10) gset = convert(GeometrySet, grid) From 0947f63bc6b05acc24ec9b7ada8e2ebd62309e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 12 Sep 2024 17:10:25 -0300 Subject: [PATCH 335/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9484711d9..0bcb11e9b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.5" +version = "0.51.6" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 920b7ad35009b52c4371195dfc776273e26cbf03 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:58:46 -0300 Subject: [PATCH 336/423] Search methods: handle different units (#1064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Search methods: handle different units * Minor adjustments * Minor adjustments --------- Co-authored-by: Júlio Hoffimann --- src/neighborsearch/ball.jl | 8 ++++++-- src/neighborsearch/kball.jl | 11 +++++++---- src/neighborsearch/knearest.jl | 7 +++++-- test/neighborsearch.jl | 21 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index 05d59bcca..2565e5030 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -27,9 +27,13 @@ BallSearch(geoms, ball) = BallSearch(GeometrySet(geoms), ball) function search(pₒ::Point, method::BallSearch; mask=nothing) tree = method.tree - dmax = radius(method.ball) - inds = inrange(tree, ustrip.(to(pₒ)), ustrip(dmax)) + # adjust units of query point and radius + u = unit(lentype(method.domain)) + r = ustrip(u, radius(method.ball)) + x = ustrip.(u, to(pₒ)) + + inds = inrange(tree, x, r) if isnothing(mask) inds diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 943c152d1..94ec1073c 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -30,15 +30,18 @@ KBallSearch(geoms, k, ball) = KBallSearch(GeometrySet(geoms), k, ball) maxneighbors(method::KBallSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; mask=nothing) - u = unit(lentype(pₒ)) tree = method.tree - dmax = radius(method.ball) k = method.k - inds, dists = knn(tree, ustrip.(to(pₒ)), k, true) + # adjust units of query point and radius + u = unit(lentype(method.domain)) + r = ustrip(u, radius(method.ball)) + x = ustrip.(u, to(pₒ)) + + inds, dists = knn(tree, x, k, true) # keep neighbors inside ball - keep = dists .≤ ustrip(dmax) + keep = dists .≤ r # possibly mask some of the neighbors isnothing(mask) || (keep .*= mask[inds]) diff --git a/src/neighborsearch/knearest.jl b/src/neighborsearch/knearest.jl index 830927cec..8d4ef330a 100644 --- a/src/neighborsearch/knearest.jl +++ b/src/neighborsearch/knearest.jl @@ -28,11 +28,14 @@ KNearestSearch(geoms, k; metric=Euclidean()) = KNearestSearch(GeometrySet(geoms) maxneighbors(method::KNearestSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KNearestSearch; mask=nothing) - u = unit(lentype(pₒ)) tree = method.tree k = method.k - inds, dists = knn(tree, ustrip.(to(pₒ)), k, true) + # adjust units of query point + u = unit(lentype(method.domain)) + x = ustrip.(u, to(pₒ)) + + inds, dists = knn(tree, x, k, true) if isnothing(mask) nneigh = k diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index ac779ef4b..727d1ab89 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -21,6 +21,13 @@ n = search(cart(9, 9), s) @test Set(n) == Set([89, 90, 99, 100]) + # different units + s = BallSearch(𝒟, MetricBall(T(10) * u"dm")) + n = search(Point(T(900) * u"cm", T(900) * u"cm"), s) + @test Set(n) == Set([100, 99, 90]) + n = search(Point(T(9000) * u"mm", T(9000) * u"mm"), s) + @test Set(n) == Set([100, 99, 90]) + # non MinkowskiMetric example 𝒟 = CartesianGrid((360, 180), T.((0.0, -90.0)), T.((1.0, 1.0))) s = BallSearch(𝒟, MetricBall(T(150), Haversine(T(6371)))) @@ -56,6 +63,13 @@ end @test nn == 3 @test Set(n[1:nn]) == Set([100, 99, 90]) + # different units + s = KNearestSearch(𝒟, 3) + n = search(Point(T(900) * u"cm", T(900) * u"cm"), s) + @test Set(n) == Set([100, 99, 90]) + n = search(Point(T(9000) * u"mm", T(9000) * u"mm"), s) + @test Set(n) == Set([100, 99, 90]) + # construct from vector of geometries s = KNearestSearch(randpoint2(100), 3) @test s isa KNearestSearch @@ -105,6 +119,13 @@ end @test length(n) == 4 @test length(d) == 4 + # different units + s = KBallSearch(𝒟, 10, MetricBall(T(10) * u"dm")) + n = search(Point(T(500) * u"cm", T(500) * u"cm"), s) + @test Set(n) == Set([56, 66, 55, 57, 46]) + n = search(Point(T(5000) * u"mm", T(5000) * u"mm"), s) + @test Set(n) == Set([56, 66, 55, 57, 46]) + # construct from vector of geometries s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) @test s isa KBallSearch From d412cf0eb2dcbabe8710091ff53d517152436300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 16 Sep 2024 15:59:13 -0300 Subject: [PATCH 337/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0bcb11e9b..a22887581 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.6" +version = "0.51.7" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From c1e71535a55925653c188a32d50f66cb985a6dac Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:23:26 -0300 Subject: [PATCH 338/423] 'Torus': Rename 'normal' to 'direction' (#1065) --- src/Meshes.jl | 1 + src/geometries/primitives/torus.jl | 27 ++++++++++++++------------- src/predicates/in.jl | 2 +- test/primitives.jl | 8 ++++---- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index 2637c9d7c..e3e1e4fe2 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -192,6 +192,7 @@ export sides, diagonal, focallength, + direction, # polytopes Polytope, diff --git a/src/geometries/primitives/torus.jl b/src/geometries/primitives/torus.jl index d228f4fa5..e3785080f 100644 --- a/src/geometries/primitives/torus.jl +++ b/src/geometries/primitives/torus.jl @@ -3,26 +3,27 @@ # ------------------------------------------------------------------ """ - Torus(center, normal, major, minor) + Torus(center, direction, major, minor) A torus centered at `center` with axis of revolution directed by -`normal` and with radii `major` and `minor`. +`direction` and with radii `major` and `minor`. """ struct Torus{C<:CRS,Mₚ<:Manifold,V<:Vec{3},ℒ<:Len} <: Primitive{𝔼{3},C} center::Point{Mₚ,C} - normal::V + direction::V major::ℒ minor::ℒ - Torus(center::Point{Mₚ,C}, normal::V, major::ℒ, minor::ℒ) where {C<:CRS,Mₚ<:Manifold,V<:Vec{3},ℒ<:Len} = - new{C,Mₚ,V,float(ℒ)}(center, normal, major, minor) + Torus(center::Point{Mₚ,C}, direction::V, major::ℒ, minor::ℒ) where {C<:CRS,Mₚ<:Manifold,V<:Vec{3},ℒ<:Len} = + new{C,Mₚ,V,float(ℒ)}(center, direction, major, minor) end -Torus(center::Point, normal::Vec, major::Len, minor::Len) = Torus(center, normal, promote(major, minor)...) +Torus(center::Point, direction::Vec, major::Len, minor::Len) = Torus(center, direction, promote(major, minor)...) -Torus(center::Point, normal::Vec, major, minor) = Torus(center, normal, addunit(major, u"m"), addunit(minor, u"m")) +Torus(center::Point, direction::Vec, major, minor) = + Torus(center, direction, addunit(major, u"m"), addunit(minor, u"m")) -Torus(center::Tuple, normal::Tuple, major, minor) = Torus(Point(center), Vec(normal), major, minor) +Torus(center::Tuple, direction::Tuple, major, minor) = Torus(Point(center), Vec(direction), major, minor) """ Torus(p1, p2, p3, minor) @@ -44,18 +45,18 @@ paramdim(::Type{<:Torus}) = 2 center(t::Torus) = t.center -normal(t::Torus) = t.normal +direction(t::Torus) = t.direction radii(t::Torus) = (t.major, t.minor) -axis(t::Torus) = Line(t.center, t.center + t.normal) +axis(t::Torus) = Line(t.center, t.center + t.direction) ==(t₁::Torus, t₂::Torus) = - t₁.center == t₂.center && t₁.normal == t₂.normal && t₁.major == t₂.major && t₁.minor == t₂.minor + t₁.center == t₂.center && t₁.direction == t₂.direction && t₁.major == t₂.major && t₁.minor == t₂.minor Base.isapprox(t₁::Torus, t₂::Torus; atol=atol(lentype(t₁)), kwargs...) = isapprox(t₁.center, t₂.center; atol, kwargs...) && - isapprox(t₁.normal, t₂.normal; atol, kwargs...) && + isapprox(t₁.direction, t₂.direction; atol, kwargs...) && isapprox(t₁.major, t₂.major; atol, kwargs...) && isapprox(t₁.minor, t₂.minor; atol, kwargs...) @@ -65,7 +66,7 @@ function (t::Torus)(θ, φ) if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) throw(DomainError((θ, φ), "t(θ, φ) is not defined for θ, φ outside [0, 1]².")) end - c, n⃗ = t.center, t.normal + c, n⃗ = t.center, t.direction R, r = t.major, t.minor Q = urotbetween(Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)), n⃗) diff --git a/src/predicates/in.jl b/src/predicates/in.jl index 4472e9a75..e170111ba 100644 --- a/src/predicates/in.jl +++ b/src/predicates/in.jl @@ -104,7 +104,7 @@ end function Base.in(p::Point, t::Torus) ℒ = lentype(p) R, r = radii(t) - c, n = center(t), normal(t) + c, n = center(t), direction(t) Q = urotbetween(n, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) x, y, z = Q * (p - c) (R - √(x^2 + y^2))^2 + z^2 ≤ r^2 diff --git a/test/primitives.jl b/test/primitives.jl index e703aa642..a80c113e1 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1270,7 +1270,7 @@ end @test crs(t) <: Cartesian{NoDatum} @test Meshes.lentype(t) == ℳ @test center(t) == cart(1, 1, 1) - @test normal(t) == vector(1, 0, 0) + @test direction(t) == vector(1, 0, 0) @test radii(t) == (T(2) * u"m", T(1) * u"m") @test axis(t) == Line(cart(1, 1, 1), cart(2, 1, 1)) @test measure(t) ≈ 8 * T(π)^2 * u"m^2" @@ -1305,19 +1305,19 @@ end t = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) @test sprint(show, t) == - "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), normal: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" + "Torus(center: (x: 1.0 m, y: 1.0 m, z: 1.0 m), direction: (1.0 m, 0.0 m, 0.0 m), major: 2.0 m, minor: 1.0 m)" if T === Float32 @test sprint(show, MIME("text/plain"), t) == """ Torus ├─ center: Point(x: 1.0f0 m, y: 1.0f0 m, z: 1.0f0 m) - ├─ normal: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) + ├─ direction: Vec(1.0f0 m, 0.0f0 m, 0.0f0 m) ├─ major: 2.0f0 m └─ minor: 1.0f0 m""" else @test sprint(show, MIME("text/plain"), t) == """ Torus ├─ center: Point(x: 1.0 m, y: 1.0 m, z: 1.0 m) - ├─ normal: Vec(1.0 m, 0.0 m, 0.0 m) + ├─ direction: Vec(1.0 m, 0.0 m, 0.0 m) ├─ major: 2.0 m └─ minor: 1.0 m""" end From e3840420ca642098a0e909d81027ab824a5aec38 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:28:39 -0300 Subject: [PATCH 339/423] `RegularGrid`: Add more constructors (#1066) * 'RegularGrid': Add more constructors * Update code * Update code * Update 'CartesianGrid' constructors * Update code * Update code order * Add tests --- src/domains/meshes/cartesiangrid.jl | 60 +++++++------------------ src/domains/meshes/regulargrid.jl | 70 ++++++++++++++++++++++++++--- test/meshes.jl | 25 +++++++++++ 3 files changed, 103 insertions(+), 52 deletions(-) diff --git a/src/domains/meshes/cartesiangrid.jl b/src/domains/meshes/cartesiangrid.jl index 15c0fc582..b972ff0dc 100644 --- a/src/domains/meshes/cartesiangrid.jl +++ b/src/domains/meshes/cartesiangrid.jl @@ -56,41 +56,19 @@ See also [`RegularGrid`](@ref). """ const CartesianGrid{M<:𝔼,C<:Cartesian} = RegularGrid{M,C} -function CartesianGrid( - origin::Point{<:𝔼}, - spacing::NTuple{Dim,ℒ}, - offset::Dims{Dim}, - topology::GridTopology{Dim} -) where {Dim,ℒ<:Len} - orig = Point(convert(Cartesian, coords(origin))) - RegularGrid(orig, spacing, offset, topology) -end - -CartesianGrid( - origin::Point{<:𝔼}, - spacing::NTuple{Dim,Len}, - offset::Dims{Dim}, - topology::GridTopology{Dim} -) where {Dim} = CartesianGrid(origin, promote(spacing...), offset, topology) - CartesianGrid( - origin::Point{<:𝔼}, + origin::Point{𝔼{Dim}}, spacing::NTuple{Dim,Number}, offset::Dims{Dim}, topology::GridTopology{Dim} -) where {Dim} = CartesianGrid(origin, addunit.(spacing, u"m"), offset, topology) +) where {Dim} = RegularGrid(_cartpoint(origin), spacing, offset, topology) -function CartesianGrid( +CartesianGrid( dims::Dims{Dim}, - origin::Point, + origin::Point{𝔼{Dim}}, spacing::NTuple{Dim,Number}, offset::Dims{Dim}=ntuple(i -> 1, Dim) -) where {Dim} - if !all(>(0), dims) - throw(ArgumentError("dimensions must be positive")) - end - CartesianGrid(origin, spacing, offset, GridTopology(dims)) -end +) where {Dim} = RegularGrid(dims, _cartpoint(origin), spacing, offset) CartesianGrid( dims::Dims{Dim}, @@ -99,28 +77,14 @@ CartesianGrid( offset::Dims{Dim}=ntuple(i -> 1, Dim) ) where {Dim} = CartesianGrid(dims, Point(origin), spacing, offset) -function CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,ℒ}) where {Dim,ℒ<:Len} - dims = Tuple(ceil.(Int, (finish - start) ./ spacing)) - origin = start - offset = ntuple(i -> 1, Dim) - CartesianGrid(dims, origin, spacing, offset) -end - -CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,Len}) where {Dim} = - CartesianGrid(start, finish, promote(spacing...)) - -CartesianGrid(start::Point, finish::Point, spacing::NTuple{Dim,Number}) where {Dim} = - CartesianGrid(start, finish, addunit.(spacing, u"m")) +CartesianGrid(start::Point{𝔼{Dim}}, finish::Point{𝔼{Dim}}, spacing::NTuple{Dim,Number}) where {Dim} = + RegularGrid(_cartpoint(start), _cartpoint(finish), spacing) CartesianGrid(start::NTuple{Dim,Number}, finish::NTuple{Dim,Number}, spacing::NTuple{Dim,Number}) where {Dim} = CartesianGrid(Point(start), Point(finish), spacing) -function CartesianGrid(start::Point, finish::Point; dims::Dims=ntuple(i -> 100, embeddim(start))) - origin = start - spacing = Tuple((finish - start) ./ dims) - offset = ntuple(i -> 1, length(dims)) - CartesianGrid(dims, origin, spacing, offset) -end +CartesianGrid(start::Point{𝔼{Dim}}, finish::Point{𝔼{Dim}}; dims::Dims{Dim}=ntuple(i -> 100, Dim)) where {Dim} = + RegularGrid(_cartpoint(start), _cartpoint(finish); dims) CartesianGrid( start::NTuple{Dim,Number}, @@ -136,3 +100,9 @@ function CartesianGrid(dims::Dims{Dim}) where {Dim} end CartesianGrid(dims::Int...) = CartesianGrid(dims) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_cartpoint(p) = Point(convert(Cartesian, coords(p))) diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl index 16c5ef3ef..192632813 100644 --- a/src/domains/meshes/regulargrid.jl +++ b/src/domains/meshes/regulargrid.jl @@ -13,6 +13,16 @@ and cell spacing `spacing`. The three arguments must have the same length. A regular grid with dimensions `dims`, with lower left corner of element `offset` at `origin` and cell spacing `spacing`. + RegularGrid(start, finish, dims=dims) + +Alternatively, construct a regular grid from a `start` point to a `finish` +with dimensions `dims`. + + RegularGrid(start, finish, spacing) + +Alternatively, construct a regular grid from a `start` point to a `finish` +point using a given `spacing`. + ## Examples ``` @@ -44,13 +54,9 @@ function RegularGrid( offset::Dims{N}, topology::GridTopology{N} ) where {M<:Manifold,C<:CRS,N} - if M <: 🌐 && !(C <: LatLon) - throw(ArgumentError("regular spacing on `🌐` requires `LatLon` coordinates")) - end + _checkorigin(origin) - T = CoordRefSystems.mactype(C) nc = CoordRefSystems.ncoords(C) - us = CoordRefSystems.units(C) if N ≠ nc throw(ArgumentError(""" @@ -59,9 +65,9 @@ function RegularGrid( """)) end - sp = ntuple(i -> numconvert(T, withunit(spacing[i], us[i])), nc) + spac = _spacing(origin, spacing) - RegularGrid{M,C,N,typeof(sp)}(origin, sp, offset, topology) + RegularGrid{M,C,N,typeof(spac)}(origin, spac, offset, topology) end function RegularGrid( @@ -76,6 +82,21 @@ function RegularGrid( RegularGrid(origin, spacing, offset, GridTopology(dims)) end +function RegularGrid(start::Point, finish::Point, spacing::NTuple{N,Number}) where {N} + _checkorigin(start) + svals, fvals = _startfinish(start, finish) + spac = _spacing(start, spacing) + dims = ceil.(Int, (fvals .- svals) ./ spac) + RegularGrid(dims, start, spac) +end + +function RegularGrid(start::Point, finish::Point; dims::Dims=ntuple(i -> 100, CoordRefSystems.ncoords(crs(start)))) + _checkorigin(start) + svals, fvals = _startfinish(start, finish) + spacing = (fvals .- svals) ./ dims + RegularGrid(dims, start, spacing) +end + spacing(g::RegularGrid) = g.spacing offset(g::RegularGrid) = g.offset @@ -132,3 +153,38 @@ function Base.show(io::IO, ::MIME"text/plain", g::RegularGrid) println(io, "├─ maximum: ", maximum(g)) print(io, "└─ spacing: ", spacing(g)) end + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function _checkorigin(origin) + if manifold(origin) <: 🌐 && !(crs(origin) <: LatLon) + throw(ArgumentError("regular spacing on `🌐` requires `LatLon` coordinates")) + end +end + +function _spacing(origin, spacing) + C = crs(origin) + T = CoordRefSystems.mactype(C) + nc = CoordRefSystems.ncoords(C) + us = CoordRefSystems.units(C) + ntuple(i -> numconvert(T, withunit(spacing[i], us[i])), nc) +end + +function _startfinish(start::Point{<:𝔼}, finish::Point{<:𝔼}) + scoords = coords(start) + fcoords = convert(crs(start), coords(finish)) + svals = CoordRefSystems.values(scoords) + fvals = CoordRefSystems.values(fcoords) + svals, fvals +end + +function _startfinish(start::Point{<:🌐}, finish::Point{<:🌐}) + slatlon = convert(LatLon, coords(start)) + flatlon = convert(LatLon, coords(finish)) + slon = flatlon.lon < slatlon.lon ? slatlon.lon - 360u"°" : slatlon.lon + svals = (slatlon.lat, slon) + fvals = (flatlon.lat, flatlon.lon) + svals, fvals +end diff --git a/test/meshes.jl b/test/meshes.jl index 1e3907f66..3cbe3c74f 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -67,6 +67,31 @@ @test grid[1, 1, 1] == grid[1] @test grid[10, 20, 30] == grid[6000] + # constructors with start and finish + grid = RegularGrid(merc(0, 0), merc(10, 10), T.((0.1, 0.1))) + @test size(grid) == (100, 100) + @test minimum(grid) == merc(0, 0) + @test maximum(grid) == merc(10, 10) + @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m") + + grid = RegularGrid(latlon(-50, 150), latlon(50, 30), T.((10, 12))) + @test size(grid) == (10, 20) + @test minimum(grid) == latlon(-50, 150) + @test maximum(grid) == latlon(50, 30) + @test spacing(grid) == (T(10) * u"°", T(12) * u"°") + + grid = RegularGrid(merc(0, 0), merc(10, 10), dims=(100, 100)) + @test size(grid) == (100, 100) + @test minimum(grid) == merc(0, 0) + @test maximum(grid) == merc(10, 10) + @test spacing(grid) == (T(0.1) * u"m", T(0.1) * u"m") + + grid = RegularGrid(latlon(-50, 150), latlon(50, 30), dims=(10, 20)) + @test size(grid) == (10, 20) + @test minimum(grid) == latlon(-50, 150) + @test maximum(grid) == latlon(50, 30) + @test spacing(grid) == (T(10) * u"°", T(12) * u"°") + # spacing unit and numtype grid = RegularGrid((10, 20), Point(Polar(T(0) * u"cm", T(0) * u"rad")), (10.0 * u"mm", 1.0f0 * u"rad")) @test unit.(spacing(grid)) == (u"cm", u"rad") From a601abb6d89d47b81268e1e0dd8cbea725e46815 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:34:49 -0300 Subject: [PATCH 340/423] Fix 'RegularDiscretization' of 'Box' (#1067) --- src/discretization/regular.jl | 2 +- test/discretization.jl | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/discretization/regular.jl b/src/discretization/regular.jl index d61eae91f..ac783377b 100644 --- a/src/discretization/regular.jl +++ b/src/discretization/regular.jl @@ -177,5 +177,5 @@ end function discretize(box::Box, method::RegularDiscretization) sz = fitdims(method.sizes, paramdim(box)) - CartesianGrid(extrema(box)..., dims=sz) + RegularGrid(extrema(box)..., dims=sz) end diff --git a/test/discretization.jl b/test/discretization.jl index ebd728bac..d104995ff 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -335,6 +335,24 @@ end @test eltype(mesh) <: Quadrangle @test nvertices.(mesh) ⊆ [4] + box = Box(merc(0, 0), merc(2, 2)) + mesh = discretize(box, RegularDiscretization(10)) + @test mesh isa RegularGrid + @test crs(mesh) <: Mercator + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + + box = Box(latlon(-50, 150), latlon(50, 30)) + mesh = discretize(box, RegularDiscretization(10)) + @test mesh isa RegularGrid + @test crs(mesh) <: LatLon + @test nvertices(mesh) == 121 + @test nelements(mesh) == 100 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + sphere = Sphere(cart(0, 0), T(1)) mesh = discretize(sphere, RegularDiscretization(10)) @test nvertices(mesh) == 10 From dd5595dafe003950a1d272725b077a6f4ffcc068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 17 Sep 2024 18:14:05 -0300 Subject: [PATCH 341/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a22887581..63cc2178e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.7" +version = "0.51.8" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ad5f90ecac1a078dda939ed42a97d83c9aeef90d Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:11:36 -0300 Subject: [PATCH 342/423] Add `MaxLengthDiscretization` (#1068) * Add 'MaxLengthDiscretization' * Add more methods * Update src/discretization/maxlength.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update code * Apply suggestions * Update code * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Add tests --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/Meshes.jl | 1 + src/discretization.jl | 3 +- src/discretization/maxlength.jl | 58 +++++++++++++++++++++++++++++++++ test/discretization.jl | 44 +++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/discretization/maxlength.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index e3e1e4fe2..31c1eb6ef 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -506,6 +506,7 @@ export DelaunayTriangulation, ManualDiscretization, RegularDiscretization, + MaxLengthDiscretization, discretize, discretizewithin, simplexify, diff --git a/src/discretization.jl b/src/discretization.jl index c96fd4f02..48e98e717 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -190,8 +190,6 @@ simplexify(box::Box{𝔼{2}}) = discretize(box, FanTriangulation()) simplexify(box::Box{𝔼{3}}) = discretize(box, ManualDiscretization()) -simplexify(seg::Segment) = SimpleMesh(pointify(seg), GridTopology(1)) - function simplexify(chain::Chain) np = nvertices(chain) + isclosed(chain) ip = isperiodic(chain) @@ -263,3 +261,4 @@ include("discretization/held.jl") include("discretization/delaunay.jl") include("discretization/manual.jl") include("discretization/regular.jl") +include("discretization/maxlength.jl") diff --git a/src/discretization/maxlength.jl b/src/discretization/maxlength.jl new file mode 100644 index 000000000..a64a61054 --- /dev/null +++ b/src/discretization/maxlength.jl @@ -0,0 +1,58 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + MaxLengthDiscretization(length) + +Discretize geometries into parts with sides of maximum `length` in length units (default to meters). +""" +struct MaxLengthDiscretization{ℒ<:Len} <: DiscretizationMethod + length::ℒ + MaxLengthDiscretization(length::ℒ) where {ℒ<:Len} = new{float(ℒ)}(length) +end + +MaxLengthDiscretization(length) = MaxLengthDiscretization(addunit(length, u"m")) + +function discretize(box::Box, method::MaxLengthDiscretization) + sizes = ceil.(Int, _sides(box) ./ method.length) + discretize(box, RegularDiscretization(sizes)) +end + +function discretize(segment::Segment, method::MaxLengthDiscretization) + size = ceil(Int, _measure(segment) / method.length) + discretize(segment, RegularDiscretization(size)) +end + +discretize(chain::Chain, method::MaxLengthDiscretization) = + mapreduce(s -> discretize(s, method), merge, segments(chain)) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +_sides(box::Box{<:𝔼}) = sides(box) + +function _sides(box::Box{<:🌐}) + A, B = extrema(box) + a = convert(LatLon, coords(A)) + b = convert(LatLon, coords(B)) + P = withcrs(box, (a.lat, b.lon), LatLon) + + AP = Segment(A, P) + PB = Segment(P, B) + (_measure(AP), _measure(PB)) +end + +_measure(segment::Segment{<:𝔼}) = measure(segment) + +# TODO: Haversine returns the shortest distance between two points +# this is not always equal to the distance between two directed points +function _measure(segment::Segment{<:🌐}) + T = numtype(lentype(segment)) + 🌎 = ellipsoid(datum(crs(segment))) + r = numconvert(T, majoraxis(🌎)) + + A, B = extrema(segment) + evaluate(Haversine(r), A, B) +end diff --git a/test/discretization.jl b/test/discretization.jl index d104995ff..4a8fa21f1 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -425,6 +425,50 @@ end @test all(intersects(poly), mesh) end +@testitem "MaxLengthDiscretization" setup = [Setup] begin + box = Box(cart(0, 0), cart(10, 10)) + mesh = discretize(box, MaxLengthDiscretization(T(1))) + @test nvertices(mesh) == 11 * 11 + @test nelements(mesh) == 10 * 10 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + + box = Box(latlon(0, 0), latlon(45, 45)) + mesh = discretize(box, MaxLengthDiscretization(T(1e5))) + @test nvertices(mesh) == 52 * 52 + @test nelements(mesh) == 51 * 51 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] + + seg = Segment(cart(0, 0), cart(0, 1)) + mesh = discretize(seg, MaxLengthDiscretization(T(0.1))) + @test nvertices(mesh) == 11 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + seg = Segment(latlon(0, 0), latlon(0, 45)) + mesh = discretize(seg, MaxLengthDiscretization(T(1e5))) + @test nvertices(mesh) == 52 + @test nelements(mesh) == 51 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + rope = Rope(cart(0, 0), cart(1, 0), cart(0, 1)) + mesh = discretize(rope, MaxLengthDiscretization(T(0.1))) + @test nvertices(mesh) == 27 + @test nelements(mesh) == 25 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + + ring = Ring(latlon(-45, 90), latlon(45, 90), latlon(45, -90), latlon(-45, -90)) + mesh = discretize(ring, MaxLengthDiscretization(T(1e5))) + @test nvertices(mesh) == 408 + @test nelements(mesh) == 404 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] +end + @testitem "Discretize" setup = [Setup] begin ball = Ball(cart(0, 0), T(1)) mesh = discretize(ball) From 2ae525241da369f0236dbbf7aae96f68ba0c7b77 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:01:20 -0300 Subject: [PATCH 343/423] Add method for `measure` of `Segment` with Ellipsoid manifold (#1069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add method for 'measure' of 'Segment' with Ellipsoid manifold * Apply suggestions from code review Co-authored-by: Júlio Hoffimann --------- Co-authored-by: Júlio Hoffimann --- src/discretization/maxlength.jl | 17 ++--------------- src/geometries/primitives/box.jl | 4 ++-- src/measures.jl | 12 +++++++++++- test/polytopes.jl | 11 +++++++++++ 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/discretization/maxlength.jl b/src/discretization/maxlength.jl index a64a61054..20c663624 100644 --- a/src/discretization/maxlength.jl +++ b/src/discretization/maxlength.jl @@ -20,7 +20,7 @@ function discretize(box::Box, method::MaxLengthDiscretization) end function discretize(segment::Segment, method::MaxLengthDiscretization) - size = ceil(Int, _measure(segment) / method.length) + size = ceil(Int, measure(segment) / method.length) discretize(segment, RegularDiscretization(size)) end @@ -41,18 +41,5 @@ function _sides(box::Box{<:🌐}) AP = Segment(A, P) PB = Segment(P, B) - (_measure(AP), _measure(PB)) -end - -_measure(segment::Segment{<:𝔼}) = measure(segment) - -# TODO: Haversine returns the shortest distance between two points -# this is not always equal to the distance between two directed points -function _measure(segment::Segment{<:🌐}) - T = numtype(lentype(segment)) - 🌎 = ellipsoid(datum(crs(segment))) - r = numconvert(T, majoraxis(🌎)) - - A, B = extrema(segment) - evaluate(Haversine(r), A, B) + (measure(AP), measure(PB)) end diff --git a/src/geometries/primitives/box.jl b/src/geometries/primitives/box.jl index 3421cdb57..4eb3e2947 100644 --- a/src/geometries/primitives/box.jl +++ b/src/geometries/primitives/box.jl @@ -51,9 +51,9 @@ Base.maximum(b::Box) = b.max Base.extrema(b::Box) = b.min, b.max -diagonal(b::Box) = norm(b.max - b.min) +diagonal(b::Box{<:𝔼}) = norm(b.max - b.min) -sides(b::Box) = Tuple(b.max - b.min) +sides(b::Box{<:𝔼}) = Tuple(b.max - b.min) ==(b₁::Box, b₂::Box) = b₁.min == b₂.min && b₁.max == b₂.max diff --git a/src/measures.jl b/src/measures.jl index c54eee3a1..d15cce6c8 100644 --- a/src/measures.jl +++ b/src/measures.jl @@ -75,7 +75,17 @@ function measure(t::Torus) 4 * T(π)^2 * R * r end -measure(s::Segment) = norm(maximum(s) - minimum(s)) +measure(s::Segment{<:𝔼}) = norm(maximum(s) - minimum(s)) + +# TODO: replace Haversine by an appropriate geodesic distance +# that considers the west-east orientation of segments +function measure(s::Segment{<:🌐}) + T = numtype(lentype(s)) + 🌎 = ellipsoid(datum(crs(s))) + r = numconvert(T, majoraxis(🌎)) + + evaluate(Haversine(r), extrema(s)...) +end function measure(t::Triangle) A, B, C = vertices(t) diff --git a/test/polytopes.jl b/test/polytopes.jl index ca3fd39cc..359493dcd 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -71,6 +71,17 @@ s = Segment(merc(0, 0), merc(1, 1)) @test crs(s(T(0))) === crs(s) + # measure + s = Segment(merc(0, 0), merc(1, 1)) + @test measure(s) ≈ T(√2) * u"m" + s = Segment(latlon(0, 45), latlon(0, 135)) + r = majoraxis(ellipsoid(datum(crs(s)))) + C = 2 * T(π) * ℳ(r) + @test measure(s) ≈ C / 4 + # TODO: fix measure of segments on the globe + s = Segment(latlon(0, 135), latlon(0, 45)) + @test_broken measure(s) ≈ 3C / 4 + s = Segment(cart(0, 0), cart(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" if T === Float32 From 98fb7132dcdadab54bb419355a00afd0399ea3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 18 Sep 2024 16:32:48 -0300 Subject: [PATCH 344/423] Fix Rotate and Scale of RegularGrid (#1071) --- src/transforms/rotate.jl | 2 +- src/transforms/scale.jl | 2 ++ test/transforms.jl | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/transforms/rotate.jl b/src/transforms/rotate.jl index 6c92a97be..ac9660266 100644 --- a/src/transforms/rotate.jl +++ b/src/transforms/rotate.jl @@ -60,7 +60,7 @@ applycoord(t::Rotate, b::Box) = TransformedGeometry(b, t) applycoord(t::Rotate, e::Ellipsoid) = Ellipsoid(radii(e), applycoord(t, center(e)), t.rot * rotation(e)) -applycoord(t::Rotate, g::CartesianGrid) = TransformedGrid(g, t) +applycoord(t::Rotate, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Rotate, g::RectilinearGrid) = TransformedGrid(g, t) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index d1241bdda..371c2a5e8 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -76,6 +76,8 @@ function applycoord(t::Scale, g::CartesianGrid) CartesianGrid(dims, orig, spac, offs) end +applycoord(t::Scale, g::RegularGrid) = TransformedGrid(g, t) + applycoord(t::Scale, g::RectilinearGrid) = RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g))) diff --git a/test/transforms.jl b/test/transforms.jl index 5f4114ded..13e398aa5 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -182,6 +182,16 @@ @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) @test TB.revert(f, r, c) ≈ d + # ------------ + # REGULARGRID + # ------------ + + f = Rotate(Angle2d(T(π / 2))) + d = RegularGrid(merc(0, 0), merc(1, 1), dims=(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # ---------------- # RECTILINEARGRID # ---------------- @@ -851,6 +861,16 @@ end @test r ≈ CartesianGrid(cart(1, 2), cart(11, 22), dims=(10, 10)) @test TB.revert(f, r, c) ≈ d + # ------------ + # REGULARGRID + # ------------ + + f = Scale(T(1), T(2)) + d = RegularGrid(merc(1, 1), merc(11, 11), dims=(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ RegularGrid(merc(1, 2), merc(11, 22), dims=(10, 10)) + @test TB.revert(f, r, c) ≈ d + # ---------------- # RECTILINEARGRID # ---------------- From 0ef0b2eb7ad3a13e81593059523bd769ca33c122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 18 Sep 2024 16:33:04 -0300 Subject: [PATCH 345/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 63cc2178e..331d3f4f1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.8" +version = "0.51.9" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 4daa03dde0abba4ec139c1c8ad901b2721c0d92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 18 Sep 2024 17:40:04 -0300 Subject: [PATCH 346/423] Fix typo in docstring --- src/domains/meshes/regulargrid.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domains/meshes/regulargrid.jl b/src/domains/meshes/regulargrid.jl index 192632813..ad05d5941 100644 --- a/src/domains/meshes/regulargrid.jl +++ b/src/domains/meshes/regulargrid.jl @@ -28,7 +28,7 @@ point using a given `spacing`. ``` RegularGrid((10, 20), Point(LatLon(30.0°, 60.0°)), (1.0, 1.0)) # add coordinate units to spacing RegularGrid((10, 20), Point(Polar(0.0cm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units -RegularGrid((10, 20), Point(Marcator(0.0, 0.0)), (1.5, 1.5)) +RegularGrid((10, 20), Point(Mercator(0.0, 0.0)), (1.5, 1.5)) RegularGrid((10, 20, 30), Point(Cylindrical(0.0, 0.0, 0.0)), (3.0, 2.0, 1.0)) ``` From e668af7c9a10ccb841895f66c48437ba87bc5f92 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:46:06 -0300 Subject: [PATCH 347/423] Add support for predicates in `TriRefinement` (#1070) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for predicates in 'TriRefinement' * Apply suggestions * Apply suggestions * Apply suggestions * Apply suggestions * Rename variables * Fix typo * Rename variables * Update src/refinement/tri.jl Co-authored-by: Júlio Hoffimann * Add tests * Add more tests * Update code order * Update code order --------- Co-authored-by: Júlio Hoffimann --- src/refinement/tri.jl | 50 ++++++++++++++++++++++++++++++++----------- test/refinement.jl | 11 ++++++++++ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 235f09c69..8a5e86eef 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -3,14 +3,20 @@ # ------------------------------------------------------------------ """ - TriRefinement() + TriRefinement([pred]) Refinement of polygonal meshes into triangles. -A n-gon is subdivided into n triangles. +A n-gon for which the predicate `pred` holds true +is subdivided into n triangles. The method refines all +n-gons if the `pred` is ommited. """ -struct TriRefinement <: RefinementMethod end +struct TriRefinement{F} <: RefinementMethod + pred::F +end + +TriRefinement() = TriRefinement(nothing) -function refine(mesh, ::TriRefinement) +function refine(mesh, method::TriRefinement) assertion(paramdim(mesh) == 2, "TriRefinement only defined for surface meshes") (eltype(mesh) <: Triangle) || return simplexify(mesh) @@ -21,9 +27,19 @@ function refine(mesh, ::TriRefinement) # convert to half-edge structure t = convert(HalfEdgeTopology, connec) + # indices to refine + rinds = if isnothing(method.pred) + 1:nelements(t) + else + filter(i -> method.pred(mesh[i]), 1:nelements(t)) + end + + # indices to preserve + pinds = setdiff(1:nelements(t), rinds) + # add centroids of elements ∂₂₀ = Boundary{2,0}(t) - epts = map(1:nelements(t)) do elem + rpts = map(rinds) do elem is = ∂₂₀(elem) cₒ = sum(i -> to(points[i]), is) / length(is) withcrs(mesh, cₒ) @@ -33,23 +49,31 @@ function refine(mesh, ::TriRefinement) vpts = points # new points in refined mesh - newpoints = [vpts; epts] + newpoints = [vpts; rpts] + # new connectivities in refined mesh + newconnec = Connectivity{Triangle,3}[] + + # offset to new vertex indices offset = length(vpts) - # connect vertices into new triangles - newconnec = Connectivity{Triangle,3}[] - for elem in 1:nelements(t) + # connectivities of new triangles + for (i, elem) in enumerate(rinds) verts = ∂₂₀(elem) nv = length(verts) - for i in 1:nv - u = elem + offset - v = verts[mod1(i, nv)] - w = verts[mod1(i + 1, nv)] + for j in 1:nv + u = i + offset + v = verts[mod1(j, nv)] + w = verts[mod1(j + 1, nv)] tri = connect((u, v, w)) push!(newconnec, tri) end end + # connectivities of preserved elements + for elem in pinds + push!(newconnec, element(t, elem)) + end + SimpleMesh(newpoints, newconnec) end diff --git a/test/refinement.jl b/test/refinement.jl index 02896e365..a1f6d5c0c 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -15,6 +15,17 @@ grid = CartesianGrid((3, 3), merc(0, 0), (T(1), T(1))) ref = refine(grid, TriRefinement()) @test crs(ref) === crs(grid) + + # predicate + points = cart.([(0, 0), (4, 0), (8, 0), (3, 1), (5, 1), (2, 2), (4, 2), (6, 2), (4, 4)]) + connec = connect.([(1, 2, 6), (2, 3, 8), (6, 8, 9), (2, 5, 4), (4, 5, 7), (4, 7, 6), (5, 8, 7)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, TriRefinement(e -> measure(e) > T(1) * u"m^2")) + @test nelements(ref) == 13 + @test nvertices(ref) == 12 + ref = refine(mesh, TriRefinement(e -> measure(e) ≤ T(1) * u"m^2")) + @test nelements(ref) == 15 + @test nvertices(ref) == 13 end @testitem "QuadRefinement" setup = [Setup] begin From a6144b75036469d530d76f83325fe63677d9c370 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:03:46 -0300 Subject: [PATCH 348/423] Update `TriRefinement` implementation (#1076) * Update 'TriRefinement' implementation * Rename variables --- src/refinement/tri.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index 8a5e86eef..b328895b5 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -22,23 +22,20 @@ function refine(mesh, method::TriRefinement) # retrieve geometry and topology points = vertices(mesh) - connec = topology(mesh) - - # convert to half-edge structure - t = convert(HalfEdgeTopology, connec) + topo = topology(mesh) # indices to refine rinds = if isnothing(method.pred) - 1:nelements(t) + 1:nelements(topo) else - filter(i -> method.pred(mesh[i]), 1:nelements(t)) + filter(i -> method.pred(mesh[i]), 1:nelements(topo)) end # indices to preserve - pinds = setdiff(1:nelements(t), rinds) + pinds = setdiff(1:nelements(topo), rinds) # add centroids of elements - ∂₂₀ = Boundary{2,0}(t) + ∂₂₀ = Boundary{2,0}(topo) rpts = map(rinds) do elem is = ∂₂₀(elem) cₒ = sum(i -> to(points[i]), is) / length(is) @@ -72,7 +69,7 @@ function refine(mesh, method::TriRefinement) # connectivities of preserved elements for elem in pinds - push!(newconnec, element(t, elem)) + push!(newconnec, element(topo, elem)) end SimpleMesh(newpoints, newconnec) From ae343f670c0c9ad5afb08d0b06323e7d86558def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 19 Sep 2024 16:02:40 -0300 Subject: [PATCH 349/423] Bump version --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 331d3f4f1..6e20e527b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.9" +version = "0.51.10" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" @@ -25,7 +25,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1, 1" -CoordRefSystems = "0.13.4" +CoordRefSystems = "0.14" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" From 70d1d3a64d2d6b4ac2d24254fc81d6e139f8f3b8 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:11:00 -0300 Subject: [PATCH 350/423] Add `MaxLengthDiscretization` for `Geometry` (#1075) * Add 'MaxLengthDiscretization' for 'Polygon' * Use 'TriSubdivision' * Apply suggestions * Add tests * Update method * Update method * Add more tests * Apply suggestions --- src/discretization/maxlength.jl | 13 +++++++++++++ test/discretization.jl | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/discretization/maxlength.jl b/src/discretization/maxlength.jl index 20c663624..9490b2106 100644 --- a/src/discretization/maxlength.jl +++ b/src/discretization/maxlength.jl @@ -27,10 +27,23 @@ end discretize(chain::Chain, method::MaxLengthDiscretization) = mapreduce(s -> discretize(s, method), merge, segments(chain)) +discretize(multi::Multi, method::MaxLengthDiscretization) = _iterativerefinement(multi, method) + +discretize(geometry::Geometry, method::MaxLengthDiscretization) = _iterativerefinement(geometry, method) + # ----------------- # HELPER FUNCTIONS # ----------------- +function _iterativerefinement(geometry, method) + iscoarse(e) = perimeter(e) > method.length * nvertices(e) + mesh = simplexify(geometry) + while any(iscoarse, mesh) + mesh = refine(mesh, TriSubdivision()) + end + mesh +end + _sides(box::Box{<:𝔼}) = sides(box) function _sides(box::Box{<:🌐}) diff --git a/test/discretization.jl b/test/discretization.jl index 4a8fa21f1..f25ea196e 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -467,6 +467,29 @@ end @test nelements(mesh) == 404 @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] + + tri = Triangle(cart(0, 0), cart(10, 0), cart(0, 10)) + mesh = discretize(tri, MaxLengthDiscretization(T(3))) + @test nvertices(mesh) == 15 + @test nelements(mesh) == 16 + @test eltype(mesh) <: Triangle + @test nvertices.(mesh) ⊆ [3] + + quad = Quadrangle(latlon(0, 0), latlon(0, 45), latlon(45, 45), latlon(45, 0)) + mesh = discretize(quad, MaxLengthDiscretization(T(1e6))) + @test nvertices(mesh) == 81 + @test nelements(mesh) == 128 + @test eltype(mesh) <: Triangle + @test nvertices.(mesh) ⊆ [3] + + quad1 = Quadrangle(latlon(0, 0), latlon(0, 45), latlon(45, 45), latlon(45, 0)) + quad2 = Quadrangle(latlon(0, 0), latlon(-45, 0), latlon(-45, 45), latlon(0, 45)) + multi = Multi([quad1, quad2]) + mesh = discretize(multi, MaxLengthDiscretization(T(1e6))) + @test nvertices(mesh) == 162 + @test nelements(mesh) == 256 + @test eltype(mesh) <: Triangle + @test nvertices.(mesh) ⊆ [3] end @testitem "Discretize" setup = [Setup] begin From 77df420ed29da8d6e58f8b05c5bc40f55b99bc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 20 Sep 2024 09:25:28 -0300 Subject: [PATCH 351/423] Remove Ring inner check (#1077) --- src/geometries/polytopes.jl | 2 +- src/geometries/polytopes/ring.jl | 14 +------------- test/polytopes.jl | 8 -------- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/geometries/polytopes.jl b/src/geometries/polytopes.jl index 641a286bf..e8c030610 100644 --- a/src/geometries/polytopes.jl +++ b/src/geometries/polytopes.jl @@ -177,7 +177,7 @@ const Polygon = Polytope{2} ≗(polygon₁, polygon₂) Tells whether or not the `polygon₁` and `polygon₂` -are equal up to circular shifts. +are equal regardless of circular shifts. """ function ≗(p₁::Polygon, p₂::Polygon) rings₁ = rings(p₁) diff --git a/src/geometries/polytopes/ring.jl b/src/geometries/polytopes/ring.jl index af552517a..6e3c726b1 100644 --- a/src/geometries/polytopes/ring.jl +++ b/src/geometries/polytopes/ring.jl @@ -11,20 +11,8 @@ See also [`Chain`](@ref) and [`Rope`](@ref). """ struct Ring{M<:Manifold,C<:CRS,V<:CircularVector{Point{M,C}}} <: Chain{M,C} vertices::V - - function Ring{M,C,V}(vertices) where {M<:Manifold,C<:CRS,V<:CircularVector{Point{M,C}}} - if first(vertices) == last(vertices) && length(vertices) ≥ 2 - throw(ArgumentError(""" - First and last vertices of `Ring` constructor must be different - in the latest version of Meshes.jl. The type itself now holds - this connectivity information. - """)) - end - new(vertices) - end end -Ring(vertices::CircularVector{Point{M,C}}) where {M<:Manifold,C<:CRS} = Ring{M,C,typeof(vertices)}(vertices) Ring(vertices::Tuple...) = Ring([Point(v) for v in vertices]) Ring(vertices::P...) where {P<:Point} = Ring(collect(vertices)) Ring(vertices::AbstractVector{<:Tuple}) = Ring(Point.(vertices)) @@ -38,7 +26,7 @@ nvertices(r::Ring) = length(r.vertices) ≗(ring₁, ring₂) Tells whether or not the `ring₁` and `ring₂` -are equal up to circular shifts. +are equal regardless of circular shifts. """ function ≗(r₁::Ring, r₂::Ring) n = length(r₁.vertices) diff --git a/test/polytopes.jl b/test/polytopes.jl index 359493dcd..8c88c26b0 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -178,10 +178,6 @@ end c = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) @test isnothing(boundary(c)) - # should not repeat the first vertex manually - @test_throws ArgumentError Ring(cart.([(0, 0), (0, 0)])) - @test_throws ArgumentError Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) - # degenerate rings with 1 or 2 vertices are allowed r = Ring(cart.([(0, 0)])) @test isclosed(r) @@ -657,10 +653,6 @@ end inner ├─ Ring((x: 0.2 m, y: 0.2 m), ..., (x: 0.4 m, y: 0.2 m)) └─ Ring((x: 0.6 m, y: 0.2 m), ..., (x: 0.8 m, y: 0.2 m))""" - - # should not repeat the first vertex manually - @test_throws ArgumentError PolyArea(cart.([(0, 0), (0, 0)])) - @test_throws ArgumentError PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 0)])) end @testitem "Polyhedra" setup = [Setup] begin From cf6d934f0b20fb1f808aaf04105bbbae220a979c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 20 Sep 2024 09:25:58 -0300 Subject: [PATCH 352/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6e20e527b..df239c43b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.10" +version = "0.51.11" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From cac67c993b1e5b5e7227055e8e6ccccc830eb5ea Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:38:34 -0300 Subject: [PATCH 353/423] Update the method definition of `centroid` of grids (#1078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update the method definition of 'centroid' of grids * Add more tests * Update src/utils.jl --------- Co-authored-by: Júlio Hoffimann --- src/centroid.jl | 4 ++-- src/utils.jl | 3 ++- test/meshes.jl | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/centroid.jl b/src/centroid.jl index e8d8090a5..761d9c95d 100644 --- a/src/centroid.jl +++ b/src/centroid.jl @@ -65,12 +65,12 @@ centroid(d::Domain, ind::Int) = centroid(d[ind]) centroid(d::SubDomain, ind::Int) = centroid(parent(d), parentindices(d)[ind]) -function centroid(g::CartesianGrid, ind::Int) +function centroid(g::RegularGrid{<:𝔼,<:CartesianOrProjected}, ind::Int) ijk = elem2cart(topology(g), ind) vertex(g, ijk) + Vec(spacing(g) ./ 2) end -function centroid(g::RectilinearGrid, ind::Int) +function centroid(g::RectilinearGrid{<:𝔼,<:CartesianOrProjected}, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) diff --git a/src/utils.jl b/src/utils.jl index adc3a611f..9d3145af9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -2,8 +2,9 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -# auxiliary type for dispatch purposes +# auxiliary types for dispatch purposes const GeometryOrDomain = Union{Geometry,Domain} +const CartesianOrProjected = Union{Cartesian,Projected} include("utils/basic.jl") include("utils/assert.jl") diff --git a/test/meshes.jl b/test/meshes.jl index 3cbe3c74f..49f6fcca7 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -13,6 +13,7 @@ @test eltype(grid) <: Quadrangle @test vertex(grid, 1) == vertex(grid, (1, 1)) @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test centroid(grid, 1) == centroid(grid[1]) @test grid[1, 1] == grid[1] @test grid[10, 20] == grid[200] @@ -30,6 +31,7 @@ @test eltype(grid) <: Quadrangle @test vertex(grid, 1) == vertex(grid, (1, 1)) @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test centroid(grid, 1) == centroid(grid[1]) @test grid[1, 1] == grid[1] @test grid[10, 20] == grid[200] @@ -47,6 +49,7 @@ @test eltype(grid) <: Quadrangle @test vertex(grid, 1) == vertex(grid, (1, 1)) @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21)) + @test centroid(grid, 1) == centroid(grid[1]) @test grid[1, 1] == grid[1] @test grid[10, 20] == grid[200] @@ -64,6 +67,7 @@ @test eltype(grid) <: Hexahedron @test vertex(grid, 1) == vertex(grid, (1, 1, 1)) @test vertex(grid, nvertices(grid)) == vertex(grid, (11, 21, 31)) + @test centroid(grid, 1) == centroid(grid[1]) @test grid[1, 1, 1] == grid[1] @test grid[10, 20, 30] == grid[6000] From e9cc5f5a0d11b1cc2927079ef1591046f49c4d39 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:04:58 -0300 Subject: [PATCH 354/423] Generalize dispatch to OrthoRegularGrid (#1079) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update definition of grid methods * Apply suggestions * Apply suggestions * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/domains/meshes/cartesiangrid.jl * Apply suggestions * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Júlio Hoffimann --- src/boundingboxes.jl | 2 +- src/centroid.jl | 2 +- src/coarsening/regular.jl | 4 ++-- src/domains/meshes.jl | 3 +++ src/indices.jl | 12 ++++++------ src/refinement/regular.jl | 4 ++-- src/sampling/regular.jl | 2 +- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index d9f4f263d..7ead5c8ba 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -69,7 +69,7 @@ end boundingbox(t::Torus) = _pboxes(pointify(t)) -boundingbox(g::CartesianGrid) = Box(extrema(g)...) +boundingbox(g::OrthoRegularGrid) = Box(extrema(g)...) boundingbox(g::RectilinearGrid) = Box(extrema(g)...) diff --git a/src/centroid.jl b/src/centroid.jl index 761d9c95d..9aa490bd1 100644 --- a/src/centroid.jl +++ b/src/centroid.jl @@ -65,7 +65,7 @@ centroid(d::Domain, ind::Int) = centroid(d[ind]) centroid(d::SubDomain, ind::Int) = centroid(parent(d), parentindices(d)[ind]) -function centroid(g::RegularGrid{<:𝔼,<:CartesianOrProjected}, ind::Int) +function centroid(g::OrthoRegularGrid, ind::Int) ijk = elem2cart(topology(g), ind) vertex(g, ijk) + Vec(spacing(g) ./ 2) end diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index 716c41e0f..ad1f46c43 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -20,9 +20,9 @@ end RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) -function coarsen(grid::CartesianGrid, method::RegularCoarsening) +function coarsen(grid::OrthoRegularGrid, method::RegularCoarsening) factors = fitdims(method.factors, paramdim(grid)) - CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) + RegularGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) end function coarsen(grid::RectilinearGrid, method::RegularCoarsening) diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index d084da93e..eaf28207c 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -246,3 +246,6 @@ include("meshes/rectilineargrid.jl") include("meshes/structuredgrid.jl") include("meshes/simplemesh.jl") include("meshes/transformedmesh.jl") + +# alias for dispatch purposes +const OrthoRegularGrid{M<:𝔼,C<:Union{Cartesian,Projected}} = RegularGrid{M,C} diff --git a/src/indices.jl b/src/indices.jl index 523491a4b..321c63213 100644 --- a/src/indices.jl +++ b/src/indices.jl @@ -13,7 +13,7 @@ Return the indices of the elements of the `domain` that intersect with the `geom """ indices(domain::Domain, geometry::Geometry) = findall(intersects(geometry), domain) -function indices(grid::CartesianGrid, point::Point) +function indices(grid::OrthoRegularGrid, point::Point) point ∉ grid && return Int[] # grid properties @@ -31,7 +31,7 @@ function indices(grid::CartesianGrid, point::Point) [LinearIndices(dims)[coords...]] end -function indices(grid::CartesianGrid, chain::Chain) +function indices(grid::OrthoRegularGrid, chain::Chain) dims = size(grid) mask = falses(dims) @@ -43,7 +43,7 @@ function indices(grid::CartesianGrid, chain::Chain) LinearIndices(dims)[mask] end -function indices(grid::CartesianGrid, poly::Polygon) +function indices(grid::OrthoRegularGrid, poly::Polygon) dims = size(grid) mask = zeros(Int, dims) cpoly = poly ∩ boundingbox(grid) @@ -56,7 +56,7 @@ function indices(grid::CartesianGrid, poly::Polygon) LinearIndices(dims)[mask .> 0] end -function indices(grid::CartesianGrid, box::Box) +function indices(grid::OrthoRegularGrid, box::Box) # cartesian range range = cartesianrange(grid, box) @@ -64,7 +64,7 @@ function indices(grid::CartesianGrid, box::Box) LinearIndices(size(grid))[range] |> vec end -indices(grid::CartesianGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique +indices(grid::OrthoRegularGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique function indices(grid::RectilinearGrid, box::Box) # cartesian range @@ -89,7 +89,7 @@ _manifoldrange(::Type{<:𝔼}, grid::Grid, box::Box) = _euclideanrange(grid, box _manifoldrange(::Type{<:🌐}, grid::Grid, box::Box) = _geodesicrange(grid, box) -function _euclideanrange(grid::CartesianGrid, box::Box) +function _euclideanrange(grid::OrthoRegularGrid, box::Box) # grid properties or = minimum(grid) sp = spacing(grid) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index d4f0ebb5e..f1bced176 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -20,9 +20,9 @@ end RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors) -function refine(grid::CartesianGrid, method::RegularRefinement) +function refine(grid::OrthoRegularGrid, method::RegularRefinement) factors = fitdims(method.factors, paramdim(grid)) - CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) + RegularGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors) end function refine(grid::RectilinearGrid, method::RegularRefinement) diff --git a/src/sampling/regular.jl b/src/sampling/regular.jl index 72533c0fc..6f6cfb648 100644 --- a/src/sampling/regular.jl +++ b/src/sampling/regular.jl @@ -87,6 +87,6 @@ extrapoints(c::FrustumSurface, sz) = (bottom(c)(0, 0), top(c)(0, 0)) # SPECIAL CASES # -------------- -function sample(rng::AbstractRNG, grid::CartesianGrid, method::RegularSampling) +function sample(rng::AbstractRNG, grid::OrthoRegularGrid, method::RegularSampling) sample(rng, boundingbox(grid), method) end From c9cc7219be21e5883a73dd27ad127d0ee93bf308 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:41:40 -0300 Subject: [PATCH 355/423] Generalize dispatch to OrthoRectilinearGrid and OrthoStructuredGrid (#1080) --- src/boundingboxes.jl | 7 ++++--- src/centroid.jl | 2 +- src/domains/meshes.jl | 4 +++- src/indices.jl | 4 ++-- src/refinement/regular.jl | 8 ++++---- src/utils.jl | 3 +-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 7ead5c8ba..8098b5892 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -71,11 +71,12 @@ boundingbox(t::Torus) = _pboxes(pointify(t)) boundingbox(g::OrthoRegularGrid) = Box(extrema(g)...) -boundingbox(g::RectilinearGrid) = Box(extrema(g)...) +boundingbox(g::OrthoRectilinearGrid) = Box(extrema(g)...) -boundingbox(g::TransformedGrid{<:Any,<:Any,<:CartesianGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:Any,<:OrthoRegularGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox -boundingbox(g::TransformedGrid{<:Any,<:Any,<:RectilinearGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox +boundingbox(g::TransformedGrid{<:Any,<:Any,<:OrthoRectilinearGrid}) = + boundingbox(parent(g)) |> transform(g) |> boundingbox boundingbox(m::Mesh) = _pboxes(vertices(m)) diff --git a/src/centroid.jl b/src/centroid.jl index 9aa490bd1..eedb5c6c9 100644 --- a/src/centroid.jl +++ b/src/centroid.jl @@ -70,7 +70,7 @@ function centroid(g::OrthoRegularGrid, ind::Int) vertex(g, ijk) + Vec(spacing(g) ./ 2) end -function centroid(g::RectilinearGrid{<:𝔼,<:CartesianOrProjected}, ind::Int) +function centroid(g::OrthoRectilinearGrid, ind::Int) ijk = elem2cart(topology(g), ind) p1 = vertex(g, ijk) p2 = vertex(g, ijk .+ 1) diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index eaf28207c..f98cad96b 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -247,5 +247,7 @@ include("meshes/structuredgrid.jl") include("meshes/simplemesh.jl") include("meshes/transformedmesh.jl") -# alias for dispatch purposes +# aliases for dispatch purposes const OrthoRegularGrid{M<:𝔼,C<:Union{Cartesian,Projected}} = RegularGrid{M,C} +const OrthoRectilinearGrid{M<:𝔼,C<:Union{Cartesian,Projected}} = RectilinearGrid{M,C} +const OrthoStructuredGrid{M<:𝔼,C<:Union{Cartesian,Projected}} = StructuredGrid{M,C} diff --git a/src/indices.jl b/src/indices.jl index 321c63213..722379bd7 100644 --- a/src/indices.jl +++ b/src/indices.jl @@ -66,7 +66,7 @@ end indices(grid::OrthoRegularGrid, multi::Multi) = mapreduce(geom -> indices(grid, geom), vcat, parent(multi)) |> unique -function indices(grid::RectilinearGrid, box::Box) +function indices(grid::OrthoRectilinearGrid, box::Box) # cartesian range range = cartesianrange(grid, box) @@ -106,7 +106,7 @@ function _euclideanrange(grid::OrthoRegularGrid, box::Box) CartesianIndex(Tuple(ijkₛ)):CartesianIndex(Tuple(ijkₑ)) end -function _euclideanrange(grid::RectilinearGrid, box::Box) +function _euclideanrange(grid::OrthoRectilinearGrid, box::Box) # grid properties nd = paramdim(grid) diff --git a/src/refinement/regular.jl b/src/refinement/regular.jl index f1bced176..f272bf21b 100644 --- a/src/refinement/regular.jl +++ b/src/refinement/regular.jl @@ -32,7 +32,7 @@ function refine(grid::RectilinearGrid, method::RegularRefinement) RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end -function refine(grid::StructuredGrid, method::RegularRefinement) +function refine(grid::OrthoStructuredGrid, method::RegularRefinement) factors = fitdims(method.factors, paramdim(grid)) XYZ′ = _XYZ(grid, factors) StructuredGrid{manifold(grid),crs(grid)}(XYZ′) @@ -49,9 +49,9 @@ function _refinedims(x, f) x′ end -_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(paramdim(grid)), factors) +_XYZ(grid::OrthoStructuredGrid, factors::Dims) = _XYZ(grid, Val(paramdim(grid)), factors) -function _XYZ(grid::StructuredGrid, ::Val{2}, factors::Dims{2}) +function _XYZ(grid::OrthoStructuredGrid, ::Val{2}, factors::Dims{2}) T = numtype(lentype(grid)) fᵢ, fⱼ = factors sᵢ, sⱼ = size(grid) @@ -77,7 +77,7 @@ function _XYZ(grid::StructuredGrid, ::Val{2}, factors::Dims{2}) (X, Y) end -function _XYZ(grid::StructuredGrid, ::Val{3}, factors::Dims{3}) +function _XYZ(grid::OrthoStructuredGrid, ::Val{3}, factors::Dims{3}) T = numtype(lentype(grid)) fᵢ, fⱼ, fₖ = factors sᵢ, sⱼ, sₖ = size(grid) diff --git a/src/utils.jl b/src/utils.jl index 9d3145af9..adc3a611f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -2,9 +2,8 @@ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ -# auxiliary types for dispatch purposes +# auxiliary type for dispatch purposes const GeometryOrDomain = Union{Geometry,Domain} -const CartesianOrProjected = Union{Cartesian,Projected} include("utils/basic.jl") include("utils/assert.jl") From 1babc1203a65a6c714ac3d723f24c8301a837be9 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:42:05 -0300 Subject: [PATCH 356/423] Adjust transforms of `Grid` (#1081) * Adjust transforms of 'Grid' * Add more tests * Apply suggestions --- src/transforms/affine.jl | 2 +- src/transforms/lengthunit.jl | 8 +++++++ src/transforms/scale.jl | 6 ++---- src/transforms/shadow.jl | 4 ++++ src/transforms/slice.jl | 4 ++-- src/transforms/translate.jl | 16 ++++++++------ test/transforms.jl | 42 ++++++++++++++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index eaa2f0738..4a60fa3d5 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -63,7 +63,7 @@ applycoord(t::Affine, v::Vec) = t.A * v applycoord(t::Affine, b::Box) = TransformedGeometry(b, t) -applycoord(t::Affine, g::CartesianGrid) = TransformedGrid(g, t) +applycoord(t::Affine, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Affine, g::RectilinearGrid) = TransformedGrid(g, t) diff --git a/src/transforms/lengthunit.jl b/src/transforms/lengthunit.jl index 30bdf2069..7af2198a3 100644 --- a/src/transforms/lengthunit.jl +++ b/src/transforms/lengthunit.jl @@ -32,6 +32,14 @@ applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len) applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens) +function applycoord(t::LengthUnit, g::RegularGrid) + dims = size(g) + orig = applycoord(t, minimum(g)) + spac = map(s -> applycoord(t, s), spacing(g)) + offs = offset(g) + RegularGrid(dims, orig, spac, offs) +end + applycoord(t::LengthUnit, g::RectilinearGrid) = TransformedGrid(g, t) applycoord(t::LengthUnit, g::StructuredGrid) = TransformedGrid(g, t) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 371c2a5e8..2eeb09d3a 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -68,16 +68,14 @@ applycoord(t::Scale, p::ParaboloidSurface) = TransformedGeometry(p, t) applycoord(t::Scale, tr::Torus) = TransformedGeometry(tr, t) -function applycoord(t::Scale, g::CartesianGrid) +function applycoord(t::Scale, g::RegularGrid) dims = size(g) orig = applycoord(t, minimum(g)) spac = t.factors .* spacing(g) offs = offset(g) - CartesianGrid(dims, orig, spac, offs) + RegularGrid(dims, orig, spac, offs) end -applycoord(t::Scale, g::RegularGrid) = TransformedGrid(g, t) - applycoord(t::Scale, g::RectilinearGrid) = RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g))) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index ce37270ae..dd3ea2f63 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -63,6 +63,10 @@ apply(t::Shadow, tr::Torus) = TransformedGeometry(tr, t), nothing apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))), nothing +apply(t::Shadow, g::CartesianGrid) = _shadow(g, _sort(t.dims)), nothing + +apply(t::Shadow, g::RegularGrid) = TransformedGrid(g, t), nothing + apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing apply(t::Shadow, g::StructuredGrid) = TransformedGrid(g, t), nothing diff --git a/src/transforms/slice.jl b/src/transforms/slice.jl index 57556b349..d61d607e2 100644 --- a/src/transforms/slice.jl +++ b/src/transforms/slice.jl @@ -41,8 +41,8 @@ _slice(d::Domain, inds) = view(d, inds) _slice(g::Grid, inds::CartesianIndices) = getindex(g, inds) _sliceinds(d::Domain, b) = indices(d, b) -_sliceinds(g::CartesianGrid, b) = cartesianrange(g, b) -_sliceinds(g::RectilinearGrid, b) = cartesianrange(g, b) +_sliceinds(g::OrthoRegularGrid, b) = cartesianrange(g, b) +_sliceinds(g::OrthoRectilinearGrid, b) = cartesianrange(g, b) _sliceinds(g::Grid{🌐}, b::Box{🌐}) = cartesianrange(g, b) function _slicebox(box::Box{𝔼{2}}, limits) diff --git a/src/transforms/translate.jl b/src/transforms/translate.jl index c563d9536..083d2e843 100644 --- a/src/transforms/translate.jl +++ b/src/transforms/translate.jl @@ -37,12 +37,16 @@ applycoord(::Translate, v::Vec) = v # SPECIAL CASES # -------------- -apply(t::Translate, g::RectilinearGrid) = - RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], paramdim(g))), nothing +applycoord(t::Translate, g::RegularGrid) = TransformedGrid(g, t) -revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g)) +applycoord(t::Translate, g::OrthoRegularGrid) = RegularGrid(size(g), applycoord(t, minimum(g)), spacing(g), offset(g)) -apply(t::Translate, g::StructuredGrid) = - StructuredGrid{manifold(g),crs(g)}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], paramdim(g))), nothing +applycoord(t::Translate, g::RectilinearGrid) = TransformedGrid(g, t) -revert(t::Translate, g::StructuredGrid, c) = first(apply(inverse(t), g)) +applycoord(t::Translate, g::OrthoRectilinearGrid) = + RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], paramdim(g))) + +applycoord(t::Translate, g::StructuredGrid) = TransformedGrid(g, t) + +applycoord(t::Translate, g::OrthoStructuredGrid) = + StructuredGrid{manifold(g),crs(g)}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], paramdim(g))) diff --git a/test/transforms.jl b/test/transforms.jl index 13e398aa5..ae3ad4768 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -362,6 +362,16 @@ end @test all(r .≈ [f(t), f(t)]) @test all(TB.revert(f, r, c) .≈ d) + # ------------ + # REGULARGRID + # ------------ + + f = Translate(T(1), T(1)) + d = RegularGrid((8, 8), Point(Polar(T(0), T(0))), (T(1), T(π / 4))) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # -------------- # CARTESIANGRID # -------------- @@ -384,6 +394,13 @@ end @test r ≈ RectilinearGrid(T.(1:11), T.(1:11)) @test TB.revert(f, r, c) ≈ d + f = Translate(T(1), T(1)) + g = RegularGrid((8, 8), Point(Polar(T(0), T(0))), (T(1), T(π / 4))) + d = convert(RectilinearGrid, g) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # --------------- # STRUCTUREDGRID # --------------- @@ -395,6 +412,13 @@ end @test r ≈ StructuredGrid(repeat(T.(1:11), 1, 11), repeat(T.(1:11)', 11, 1)) @test TB.revert(f, r, c) ≈ d + f = Translate(T(1), T(1)) + g = RegularGrid((8, 8), Point(Polar(T(0), T(0))), (T(1), T(π / 4))) + d = convert(StructuredGrid, g) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + @test TB.revert(f, r, c) ≈ d + # ----------- # SIMPLEMESH # ----------- @@ -1403,6 +1427,15 @@ end r, c = TB.apply(f, d) @test all(r .≈ [f(t), f(t)]) + # ------------ + # REGULARGRID + # ------------ + + f = LengthUnit(u"cm") + d = RegularGrid((8, 8), Point(Polar(T(1), T(0))), (T(1), T(π / 4))) + r, c = TB.apply(f, d) + @test r ≈ RegularGrid((8, 8), Point(Polar(T(100) * u"cm", T(0) * u"rad")), (T(100) * u"cm", T(π / 4) * u"rad")) + # -------------- # CARTESIANGRID # -------------- @@ -1622,6 +1655,15 @@ end r, c = TB.apply(f, d) @test all(r .== [f(t), f(t)]) + # ------------ + # REGULARGRID + # ------------ + + f = Shadow(:yz) + d = RegularGrid((8, 8, 8), Point(Cylindrical(T(0), T(0), T(0))), (T(1), T(π / 4), T(1))) + r, c = TB.apply(f, d) + @test r == SimpleMesh(f.(vertices(d)), topology(d)) + # -------------- # CARTESIANGRID # -------------- From 14b4cd0830243de57cfefe6b36c9bc909942150d Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:05:35 -0300 Subject: [PATCH 357/423] Remove unused code (#1082) --- ext/grid/cartesian.jl | 2 -- ext/grid/rectilinear.jl | 2 -- ext/grid/structured.jl | 2 -- 3 files changed, 6 deletions(-) diff --git a/ext/grid/cartesian.jl b/ext/grid/cartesian.jl index 2a7b8ca6c..270f634a4 100644 --- a/ext/grid/cartesian.jl +++ b/ext/grid/cartesian.jl @@ -9,8 +9,6 @@ function vizgrid!(plot::Viz{<:Tuple{CartesianGrid}}, ::Type{<:𝔼}, ::Val{2}, : colormap = plot[:colormap] colorrange = plot[:colorrange] showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] # process color spec into colorant colorant = Makie.@lift process($color, $colormap, $colorrange, $alpha) diff --git a/ext/grid/rectilinear.jl b/ext/grid/rectilinear.jl index 4c9be43b2..7178b952e 100644 --- a/ext/grid/rectilinear.jl +++ b/ext/grid/rectilinear.jl @@ -9,8 +9,6 @@ function vizgrid!(plot::Viz{<:Tuple{RectilinearGrid}}, M::Type{<:𝔼}, pdim::Va colormap = plot[:colormap] colorrange = plot[:colorrange] showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] if crs(grid[]) <: Cartesian # process color spec into colorant diff --git a/ext/grid/structured.jl b/ext/grid/structured.jl index 27c1dae85..b8d928e50 100644 --- a/ext/grid/structured.jl +++ b/ext/grid/structured.jl @@ -9,8 +9,6 @@ function vizgrid!(plot::Viz{<:Tuple{StructuredGrid}}, M::Type{<:𝔼}, pdim::Val colormap = plot[:colormap] colorrange = plot[:colorrange] showsegments = plot[:showsegments] - segmentcolor = plot[:segmentcolor] - segmentsize = plot[:segmentsize] if crs(grid[]) <: Cartesian # process color spec into colorant From 7b49416f44c0b91ca6da4e36bfad420a3c2ef9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 20 Sep 2024 18:07:08 -0300 Subject: [PATCH 358/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index df239c43b..613a04638 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.11" +version = "0.51.12" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 35d1d455437731475714d83a985361f2a81a0e8f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:08:50 -0300 Subject: [PATCH 359/423] Viz: Reverse the connectivity of grids with CW orientation (#1084) --- ext/grid.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/grid.jl b/ext/grid.jl index 29dd2ee76..10a743aeb 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -61,7 +61,7 @@ function vizgridfallback!(plot, M, pdim, edim) # or when there is a large number of elements if pdim == Val(2) && (ncolor[] == 1 || ncolor[] == nverts[] || nelems[] ≥ 1000) # decide whether or not to reverse connectivity list - rfunc = Makie.@lift _reverse(crs($grid)) + rfunc = Makie.@lift _reverse(first($grid)) verts = Makie.@lift map(asmakie, vertices($grid)) quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($grid))] @@ -94,8 +94,7 @@ function vizgridfallback!(plot, M, pdim, edim) end end -_reverse(::Type{<:CRS}) = identity -_reverse(::Type{<:LatLon}) = reverse +_reverse(quad) = orientation(quad) == CW ? reverse : identity # helper functions to create a minimum number # of line segments within Cartesian/Rectilinear grid From 56047255cc74f80a56a4e6d5abc046b6522f1a57 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:09:21 -0300 Subject: [PATCH 360/423] Viz geometries with globe manifold and `paramdim=1` using `MaxLengthDiscretization` (#1083) * Viz geometries with globe manifold and 'paramdim=1' using 'MaxLengthDiscretization' * Update code * Apply suggestions --- ext/geometryset.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 35ff30de6..433afd1c6 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -26,6 +26,21 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms::ObservableVector Makie.scatter!(plot, coords, color=colorant, marker=pointmarker, markersize=pointsize, overdraw=true) end +function vizgset!(plot, ::Type{<:🌐}, ::Val{1}, ::Val, geoms, colorant) + showpoints = plot[:showpoints] + + meshes = Makie.@lift begin + T = numtype(Meshes.lentype(first($geoms))) + method = MaxLengthDiscretization(T(1000) * u"km") + [discretize(g, method) for g in $geoms] + end + vizmany!(plot, meshes, colorant) + + if showpoints[] + vizfacets!(plot, geoms) + end +end + function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms, colorant) showpoints = plot[:showpoints] From b29b7cd0b4480ffe49ba27efe46ff42f53861228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 23 Sep 2024 12:30:55 -0300 Subject: [PATCH 361/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 613a04638..c3d2e3287 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.12" +version = "0.51.13" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From dd9115efad4e7063d18b865eefa8eb3848dc4fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 24 Sep 2024 12:09:02 -0300 Subject: [PATCH 362/423] Specialize centroid for CylinderTrajectory (#1085) --- src/domains/trajecs.jl | 2 ++ test/trajecs.jl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/domains/trajecs.jl b/src/domains/trajecs.jl index 9c3bab238..43db14b93 100644 --- a/src/domains/trajecs.jl +++ b/src/domains/trajecs.jl @@ -72,4 +72,6 @@ nelements(t::CylindricalTrajectory) = length(t.centroids) Base.eltype(t::CylindricalTrajectory) = typeof(first(t)) +centroid(t::CylindricalTrajectory, ind::Int) = t.centroids[ind] + radius(t::CylindricalTrajectory) = t.radius diff --git a/test/trajecs.jl b/test/trajecs.jl index 99f209d5f..f71cca970 100644 --- a/test/trajecs.jl +++ b/test/trajecs.jl @@ -8,6 +8,8 @@ @test nelements(t) == 10 @test radius(t) == T(1) * u"m" @test topology(t) == GridTopology(10) + @test centroid(t, 1) == cart(0, 0, 0) + @test centroid(t, 10) == cart(0, 0, 1) b = BezierCurve([cart(0, 0, 0), cart(3, 3, 0), cart(3, 0, 7)]) c = [b(t) for t in range(T(0), stop=T(1), length=20)] @@ -18,6 +20,8 @@ @test nelements(t) == 20 @test radius(t) == T(2) * u"m" @test topology(t) == GridTopology(20) + @test centroid(t, 1) == cart(0, 0, 0) + @test centroid(t, 20) == cart(3, 0, 7) # trajectory with single cylinder t = CylindricalTrajectory([cart(0, 0, 0)], T(1)) From a355bc99ce27163ef5e3a74c307a025264b0daf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 24 Sep 2024 12:09:44 -0300 Subject: [PATCH 363/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c3d2e3287..dba4508d8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.13" +version = "0.51.14" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 07908d34cadebeaed1f3866eea2417074a207f2d Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:24:04 -0300 Subject: [PATCH 364/423] Viz boxes with globe manifold using 'MaxLengthDiscretization' (#1087) --- ext/geometryset.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index 433afd1c6..aa21804bf 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -102,6 +102,21 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val{2}, geoms::ObservableVec end end +function vizgset!(plot, ::Type{<:🌐}, ::Val{2}, ::Val, geoms::ObservableVector{<:Box}, colorant) + showsegments = plot[:showsegments] + + meshes = Makie.@lift begin + T = numtype(Meshes.lentype(first($geoms))) + method = MaxLengthDiscretization(T(100) * u"km") + [discretize(g, method) for g in $geoms] + end + vizmany!(plot, meshes, colorant) + + if showsegments[] + vizfacets!(plot, geoms) + end +end + function vizgset!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val, geoms, colorant) meshes = Makie.@lift discretize.(boundary.($geoms)) vizmany!(plot, meshes, colorant) From 53fa85db9ea267189bd1b27ffc188bf2086bfaf3 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:24:18 -0300 Subject: [PATCH 365/423] Viz: Update connectivity reversion heuristic (#1088) --- ext/grid.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/grid.jl b/ext/grid.jl index 10a743aeb..1ef418180 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -61,7 +61,7 @@ function vizgridfallback!(plot, M, pdim, edim) # or when there is a large number of elements if pdim == Val(2) && (ncolor[] == 1 || ncolor[] == nverts[] || nelems[] ≥ 1000) # decide whether or not to reverse connectivity list - rfunc = Makie.@lift _reverse(first($grid)) + rfunc = Makie.@lift _reverse($grid) verts = Makie.@lift map(asmakie, vertices($grid)) quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($grid))] @@ -94,7 +94,7 @@ function vizgridfallback!(plot, M, pdim, edim) end end -_reverse(quad) = orientation(quad) == CW ? reverse : identity +_reverse(grid) = crs(grid) <: LatLon && orientation(first(grid)) == CW ? reverse : identity # helper functions to create a minimum number # of line segments within Cartesian/Rectilinear grid From b4b39a2eb52452dab21669d22780a6c3ce14be48 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:24:46 -0300 Subject: [PATCH 366/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index dba4508d8..5acec5738 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.14" +version = "0.51.15" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 80c9dcf5877dede733d756d36095a14d78f44f25 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:38:50 -0300 Subject: [PATCH 367/423] Fix parameterization of 'Segement' with ellipsoid manifold (#1089) --- src/geometries/polytopes/segment.jl | 12 +++++++++++- test/polytopes.jl | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/geometries/polytopes/segment.jl b/src/geometries/polytopes/segment.jl index 0c1623afe..dd7799389 100644 --- a/src/geometries/polytopes/segment.jl +++ b/src/geometries/polytopes/segment.jl @@ -26,7 +26,7 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] Base.isapprox(s₁::Segment, s₂::Segment; atol=atol(lentype(s₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(s₁.vertices, s₂.vertices)) -function (s::Segment)(t) +function (s::Segment{<:𝔼})(t) if t < 0 || t > 1 throw(DomainError(t, "s(t) is not defined for t outside [0, 1].")) end @@ -34,4 +34,14 @@ function (s::Segment)(t) a + t * (b - a) end +function (s::Segment{<:🌐})(t) + if t < 0 || t > 1 + throw(DomainError(t, "s(t) is not defined for t outside [0, 1].")) + end + verts = convert.(LatLon, coords.(s.vertices)) + a, b = CoordRefSystems.values.(verts) + vals = a .+ t .* (b .- a) + withcrs(s, vals, LatLon) +end + Base.reverse(s::Segment) = Segment(reverse(extrema(s))) diff --git a/test/polytopes.jl b/test/polytopes.jl index 8c88c26b0..c15badffb 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -82,6 +82,14 @@ s = Segment(latlon(0, 135), latlon(0, 45)) @test_broken measure(s) ≈ 3C / 4 + # parameterization + s = Segment(latlon(45, 0), latlon(45, 90)) + @test s(T(0)) == latlon(45, 0) + @test s(T(0.25)) == latlon(45, 22.5) + @test s(T(0.5)) == latlon(45, 45) + @test s(T(0.75)) == latlon(45, 67.5) + @test s(T(1)) == latlon(45, 90) + s = Segment(cart(0, 0), cart(1, 1)) @test sprint(show, s) == "Segment((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 1.0 m))" if T === Float32 From 887e723004c1a9163944430ebe0ff37450a4e891 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 27 Sep 2024 18:22:58 -0300 Subject: [PATCH 368/423] Add `coordsum` and `coordmean` helper functions (#1091) * Add 'coordsum' and 'coordmean' helper functions * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update 'Line' parameterization * Update 'centroid' of 'Domain' * Fix typo * Add tests * Update 'TriRefinement' and 'QuadRefinement' --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/centroid.jl | 4 +-- src/geometries/polytopes/ngon.jl | 12 ++++--- src/geometries/polytopes/segment.jl | 14 ++------- src/geometries/primitives/line.jl | 2 +- src/refinement/quad.jl | 6 ++-- src/refinement/tri.jl | 3 +- src/utils/crs.jl | 49 +++++++++++++++++++++++++++++ test/polytopes.jl | 27 ++++++++++++++++ test/primitives.jl | 11 +++++++ test/refinement.jl | 16 ++++++++++ 10 files changed, 119 insertions(+), 25 deletions(-) diff --git a/src/centroid.jl b/src/centroid.jl index eedb5c6c9..066430bf2 100644 --- a/src/centroid.jl +++ b/src/centroid.jl @@ -13,9 +13,9 @@ centroid(p::Point) = p centroid(p::Polygon) = centroid(first(rings(p))) -centroid(p::Polytope) = withcrs(p, sum(to, vertices(p)) / nvertices(p)) +centroid(p::Polytope) = coordmean(vertices(p)) -centroid(b::Box) = withcrs(b, sum(to, extrema(b)) / 2) +centroid(b::Box) = coordmean(extrema(b)) centroid(p::Plane) = p(0, 0) diff --git a/src/geometries/polytopes/ngon.jl b/src/geometries/polytopes/ngon.jl index 9216af334..9d0012a6b 100644 --- a/src/geometries/polytopes/ngon.jl +++ b/src/geometries/polytopes/ngon.jl @@ -78,8 +78,8 @@ function (t::Triangle)(u, v) if (u < 0 || u > 1) || (v < 0 || v > 1) || (w < 0 || w > 1) throw(DomainError((u, v), "invalid barycentric coordinates for triangle.")) end - v₁, v₂, v₃ = to.(t.vertices) - withcrs(t, v₁ * w + v₂ * u + v₃ * v) + v₁, v₂, v₃ = t.vertices + coordsum((v₁, v₂, v₃), weights=(w, u, v)) end # ------------ @@ -91,6 +91,10 @@ function (q::Quadrangle)(u, v) if (u < 0 || u > 1) || (v < 0 || v > 1) throw(DomainError((u, v), "q(u, v) is not defined for u, v outside [0, 1]².")) end - c₀₀, c₀₁, c₁₁, c₁₀ = to.(q.vertices) - withcrs(q, c₀₀ * (1 - u) * (1 - v) + c₀₁ * u * (1 - v) + c₁₀ * (1 - u) * v + c₁₁ * u * v) + c₀₀, c₀₁, c₁₁, c₁₀ = q.vertices + w₀₀ = (1 - u) * (1 - v) + w₀₁ = u * (1 - v) + w₁₀ = (1 - u) * v + w₁₁ = u * v + coordsum((c₀₀, c₀₁, c₁₀, c₁₁), weights=(w₀₀, w₀₁, w₁₀, w₁₁)) end diff --git a/src/geometries/polytopes/segment.jl b/src/geometries/polytopes/segment.jl index dd7799389..28e523dae 100644 --- a/src/geometries/polytopes/segment.jl +++ b/src/geometries/polytopes/segment.jl @@ -26,22 +26,12 @@ Base.extrema(s::Segment) = s.vertices[1], s.vertices[2] Base.isapprox(s₁::Segment, s₂::Segment; atol=atol(lentype(s₁)), kwargs...) = all(isapprox(v₁, v₂; atol, kwargs...) for (v₁, v₂) in zip(s₁.vertices, s₂.vertices)) -function (s::Segment{<:𝔼})(t) +function (s::Segment)(t) if t < 0 || t > 1 throw(DomainError(t, "s(t) is not defined for t outside [0, 1].")) end a, b = s.vertices - a + t * (b - a) -end - -function (s::Segment{<:🌐})(t) - if t < 0 || t > 1 - throw(DomainError(t, "s(t) is not defined for t outside [0, 1].")) - end - verts = convert.(LatLon, coords.(s.vertices)) - a, b = CoordRefSystems.values.(verts) - vals = a .+ t .* (b .- a) - withcrs(s, vals, LatLon) + coordsum((a, b), weights=((1 - t), t)) end Base.reverse(s::Segment) = Segment(reverse(extrema(s))) diff --git a/src/geometries/primitives/line.jl b/src/geometries/primitives/line.jl index f5ec752a3..14e895c60 100644 --- a/src/geometries/primitives/line.jl +++ b/src/geometries/primitives/line.jl @@ -23,4 +23,4 @@ paramdim(::Type{<:Line}) = 1 Base.isapprox(l₁::Line, l₂::Line; atol=atol(lentype(l₁)), kwargs...) = isapprox(l₁.a, l₂.a; atol, kwargs...) && isapprox(l₁.b, l₂.b; atol, kwargs...) -(l::Line)(t) = l.a + t * (l.b - l.a) +(l::Line)(t) = coordsum((l.a, l.b), weights=((1 - t), t)) diff --git a/src/refinement/quad.jl b/src/refinement/quad.jl index 4b93ad933..8fc61c5e0 100644 --- a/src/refinement/quad.jl +++ b/src/refinement/quad.jl @@ -22,16 +22,14 @@ function refine(mesh, ::QuadRefinement) ∂₂₀ = Boundary{2,0}(t) epts = map(1:nelements(t)) do elem is = ∂₂₀(elem) - cₒ = sum(j -> to(points[j]), is) / length(is) - withcrs(mesh, cₒ) + coordmean(points[i] for i in is) end # add midpoints of edges ∂₁₀ = Boundary{1,0}(t) fpts = map(1:nfacets(t)) do edge is = ∂₁₀(edge) - cₒ = sum(i -> to(points[i]), is) / length(is) - withcrs(mesh, cₒ) + coordmean(points[i] for i in is) end # original vertices diff --git a/src/refinement/tri.jl b/src/refinement/tri.jl index b328895b5..c03a772f5 100644 --- a/src/refinement/tri.jl +++ b/src/refinement/tri.jl @@ -38,8 +38,7 @@ function refine(mesh, method::TriRefinement) ∂₂₀ = Boundary{2,0}(topo) rpts = map(rinds) do elem is = ∂₂₀(elem) - cₒ = sum(i -> to(points[i]), is) / length(is) - withcrs(mesh, cₒ) + coordmean(points[i] for i in is) end # original vertices diff --git a/src/utils/crs.jl b/src/utils/crs.jl index 19a64dc32..297607901 100644 --- a/src/utils/crs.jl +++ b/src/utils/crs.jl @@ -32,3 +32,52 @@ ignoring the original units of the coordinate reference system. """ flat(p::Point) = Point(flat(coords(p))) flat(c::CRS) = Cartesian{datum(c)}(CoordRefSystems.raw(c)) + +""" + coordsum(points; weights=nothing) + +Sum of the base coordinates of the points, `Cartesian` for `𝔼` and `LatLon` for `🌐`. +If `weights` is passed, the weighted sum will be returned. +""" +function coordsum(points; weights=nothing) + values = _coordsum(points, weights) + fromvalues(first(points), values) +end + +""" + coordmean(points; weights=nothing) + +Mean of the base coordinates of the points, `Cartesian` for `𝔼` and `LatLon` for `🌐`. +If `weights` is passed, the weighted mean will be returned. +""" +function coordmean(points; weights=nothing) + den = if isnothing(weights) + length(points) + else + sum(weights) + end + values = _coordsum(points, weights) ./ den + fromvalues(first(points), values) +end + +function tovalues(p) + CRS = _basecrs(manifold(p)) + c = convert(CRS, coords(p)) + CoordRefSystems.values(c) +end + +function fromvalues(g, values) + CRS = _basecrs(manifold(g)) + withcrs(g, values, CRS) +end + +function _coordsum(points, weights) + if isnothing(weights) + mapreduce(tovalues, .+, points) + else + mapreduce((p, w) -> tovalues(p) .* w, .+, points, weights) + end +end + +_basecrs(::Type{<:𝔼}) = Cartesian +_basecrs(::Type{<:🌐}) = LatLon diff --git a/test/polytopes.jl b/test/polytopes.jl index c15badffb..6b710f047 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -402,6 +402,18 @@ end t = Triangle(merc(0, 0), merc(1, 0), merc(0, 1)) @test crs(t(T(0), T(0))) === crs(t) + # parameterization + t = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) + @test t(T(0), T(0)) == latlon(0, 0) + @test t(T(0.5), T(0)) == latlon(0, 22.5) + @test t(T(1), T(0)) == latlon(0, 45) + @test t(T(0), T(0.5)) == latlon(22.5, 0) + @test t(T(0), T(1)) == latlon(45, 0) + + # centroid + t = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) + @test centroid(t) == latlon(15, 15) + t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test sprint(show, t) == "Triangle((x: 0.0 m, y: 0.0 m), (x: 1.0 m, y: 0.0 m), (x: 0.0 m, y: 1.0 m))" if T === Float32 @@ -474,6 +486,21 @@ end q = Quadrangle(merc(0, 0), merc(1, 0), merc(1, 1), merc(0, 1)) @test crs(q(T(0), T(0))) === crs(q) + # parameterization + q = Quadrangle(latlon(0, 0), latlon(0, 45), latlon(45, 45), latlon(45, 0)) + @test q(T(0), T(0)) == latlon(0, 0) + @test q(T(0.5), T(0)) == latlon(0, 22.5) + @test q(T(1), T(0)) == latlon(0, 45) + @test q(T(1), T(0.5)) == latlon(22.5, 45) + @test q(T(1), T(1)) == latlon(45, 45) + @test q(T(0.5), T(1)) == latlon(45, 22.5) + @test q(T(0), T(1)) == latlon(45, 0) + @test q(T(0), T(0.5)) == latlon(22.5, 0) + + # centroid + q = Quadrangle(latlon(0, 0), latlon(0, 45), latlon(45, 45), latlon(45, 0)) + @test centroid(q) == latlon(22.5, 22.5) + q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test sprint(show, q) == "Quadrangle((x: 0.0 m, y: 0.0 m), ..., (x: 0.0 m, y: 1.0 m))" if T === Float32 diff --git a/test/primitives.jl b/test/primitives.jl index a80c113e1..8b69c8961 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -269,6 +269,13 @@ end l = Line(cart(0, 0), cart(1, 1)) @test (l(0), l(1)) == (cart(0, 0), cart(1, 1)) + l = Line(latlon(45, 0), latlon(45, 90)) + @test l(T(0)) == latlon(45, 0) + @test l(T(0.25)) == latlon(45, 22.5) + @test l(T(0.5)) == latlon(45, 45) + @test l(T(0.75)) == latlon(45, 67.5) + @test l(T(1)) == latlon(45, 90) + l = Line(cart(0, 0), cart(1, 1)) @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" if T === Float32 @@ -517,6 +524,10 @@ end b = Box(merc(0, 0), merc(1, 1)) @test crs(centroid(b)) === crs(b) + # centroid + b = Box(latlon(0, 0), latlon(30, 60)) + @test centroid(b) == latlon(15, 30) + b = Box(cart(0, 0), cart(1, 1)) @test sprint(show, b) == "Box(min: (x: 0.0 m, y: 0.0 m), max: (x: 1.0 m, y: 1.0 m))" if T === Float32 diff --git a/test/refinement.jl b/test/refinement.jl index a1f6d5c0c..7c69c16a2 100644 --- a/test/refinement.jl +++ b/test/refinement.jl @@ -26,6 +26,14 @@ ref = refine(mesh, TriRefinement(e -> measure(e) ≤ T(1) * u"m^2")) @test nelements(ref) == 15 @test nvertices(ref) == 13 + + # latlon + points = latlon.([(0, 0), (0, 4), (0, 8), (1, 3), (1, 5), (2, 2), (2, 4), (2, 6), (4, 4)]) + connec = connect.([(1, 2, 6), (2, 3, 8), (6, 8, 9), (2, 5, 4), (4, 5, 7), (4, 7, 6), (5, 8, 7)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, TriRefinement()) + @test nelements(ref) == 21 + @test nvertices(ref) == 16 end @testitem "QuadRefinement" setup = [Setup] begin @@ -50,6 +58,14 @@ end mesh = SimpleMesh(points, connec) ref = refine(mesh, QuadRefinement()) @test crs(ref) === crs(mesh) + + # latlon + points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.25, 0.25), (0.25, 0.75), (0.75, 0.5)]) + connec = connect.([(1, 2, 6, 5), (1, 5, 7, 3), (2, 4, 7, 6), (3, 7, 4)]) + mesh = SimpleMesh(points, connec) + ref = refine(mesh, QuadRefinement()) + @test nelements(ref) == 15 + @test nvertices(ref) == 22 end @testitem "RegularRefinement" setup = [Setup] begin From 52fb8f7fa06e31f05b1cd18fa40e5c677db8e714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 27 Sep 2024 18:25:09 -0300 Subject: [PATCH 369/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5acec5738..ae966540b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.15" +version = "0.51.16" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From c142d429d11c1cd11dbbeeb3d86826f6ed387bd4 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:09:56 +0200 Subject: [PATCH 370/423] fix docstring rendering (#1094) --- src/transforms/smoothing.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transforms/smoothing.jl b/src/transforms/smoothing.jl index 253b61d2b..fdecd8085 100644 --- a/src/transforms/smoothing.jl +++ b/src/transforms/smoothing.jl @@ -66,7 +66,7 @@ function _smooth(mesh, L, n, λ, μ; revert=false) end """ -LaplaceSmoothing(n, λ=0.5) + LaplaceSmoothing(n, λ=0.5) Perform `n` iterations of Laplace smoothing with parameter `λ`. @@ -78,7 +78,7 @@ Perform `n` iterations of Laplace smoothing with parameter `λ`. LaplaceSmoothing(n, λ=0.5) = LambdaMuSmoothing(n, λ, zero(λ)) """ -TaubinSmoothing(n, λ=0.5) + TaubinSmoothing(n, λ=0.5) Perform `n` iterations of Taubin smoothing with parameter `0 < λ < 1`. From a673eb0b1760ff3f370513cdfc289546f234dc16 Mon Sep 17 00:00:00 2001 From: Elias Carvalho Date: Tue, 1 Oct 2024 14:03:46 -0300 Subject: [PATCH 371/423] Update dependencies --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index ae966540b..666e5fa5a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.16" +version = "0.51.17" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" @@ -25,7 +25,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" Bessels = "0.2" CircularArrays = "1.3" Colorfy = "0.1, 1" -CoordRefSystems = "0.14" +CoordRefSystems = "0.15" DelaunayTriangulation = "1.0" Distances = "0.10" LinearAlgebra = "1.9" From ccb84352881bfe86609f6993f632fd57cbf584f2 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 4 Oct 2024 08:45:17 -0300 Subject: [PATCH 372/423] Update parameterization implementation of `Ball` and `Sphere` (#1097) * Update parameterization implementation of 'Ball' and 'Sphere' * Apply suggestions --- src/geometries/primitives/ball.jl | 21 +++++++++------------ src/geometries/primitives/sphere.jl | 17 +++++++---------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/geometries/primitives/ball.jl b/src/geometries/primitives/ball.jl index 22e991eb2..2ef1919e3 100644 --- a/src/geometries/primitives/ball.jl +++ b/src/geometries/primitives/ball.jl @@ -43,11 +43,10 @@ function (b::Ball{𝔼{2}})(ρ, φ) end c = b.center r = b.radius - l = T(ρ) * r - sφ, cφ = sincospi(2 * T(φ)) - x = l * cφ - y = l * sφ - c + Vec(x, y) + ρ′ = T(ρ) * r + φ′ = T(φ) * 2 * T(π) * u"rad" + p = Point(convert(crs(b), Polar(ρ′, φ′))) + p + to(c) end function (b::Ball{𝔼{3}})(ρ, θ, φ) @@ -57,11 +56,9 @@ function (b::Ball{𝔼{3}})(ρ, θ, φ) end c = b.center r = b.radius - l = T(ρ) * r - sθ, cθ = sincospi(T(θ)) - sφ, cφ = sincospi(2 * T(φ)) - x = l * sθ * cφ - y = l * sθ * sφ - z = l * cθ - c + Vec(x, y, z) + ρ′ = T(ρ) * r + θ′ = T(θ) * T(π) * u"rad" + φ′ = T(φ) * 2 * T(π) * u"rad" + p = Point(convert(crs(b), Spherical(ρ′, θ′, φ′))) + p + to(c) end diff --git a/src/geometries/primitives/sphere.jl b/src/geometries/primitives/sphere.jl index c04a7ddbd..e5d960204 100644 --- a/src/geometries/primitives/sphere.jl +++ b/src/geometries/primitives/sphere.jl @@ -80,10 +80,9 @@ function (s::Sphere{𝔼{2}})(φ) end c = s.center r = s.radius - sφ, cφ = sincospi(2 * T(φ)) - x = r * cφ - y = r * sφ - c + Vec(x, y) + φ′ = T(φ) * 2 * T(π) * u"rad" + p = Point(convert(crs(s), Polar(r, φ′))) + p + to(c) end function (s::Sphere{𝔼{3}})(θ, φ) @@ -93,10 +92,8 @@ function (s::Sphere{𝔼{3}})(θ, φ) end c = s.center r = s.radius - sθ, cθ = sincospi(T(θ)) - sφ, cφ = sincospi(2 * T(φ)) - x = r * sθ * cφ - y = r * sθ * sφ - z = r * cθ - c + Vec(x, y, z) + θ′ = T(θ) * T(π) * u"rad" + φ′ = T(φ) * 2 * T(π) * u"rad" + p = Point(convert(crs(s), Spherical(r, θ′, φ′))) + p + to(c) end From 5ca1eddd300531eb92817ac799d49726dad5b4e2 Mon Sep 17 00:00:00 2001 From: Davi Sales Barreira Date: Fri, 4 Oct 2024 12:57:13 -0300 Subject: [PATCH 373/423] Fibonacci Sampling Method (#1092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Implementing Fibonacci sampling method for disk, ball and sphere. * :test_tube: Adding tests for Fibonacci sampling. * :truck: Refactoring Fibonnaci Sampling. Added box method, and capacity to alter the ratio number. Also, sampling now preserves the lentype and numtype for the underlying geometry. * :bug: Fixed ustrip warning. * :test_tube: Updated tests for fibonacci sampling. * :memo: Docs for fibonacci sampling. * :memo: Moving Fibonacci Sampling to ContinuousSampling * :bug: Correcting allocations and fibonacci constructor. * :truck: Small formatting correction. * :truck: Refactor using the geom parametrization * :bug: removing comments and adding distortion for sphere. * :truck: Adding Ball{🌐} and Sphere{𝔼{3}} * :test_tube: Added tests for ArgumentError cases. * :bug: Fixed formatting * Update src/sampling/fibonacci.jl * Refactoring * Adding license. * Update src/sampling/fibonacci.jl * :test_tube: changing test for disk fibonacci sampling. * Refactor tests * Fix T in test --------- Co-authored-by: Júlio Hoffimann --- docs/src/algorithms/sampling.md | 17 ++++++++++- src/Meshes.jl | 1 + src/sampling.jl | 1 + src/sampling/fibonacci.jl | 50 +++++++++++++++++++++++++++++++++ test/sampling.jl | 45 +++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/sampling/fibonacci.jl diff --git a/docs/src/algorithms/sampling.md b/docs/src/algorithms/sampling.md index a0db927f8..c533eaeaf 100644 --- a/docs/src/algorithms/sampling.md +++ b/docs/src/algorithms/sampling.md @@ -113,4 +113,19 @@ sampler = MinDistanceSampling(3.0) points = sample(grid, sampler) |> collect viz(points) -``` \ No newline at end of file +``` + +### FibonacciSampling +```@docs +FibonacciSampling +``` + +```@example sampling +sphere = Sphere((0.,0.,0.), 1.) + +# sample points using the Fibonacci lattice method +sampler = FibonacciSampling(100) +points = sample(sphere, sampler) |> collect + +viz(points) +``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 31c1eb6ef..544538d39 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -485,6 +485,7 @@ export RegularSampling, HomogeneousSampling, MinDistanceSampling, + FibonacciSampling, sampleinds, sample, diff --git a/src/sampling.jl b/src/sampling.jl index b42dd9541..2e229651f 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -65,6 +65,7 @@ sample(rng::AbstractRNG, g::Geometry, method::ContinuousSamplingMethod) = sample include("sampling/regular.jl") include("sampling/homogeneous.jl") include("sampling/mindistance.jl") +include("sampling/fibonacci.jl") # ---------- # UTILITIES diff --git a/src/sampling/fibonacci.jl b/src/sampling/fibonacci.jl new file mode 100644 index 000000000..67f35f84a --- /dev/null +++ b/src/sampling/fibonacci.jl @@ -0,0 +1,50 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + FibonacciSampling(n, ϕ = (1 + √5)/2) + +Generate `n` Fibonacci points with parameter `ϕ`. + +The golden ratio is used as the default value of `ϕ`, +but other irrational numbers can be used. + +See +and . +""" +struct FibonacciSampling{T<:Real} <: ContinuousSamplingMethod + n::Int + ϕ::T + + function FibonacciSampling(n::Int, ϕ::T) where {T<:Real} + if n ≤ 0 + throw(ArgumentError("Size must be positive")) + end + new{T}(n, ϕ) + end +end + +FibonacciSampling(n::Int) = FibonacciSampling(n, (1 + √5) / 2) + +function sample(geom::Geometry, method::FibonacciSampling) + if paramdim(geom) != 2 + throw(ArgumentError("Fibonacci sampling only defined for 2D geometries")) + end + + fib = _fibmap(geom) + + function point(i) + u = mod(i / method.ϕ, 1) + v = i / (method.n - 1) + geom(fib(u, v)...) + end + + (point(i) for i in 0:(method.n - 1)) +end + +_fibmap(g) = (u, v) -> (u, v) +_fibmap(d::Disk) = (u, v) -> (√u, v) +_fibmap(b::Ball{𝔼{2}}) = (u, v) -> (√u, v) +_fibmap(b::Ball{🌐}) = (u, v) -> (√u, v) +_fibmap(s::Sphere{𝔼{3}}) = (u, v) -> (acos(1 - 2v) / π, u) diff --git a/test/sampling.jl b/test/sampling.jl index 5d498f0c3..7a1b47e6b 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -387,6 +387,51 @@ end @test length(ps) > 0 end +@testitem "FibonacciSampling" setup = [Setup] begin + @test_throws ArgumentError sample(Box(cart(0, 0), cart(1, 1)), FibonacciSampling(-1)) + @test_throws ArgumentError sample(Box(Point(0, 0, 0), Point(1, 1, 1)), FibonacciSampling(100)) + + box = Box(cart(1, 1), cart(4, 2)) + ps = sample(box, FibonacciSampling(100)) |> collect + @test first(ps) isa Point + @test first(ps) ≈ cart(1, 1) + @test all(∈(box), ps) + + box = Box(cart(0, 0), cart(1, 1)) + ps = sample(box, FibonacciSampling(100, π)) |> collect + @test first(ps) isa Point + @test all(∈(box), ps) + @test ps[2] ≈ cart(mod(1 / π, 1), 1 / 99) + + tbox = Box(cart(0, 0), cart(1, 1)) + af = Affine(T[1 1; 0 1], T[2, 0]) + tbox = af(tbox) + ps = sample(tbox, FibonacciSampling(100)) |> collect + @test first(ps) isa Point + @test first(ps) ≈ af(cart(0, 0)) + @test all(∈(tbox), ps) + + disk = Disk(Plane(cart(3, 0, 0), Vec(1, 0, 0)), T(2)) + ps = sample(disk, FibonacciSampling(100)) |> collect + @test first(ps) isa Point + @test first(ps) ≈ centroid(disk) + @test all(p -> coords(p).x ≈ 3u"m", ps) + @test all(p -> -2u"m" < coords(p).y || coords(p).y < 2u"m" || isapprox(coords(p).y, 2u"m"; atol=1e-5u"m"), ps) + @test all(p -> -2u"m" < coords(p).z || coords(p).z < 2u"m" || isapprox(coords(p).z, 2u"m"; atol=1e-5u"m"), ps) + + sphere = Sphere(cart(1, 1, 1), T(2)) + ps = sample(sphere, FibonacciSampling(100)) |> collect + @test first(ps) isa Point + @test first(ps) ≈ cart(1, 1, 3) + @test all(∈(sphere), ps) + + ball = Ball(cart(2, 1), T(0.1)) + ps = sample(ball, FibonacciSampling(100)) |> collect + @test first(ps) isa Point + @test first(ps) ≈ centroid(ball) + @test all(∈(ball), ps) +end + @testitem "RNGs" setup = [Setup] begin dom = cartgrid(100, 100) for method in [UniformSampling(100), WeightedSampling(100), BallSampling(T(10))] From ab5ddb44005590d04b682fc4c83aac387fdd2301 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:05:04 -0300 Subject: [PATCH 374/423] Update parameterization implementation of `Cylinder` and `CylinderSurface` & Simplification of `Sphere` parameterization (#1098) * Update parameterization implementation of 'Cylinder' * Fix typo * Update parameterization implementation of 'CylinderSurface' * Update parameterization implementation of 'Sphere' * Apply suggestions * Apply suggestions * Add more tests * Apply suggestions * Update code * Apply suggestions --- src/geometries/primitives/cylinder.jl | 11 ++++--- src/geometries/primitives/cylindersurface.jl | 34 +------------------- src/geometries/primitives/sphere.jl | 25 +------------- test/primitives.jl | 24 ++++++++++++++ 4 files changed, 32 insertions(+), 62 deletions(-) diff --git a/src/geometries/primitives/cylinder.jl b/src/geometries/primitives/cylinder.jl index 1ef022877..c1ccf1eed 100644 --- a/src/geometries/primitives/cylinder.jl +++ b/src/geometries/primitives/cylinder.jl @@ -83,16 +83,17 @@ function (c::Cylinder)(ρ, φ, z) a = axis(c) d = a(T(1)) - a(T(0)) h = norm(d) - o = b(0, 0) + o = b(T(0), T(0)) # rotation to align z axis with cylinder axis Q = urotbetween(Vec(zero(ℒ), zero(ℒ), oneunit(ℒ)), d) # project a parametric segment between the top and bottom planes - lsφ, lcφ = T(ρ) * r .* sincospi(2 * T(φ)) - p₁ = o + Q * Vec(lcφ, lsφ, zero(ℒ)) - p₂ = o + Q * Vec(lcφ, lsφ, h) - l = Line(p₁, p₂) + ρ′ = T(ρ) * r + φ′ = T(φ) * 2 * T(π) * u"rad" + p₁ = Point(convert(crs(c), Cylindrical(ρ′, φ′, zero(ℒ)))) + p₂ = Point(convert(crs(c), Cylindrical(ρ′, φ′, h))) + l = Line(p₁, p₂) |> Affine(Q, to(o)) s = Segment(l ∩ b, l ∩ t) s(T(z)) end diff --git a/src/geometries/primitives/cylindersurface.jl b/src/geometries/primitives/cylindersurface.jl index e7ad728a9..b82581340 100644 --- a/src/geometries/primitives/cylindersurface.jl +++ b/src/geometries/primitives/cylindersurface.jl @@ -83,39 +83,7 @@ Base.isapprox(c₁::CylinderSurface, c₂::CylinderSurface; atol=atol(lentype(c isapprox(c₁.top, c₂.top; atol, kwargs...) && isapprox(c₁.radius, c₂.radius; atol, kwargs...) -function (c::CylinderSurface)(φ, z) - ℒ = lentype(c) - T = numtype(ℒ) - if (φ < 0 || φ > 1) || (z < 0 || z > 1) - throw(DomainError((φ, z), "c(φ, z) is not defined for φ, z outside [0, 1]².")) - end - t = top(c) - b = bottom(c) - r = radius(c) - a = axis(c) - d = a(T(1)) - a(T(0)) - h = norm(d) - o = centroid(c) - - # rotation to align z axis with cylinder axis - Q = urotbetween(d, Vec(zero(ℒ), zero(ℒ), oneunit(ℒ))) - - # new normals of planes in new rotated system - nᵦ = Q * normal(b) - nₜ = Q * normal(t) - - # given cylindrical coordinates (r*cos(φ), r*sin(φ), z) and the - # equation of the plane, we can solve for z and find all points - # along the ellipse obtained by intersection - rsφ, rcφ = r .* sincospi(2 * T(φ)) - zᵦ = -h / 2 - (rcφ * nᵦ[1] + rsφ * nᵦ[2]) / nᵦ[3] - zₜ = +h / 2 - (rcφ * nₜ[1] + rsφ * nₜ[2]) / nₜ[3] - pᵦ = Point(rcφ, rsφ, zᵦ) - pₜ = Point(rcφ, rsφ, zₜ) - - p = pᵦ + T(z) * (pₜ - pᵦ) - o + Q' * to(p) -end +(c::CylinderSurface)(φ, z) = Cylinder(bottom(c), top(c), radius(c))(1, φ, z) function hasintersectingplanes(c::CylinderSurface) x = c.bot ∩ c.top diff --git a/src/geometries/primitives/sphere.jl b/src/geometries/primitives/sphere.jl index e5d960204..f80d54298 100644 --- a/src/geometries/primitives/sphere.jl +++ b/src/geometries/primitives/sphere.jl @@ -73,27 +73,4 @@ radius(s::Sphere) = s.radius Base.isapprox(s₁::Sphere, s₂::Sphere; atol=atol(lentype(s₁)), kwargs...) = isapprox(s₁.center, s₂.center; atol, kwargs...) && isapprox(s₁.radius, s₂.radius; atol, kwargs...) -function (s::Sphere{𝔼{2}})(φ) - T = numtype(lentype(s)) - if (φ < 0 || φ > 1) - throw(DomainError(φ, "s(φ) is not defined for φ outside [0, 1].")) - end - c = s.center - r = s.radius - φ′ = T(φ) * 2 * T(π) * u"rad" - p = Point(convert(crs(s), Polar(r, φ′))) - p + to(c) -end - -function (s::Sphere{𝔼{3}})(θ, φ) - T = numtype(lentype(s)) - if (θ < 0 || θ > 1) || (φ < 0 || φ > 1) - throw(DomainError((θ, φ), "s(θ, φ) is not defined for θ, φ outside [0, 1]².")) - end - c = s.center - r = s.radius - θ′ = T(θ) * T(π) * u"rad" - φ′ = T(φ) * 2 * T(π) * u"rad" - p = Point(convert(crs(s), Spherical(r, θ′, φ′))) - p + to(c) -end +(s::Sphere)(uv...) = Ball(center(s), radius(s))(1, uv...) diff --git a/test/primitives.jl b/test/primitives.jl index 8b69c8961..6b674ed8f 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -594,6 +594,12 @@ end @test b(T(0), T(0)) ≈ cart(0, 0) @test b(T(1), T(0)) ≈ cart(2, 0) + # machine type is preserved in parameterization + b = Ball(cart(0, 0), T(2)) + @test Meshes.lentype(b(0, 0)) == ℳ + @test Meshes.lentype(b(0.0, 0.0)) == ℳ + @test Meshes.lentype(b(0.0f0, 0.0f0)) == ℳ + b = Ball(cart(7, 7), T(1.5)) ps = b.(1, rand(T, 100)) all(∈(b), ps) @@ -709,6 +715,12 @@ end @test s(T(0), T(0)) ≈ cart(0, 0, 2) @test s(T(0.5), T(0.5)) ≈ cart(-2, 0, 0) + # machine type is preserved in parameterization + s = Sphere(cart(0, 0), T(2)) + @test Meshes.lentype(s(0)) == ℳ + @test Meshes.lentype(s(0.0)) == ℳ + @test Meshes.lentype(s(0.0f0)) == ℳ + s = Sphere(cart(0, 0, 0), T(1)) @test sprint(show, s) == "Sphere(center: (x: 0.0 m, y: 0.0 m, z: 0.0 m), radius: 1.0 m)" if T === Float32 @@ -918,6 +930,12 @@ end @test cart(0, 0, 1.001) ∉ c @test cart(1, 1, 1) ∉ c + # machine type is preserved in parameterization + c = Cylinder(T(1)) + @test Meshes.lentype(c(0, 0, 0)) == ℳ + @test Meshes.lentype(c(0.0, 0.0, 0.0)) == ℳ + @test Meshes.lentype(c(0.0f0, 0.0f0, 0.0f0)) == ℳ + c = Cylinder(cart(0, 0, 0), cart(0, 0, 1), T(1)) @test sprint(show, c) == "Cylinder(bot: Plane(p: (x: 0.0 m, y: 0.0 m, z: 0.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), top: Plane(p: (x: 0.0 m, y: 0.0 m, z: 1.0 m), u: (1.0 m, -0.0 m, -0.0 m), v: (-0.0 m, 1.0 m, -0.0 m)), radius: 1.0 m)" @@ -974,6 +992,12 @@ end @test c(T(0), T(1)) ≈ cart(1, 0, 1) @test c(T(0.5), T(1)) ≈ cart(-1, 0, 1) + # machine type is preserved in parameterization + c = CylinderSurface(T(1)) + @test Meshes.lentype(c(0, 0)) == ℳ + @test Meshes.lentype(c(0.0, 0.0)) == ℳ + @test Meshes.lentype(c(0.0f0, 0.0f0)) == ℳ + c = CylinderSurface(1.0) @test Meshes.lentype(c) == Meshes.Met{Float64} c = CylinderSurface(1.0f0) From 3d2c523aed2e7b0b21ff7e9c677afa030af697a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 4 Oct 2024 15:05:39 -0300 Subject: [PATCH 375/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 666e5fa5a..c28751240 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.17" +version = "0.51.18" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 45f5bdbd870de6d06f74e63a31c2f5b69ba17df9 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:26:36 -0300 Subject: [PATCH 376/423] Use raw coordinates in Neighbor Search Methods (#1102) * Use raw coordinates in Neighbor Search Methods * Apply suggestions --- src/neighborsearch.jl | 9 +++++++++ src/neighborsearch/ball.jl | 11 +++++++---- src/neighborsearch/kball.jl | 11 +++++++---- src/neighborsearch/knearest.jl | 9 +++++---- test/neighborsearch.jl | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/neighborsearch.jl b/src/neighborsearch.jl index f3740dfa4..6927ff45f 100644 --- a/src/neighborsearch.jl +++ b/src/neighborsearch.jl @@ -87,3 +87,12 @@ end include("neighborsearch/ball.jl") include("neighborsearch/knearest.jl") include("neighborsearch/kball.jl") + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +# raw coordinates of point as SVector +# needed because NearestNeighbors.jl only accepts vectors +_rawcoords(p::Point) = _rawcoords(coords(p)) +_rawcoords(c::CRS) = SVector(CoordRefSystems.raw(c)) diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index 2565e5030..73516a974 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -18,7 +18,7 @@ end function BallSearch(domain::D, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [_rawcoords(centroid(domain, i)) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) BallSearch{D,B,typeof(tree)}(domain, ball, tree) end @@ -26,12 +26,15 @@ end BallSearch(geoms, ball) = BallSearch(GeometrySet(geoms), ball) function search(pₒ::Point, method::BallSearch; mask=nothing) + C = crs(method.domain) + u = unit(lentype(method.domain)) tree = method.tree - # adjust units of query point and radius - u = unit(lentype(method.domain)) + # adjust unit of query radius r = ustrip(u, radius(method.ball)) - x = ustrip.(u, to(pₒ)) + + # adjust CRS of query point + x = _rawcoords(convert(C, coords(pₒ))) inds = inrange(tree, x, r) diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 94ec1073c..18d32a588 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -20,7 +20,7 @@ end function KBallSearch(domain::D, k::Int, ball::B) where {D<:Domain,B<:MetricBall} m = metric(ball) - xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [_rawcoords(centroid(domain, i)) for i in 1:nelements(domain)] tree = m isa MinkowskiMetric ? KDTree(xs, m) : BallTree(xs, m) KBallSearch{D,B,typeof(tree)}(domain, k, ball, tree) end @@ -30,13 +30,16 @@ KBallSearch(geoms, k, ball) = KBallSearch(GeometrySet(geoms), k, ball) maxneighbors(method::KBallSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KBallSearch; mask=nothing) + C = crs(method.domain) + u = unit(lentype(method.domain)) tree = method.tree k = method.k - # adjust units of query point and radius - u = unit(lentype(method.domain)) + # adjust unit of query radius r = ustrip(u, radius(method.ball)) - x = ustrip.(u, to(pₒ)) + + # adjust CRS of query point + x = _rawcoords(convert(C, coords(pₒ))) inds, dists = knn(tree, x, k, true) diff --git a/src/neighborsearch/knearest.jl b/src/neighborsearch/knearest.jl index 8d4ef330a..64f607a35 100644 --- a/src/neighborsearch/knearest.jl +++ b/src/neighborsearch/knearest.jl @@ -18,7 +18,7 @@ struct KNearestSearch{D<:Domain,T} <: BoundedNeighborSearchMethod end function KNearestSearch(domain::D, k::Int; metric=Euclidean()) where {D<:Domain} - xs = [ustrip.(to(centroid(domain, i))) for i in 1:nelements(domain)] + xs = [_rawcoords(centroid(domain, i)) for i in 1:nelements(domain)] tree = metric isa MinkowskiMetric ? KDTree(xs, metric) : BallTree(xs, metric) KNearestSearch{D,typeof(tree)}(domain, k, tree) end @@ -28,12 +28,13 @@ KNearestSearch(geoms, k; metric=Euclidean()) = KNearestSearch(GeometrySet(geoms) maxneighbors(method::KNearestSearch) = method.k function searchdists!(neighbors, distances, pₒ::Point, method::KNearestSearch; mask=nothing) + C = crs(method.domain) + u = unit(lentype(method.domain)) tree = method.tree k = method.k - # adjust units of query point - u = unit(lentype(method.domain)) - x = ustrip.(u, to(pₒ)) + # adjust CRS of query point + x = _rawcoords(convert(C, coords(pₒ))) inds, dists = knn(tree, x, k, true) diff --git a/test/neighborsearch.jl b/test/neighborsearch.jl index 727d1ab89..006178d8e 100644 --- a/test/neighborsearch.jl +++ b/test/neighborsearch.jl @@ -37,6 +37,12 @@ # construct from vector of geometries s = BallSearch(randpoint2(100), MetricBall(T(1))) @test s isa BallSearch + + # latlon coodinates + 𝒟 = RegularGrid((10, 10), latlon(0, 0), T.((1.0, 1.0))) + s = BallSearch(𝒟, MetricBall(T(3e5), Haversine())) + n = search(latlon(0, 0), s) + @test Set(n) == Set([1, 2, 3, 11, 12, 21]) end @testitem "KNearestSearch" setup = [Setup] begin @@ -73,6 +79,12 @@ end # construct from vector of geometries s = KNearestSearch(randpoint2(100), 3) @test s isa KNearestSearch + + # latlon coodinates + 𝒟 = RegularGrid((10, 10), latlon(0, 0), T.((1.0, 1.0))) + s = KNearestSearch(𝒟, 3, metric=Haversine()) + n = search(latlon(0, 0), s) + @test Set(n) == Set([1, 2, 11]) end @testitem "KBallSearch" setup = [Setup] begin @@ -129,4 +141,10 @@ end # construct from vector of geometries s = KBallSearch(randpoint2(100), 10, MetricBall(T(1))) @test s isa KBallSearch + + # latlon coodinates + 𝒟 = RegularGrid((10, 10), latlon(0, 0), T.((1.0, 1.0))) + s = KBallSearch(𝒟, 10, MetricBall(T(3e5), Haversine())) + n = search(latlon(5, 5), s) + @test length(n) == 10 end From 83e5002c29582a00d4205242a088dfb8283295c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 8 Oct 2024 18:09:39 -0300 Subject: [PATCH 377/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c28751240..e373b16eb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.18" +version = "0.51.19" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 80eb8ab1c9e24d77721bbc21572c614209a22b07 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:23:47 -0300 Subject: [PATCH 378/423] Proj: Avoid constructing a new geometry or domain when the CRS is the same (#1104) --- src/transforms/proj.jl | 6 ++++++ test/transforms.jl | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index aca9b65b1..7891207ad 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -29,6 +29,12 @@ Proj(code::Type{<:ESRI}) = Proj{CoordRefSystems.get(code)}() parameters(::Proj{CRS}) where {CRS} = (; CRS) +# avoid constructing a new geometry or domain when the CRS is the same +function apply(t::Proj{CRS}, g::GeometryOrDomain) where {CRS} + g′ = crs(g) <: CRS ? g : applycoord(t, g) + g′, nothing +end + # convert the CRS and preserve the manifold applycoord(::Proj{CRS}, p::Point{<:🌐}) where {CRS<:Basic} = Point{🌐}(convert(CRS, coords(p))) diff --git a/test/transforms.jl b/test/transforms.jl index ae3ad4768..9e22349a3 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1294,6 +1294,36 @@ end g = Box(merc(0, 0), merc(1, 1)) r, c = TB.apply(f, g) @test manifold(r) === 🌐 + + # -------------- + # NO CONVERSION + # -------------- + + f = Proj(Cartesian) + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r === g + f = Proj(crs(cart(0, 0))) + r, c = TB.apply(f, g) + @test r === g + + f = Proj(LatLon) + g = Ring(latlon(0, 0), latlon(0, 1), latlon(1, 0)) + r, c = TB.apply(f, g) + @test r === g + f = Proj(crs(latlon(0, 0))) + r, c = TB.apply(f, g) + @test r === g + + f = Proj(Mercator) + p = merc.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r === d + f = Proj(crs(merc(0, 0))) + r, c = TB.apply(f, d) + @test r === d end @testitem "LengthUnit" setup = [Setup] begin From fc3febc546b9948f0855bf1a3b281cea31983fb8 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:45:53 +0200 Subject: [PATCH 379/423] Add `ParametrizedCurve` (#1093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add ParametrizedCurve * include file * add docstring * format * remove underscores in names * use minimum and maximum * fix * interval ab as optional argument * format * move include * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * restructure range * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * promote range * Update src/geometries/primitives/parametrizedcurve.jl * Update src/geometries/primitives/parametrizedcurve.jl * Update src/geometries/primitives/parametrizedcurve.jl Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> * add docs * Update src/geometries/primitives/parametrizedcurve.jl Co-authored-by: Júlio Hoffimann * remove newline * fix Float32 tests * add predicates test * add crs test * add sampling test * add discretization tests * format * use cart and merc consistently * Apply suggestions from code review Co-authored-by: Júlio Hoffimann * Update src/geometries/primitives/parametrizedcurve.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * func -> fun --------- Co-authored-by: Júlio Hoffimann Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> --- docs/src/geometries/primitives.md | 10 +++++ src/Meshes.jl | 1 + src/boundary.jl | 5 +++ src/discretization.jl | 2 + src/geometries/primitives.jl | 1 + .../primitives/parametrizedcurve.jl | 44 +++++++++++++++++++ src/predicates/isparametrized.jl | 2 + src/predicates/isperiodic.jl | 2 + test/crs.jl | 2 + test/discretization.jl | 12 +++++ test/predicates.jl | 1 + test/primitives.jl | 30 +++++++++++++ test/sampling.jl | 7 +++ 13 files changed, 119 insertions(+) create mode 100644 src/geometries/primitives/parametrizedcurve.jl diff --git a/docs/src/geometries/primitives.md b/docs/src/geometries/primitives.md index 0d3eb96b0..194948872 100644 --- a/docs/src/geometries/primitives.md +++ b/docs/src/geometries/primitives.md @@ -52,6 +52,16 @@ BezierCurve BezierCurve((0.,0.), (1.,0.), (1.,1.)) |> viz ``` +### ParametrizedCurve + +```@docs +ParametrizedCurve +``` + +```@example primitives +ParametrizedCurve(t -> Point(cos(t), sin(t), 0.2t), (0, 4π)) |> viz +``` + ### Plane ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index 544538d39..8eaa23953 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -157,6 +157,7 @@ export Ray, Line, BezierCurve, + ParametrizedCurve, Plane, Box, Ball, diff --git a/src/boundary.jl b/src/boundary.jl index dfbd3cee9..4ee981d8a 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -21,6 +21,11 @@ function boundary(b::BezierCurve) p₁ ≈ p₂ ? nothing : Multi([p₁, p₂]) end +function boundary(c::ParametrizedCurve) + p₁, p₂ = extrema(c) + p₁ ≈ p₂ ? nothing : Multi([p₁, p₂]) +end + boundary(::Plane) = nothing boundary(b::Box{𝔼{1}}) = Multi([minimum(b), maximum(b)]) diff --git a/src/discretization.jl b/src/discretization.jl index 48e98e717..590ff84e5 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -202,6 +202,8 @@ end simplexify(bezier::BezierCurve) = discretize(bezier, RegularDiscretization(50)) +simplexify(curve::ParametrizedCurve) = discretize(curve, RegularDiscretization(50)) + simplexify(sphere::Sphere{𝔼{2}}) = discretize(sphere, RegularDiscretization(50)) simplexify(circle::Circle) = discretize(circle, RegularDiscretization(50)) diff --git a/src/geometries/primitives.jl b/src/geometries/primitives.jl index 34ad3d82a..9b75338f4 100644 --- a/src/geometries/primitives.jl +++ b/src/geometries/primitives.jl @@ -16,6 +16,7 @@ include("primitives/point.jl") include("primitives/ray.jl") include("primitives/line.jl") include("primitives/bezier.jl") +include("primitives/parametrizedcurve.jl") include("primitives/plane.jl") include("primitives/box.jl") include("primitives/ball.jl") diff --git a/src/geometries/primitives/parametrizedcurve.jl b/src/geometries/primitives/parametrizedcurve.jl new file mode 100644 index 000000000..b7017e313 --- /dev/null +++ b/src/geometries/primitives/parametrizedcurve.jl @@ -0,0 +1,44 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + ParametrizedCurve(fun, range = (0.0, 1.0)) + +A parametrized curve is a curve defined by a function `fun` that maps a +(unitless) parameter `t` in the given `range` to a `Point` in space. + +## Examples + +```julia +ParametrizedCurve(t -> Point(cos(t), sin(t)), (0, 2π)) +``` +""" +struct ParametrizedCurve{M<:Manifold,C<:CRS,F<:Function,R<:Tuple} <: Primitive{M,C} + fun::F + range::R + ParametrizedCurve{M,C}(fun::F, range::R) where {M<:Manifold,C<:CRS,F<:Function,R<:Tuple} = new{M,C,F,R}(fun, range) +end + +function ParametrizedCurve(fun, range=(0.0, 1.0)) + a, b = promote(range...) + r = (a, b) + p = fun(a) + ParametrizedCurve{manifold(p),crs(p)}(fun, r) +end + +paramdim(::Type{<:ParametrizedCurve}) = 1 + +Base.minimum(curve::ParametrizedCurve) = curve.fun(first(curve.range)) + +Base.maximum(curve::ParametrizedCurve) = curve.fun(last(curve.range)) + +Base.extrema(curve::ParametrizedCurve) = minimum(curve), maximum(curve) + +function (curve::ParametrizedCurve)(t) + if t < 0 || t > 1 + throw(DomainError(t, "c(t) is not defined for t outside [0, 1].")) + end + a, b = curve.range + curve.fun(a + t * (b - a)) +end diff --git a/src/predicates/isparametrized.jl b/src/predicates/isparametrized.jl index 178b73d39..bbb04ba3b 100644 --- a/src/predicates/isparametrized.jl +++ b/src/predicates/isparametrized.jl @@ -25,6 +25,8 @@ isparametrized(::Type{<:Plane}) = true isparametrized(::Type{<:BezierCurve}) = true +isparametrized(::Type{<:ParametrizedCurve}) = true + isparametrized(::Type{<:Box{<:𝔼}}) = true isparametrized(::Type{<:Ball{<:𝔼}}) = true diff --git a/src/predicates/isperiodic.jl b/src/predicates/isperiodic.jl index 974a47e53..32c9327e7 100644 --- a/src/predicates/isperiodic.jl +++ b/src/predicates/isperiodic.jl @@ -18,6 +18,8 @@ isperiodic(::Type{<:Line}) = (false,) isperiodic(b::BezierCurve) = (first(controls(b)) == last(controls(b)),) +isperiodic(c::ParametrizedCurve) = (minimum(c) == maximum(c),) + isperiodic(::Type{<:Plane}) = (false, false) isperiodic(B::Type{<:Box}) = ntuple(i -> false, embeddim(B)) diff --git a/test/crs.jl b/test/crs.jl index 8b3445686..31b9bcd40 100644 --- a/test/crs.jl +++ b/test/crs.jl @@ -7,6 +7,8 @@ @test crs(g) <: Mercator{WGS84Latest} g = BezierCurve(merc(0, 0), merc(1, 1), merc(2, 0)) @test crs(g) <: Mercator{WGS84Latest} + g = ParametrizedCurve(t -> merc(cos(t), sin(t)), (T(0), T(2π))) + @test crs(g) <: Mercator{WGS84Latest} g = Box(merc(0, 0), merc(1, 1)) @test crs(g) <: Mercator{WGS84Latest} g = Ball(merc(0, 0), T(1)) diff --git a/test/discretization.jl b/test/discretization.jl index f25ea196e..17613141a 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -327,6 +327,13 @@ end @test eltype(mesh) <: Segment @test nvertices.(mesh) ⊆ [2] + curve = ParametrizedCurve(t -> cart(cos(t), sin(t)), (T(0), T(2π))) + mesh = discretize(curve, RegularDiscretization(10)) + @test nvertices(mesh) == 11 + @test nelements(mesh) == 10 + @test eltype(mesh) <: Segment + @test nvertices.(mesh) ⊆ [2] + box = Box(cart(0, 0), cart(2, 2)) mesh = discretize(box, RegularDiscretization(10)) @test mesh isa CartesianGrid @@ -570,6 +577,11 @@ end @test eltype(msh) <: Segment @test nvertices(msh) == nelements(msh) + 1 + curve = ParametrizedCurve(t -> cart(cos(t), sin(t)), (T(0), T(2π))) + msh = simplexify(curve) + @test eltype(msh) <: Segment + @test nvertices(msh) == nelements(msh) + 1 + box = Box(cart(0, 0), cart(1, 1)) ngon = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) poly = readpoly(T, joinpath(datadir, "taubin.line")) diff --git a/test/predicates.jl b/test/predicates.jl index ee4c948ce..6f8f8f738 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -123,6 +123,7 @@ end @test isparametrized(Disk) @test isparametrized(Circle) @test isparametrized(BezierCurve) + @test isparametrized(ParametrizedCurve) @test isparametrized(Cylinder) @test isparametrized(CylinderSurface) @test isparametrized(ConeSurface) diff --git a/test/primitives.jl b/test/primitives.jl index 6b674ed8f..bc3c36d58 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -1297,6 +1297,36 @@ end isapproxtest(f) end +@testitem "ParametrizedCurve" setup = [Setup] begin + fun(t) = Point(Polar(T(1), T(t))) + c = ParametrizedCurve(fun, (T(0), T(2π))) + @test embeddim(c) == 2 + @test paramdim(c) == 1 + @test crs(c) <: Polar{NoDatum} + @test Meshes.lentype(c) == ℳ + + equaltest(c) + + @test c(T(0)) == fun(T(0)) + @test c(T(1)) == fun(T(2π)) + @test c(T(0.5)) == fun(T(π)) + @test_throws DomainError(T(-0.1), "c(t) is not defined for t outside [0, 1].") c(T(-0.1)) + @test_throws DomainError(T(1.2), "c(t) is not defined for t outside [0, 1].") c(T(1.2)) + + @test boundary(c) === nothing + + c = ParametrizedCurve(t -> cart(cospi(t), sinpi(t)), (T(0), T(1))) + @test boundary(c) == Multi([cart(1, 0), cart(-1, 0)]) + @test perimeter(c) == zero(ℳ) + + # CRS propagation + foo(t) = merc(t, 2t) + c = ParametrizedCurve(foo, (T(0), T(1))) + @test crs(c(T(0))) === crs(c) + + @test sprint(show, c) == "ParametrizedCurve(fun: foo, range: (0.0, 1.0))" +end + @testitem "Torus" setup = [Setup] begin t = Torus(T.((1, 1, 1)), T.((1, 0, 0)), 2, 1) @test cart(1, 1, -1) ∈ t diff --git a/test/sampling.jl b/test/sampling.jl index 7a1b47e6b..39c79cf98 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -82,6 +82,13 @@ end @test p ≈ t end + c = ParametrizedCurve(t -> cart(cos(t), sin(t)), (T(0), T(2π))) + ps = sample(c, RegularSampling(4)) + ts = cart.([(1.0, 0.0), (-0.5, 0.8660254037844387), (-0.5, -0.8660254037844385), (1.0, 0.0)]) + for (p, t) in zip(ps, ts) + @test p ≈ t + end + s = Sphere(cart(0, 0), T(2)) ps = sample(s, RegularSampling(4)) ts = cart.([(2, 0), (0, 2), (-2, 0), (0, -2)]) From 3611cbb7401e80d3924883ee5c2377f2aa94ab4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 9 Oct 2024 14:47:01 -0300 Subject: [PATCH 380/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e373b16eb..76c41e192 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.19" +version = "0.51.20" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ee95f2b15e68f8e7e227a18d97f380e05f62a32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 9 Oct 2024 15:36:40 -0300 Subject: [PATCH 381/423] Use LTS in CI.yml --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8e45b53e8..37fb1374f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: version: - - '1.9' + - 'lts' - '1' os: - ubuntu-latest From 0a902ce371aa26e23e3d9b73d6bdad56a3032ffa Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:50:50 +0200 Subject: [PATCH 382/423] Add `Morphological` transform (#1100) * add Morphological transform * wrap result in Point * fix Point in docstring * use f: CRS->CRS * adjust docstring to how it should work * remove applycoord for Vec * Update implementation * Add to documentation * Add tests --------- Co-authored-by: Elias Carvalho --- docs/src/transforms.md | 14 ++++ src/Meshes.jl | 1 + src/transforms.jl | 1 + src/transforms/morphological.jl | 39 ++++++++++ test/transforms.jl | 123 ++++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 src/transforms/morphological.jl diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 45f690a7a..8bec4a279 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -146,6 +146,20 @@ triangle = Triangle((0, 0), (1, 0), (1, 1)) triangle |> Proj(Polar) ``` +## Morphological + +```@docs +Morphological +``` + +```@example transforms +# triangle with Cartesian coordinates +triangle = Triangle((0, 0), (1, 0), (1, 1)) + +# transform triangle coordinates +triangle |> Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) +``` + ## LengthUnit ```@docs diff --git a/src/Meshes.jl b/src/Meshes.jl index 8eaa23953..65f7c1fe4 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -537,6 +537,7 @@ export Stretch, StdCoords, Proj, + Morphological, LengthUnit, Shadow, Slice, diff --git a/src/transforms.jl b/src/transforms.jl index 895e84678..dfc64048a 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -94,6 +94,7 @@ include("transforms/affine.jl") include("transforms/stretch.jl") include("transforms/stdcoords.jl") include("transforms/proj.jl") +include("transforms/morphological.jl") include("transforms/lengthunit.jl") include("transforms/shadow.jl") include("transforms/slice.jl") diff --git a/src/transforms/morphological.jl b/src/transforms/morphological.jl new file mode 100644 index 000000000..3084a2c8e --- /dev/null +++ b/src/transforms/morphological.jl @@ -0,0 +1,39 @@ +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ + +""" + Morphological(fun) + +Morphological transform given by a function `fun` +that maps the coordinates of a geometry or a domain +to new coordinates (`coords -> newcoords`). + +# Examples + +```julia +ball = Ball((0, 0), 1) +ball |> Morphological(c -> Cartesian(c.x + c.y, c.y, c.x - c.y)) +``` +""" +struct Morphological{F<:Function} <: CoordinateTransform + fun::F +end + +parameters(t::Morphological) = (; fun=t.fun) + +applycoord(t::Morphological, p::Point) = Point(t.fun(coords(p))) + +applycoord(::Morphological, v::Vec) = v + +# -------------- +# SPECIAL CASES +# -------------- + +applycoord(t::Morphological, g::Geometry) = TransformedGeometry(g, t) + +applycoord(t::Morphological, g::RegularGrid) = TransformedGrid(g, t) + +applycoord(t::Morphological, g::RectilinearGrid) = TransformedGrid(g, t) + +applycoord(t::Morphological, g::StructuredGrid) = TransformedGrid(g, t) diff --git a/test/transforms.jl b/test/transforms.jl index 9e22349a3..d23c6b59e 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1326,6 +1326,129 @@ end @test r === d end +@testitem "Morphological" setup = [Setup] begin + f = Morphological(c -> c) + @test !isaffine(f) + @test !TB.isrevertible(f) + @test !TB.isinvertible(f) + @test TB.parameters(f) == (; fun=f.fun) + + # ---- + # VEC + # ---- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + v = vector(1, 0) + r, c = TB.apply(f, v) + @test r == v + + # ------ + # POINT + # ------ + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + g = cart(1, 1) + r, c = TB.apply(f, g) + @test r == cart(1, 1, 0) + + # -------- + # SEGMENT + # -------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + g = Segment(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Segment(cart(0, 0, 0), cart(1, 1, 0)) + + # ---- + # BOX + # ---- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + g = Box(cart(0, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + + # --------- + # TRIANGLE + # --------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r ≈ Triangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0)) + + # ---------- + # MULTIGEOM + # ---------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + g = Multi([t, t]) + r, c = TB.apply(f, g) + @test r ≈ Multi([f(t), f(t)]) + + # --------- + # POINTSET + # --------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + d = PointSet([cart(0, 0), cart(1, 0), cart(1, 1)]) + r, c = TB.apply(f, d) + @test r ≈ PointSet([cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0)]) + + # ------------ + # GEOMETRYSET + # ------------ + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + t = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + d = GeometrySet([t, t]) + r, c = TB.apply(f, d) + @test r ≈ GeometrySet([f(t), f(t)]) + d = [t, t] + r, c = TB.apply(f, d) + @test all(r .≈ [f(t), f(t)]) + + # -------------- + # CARTESIANGRID + # -------------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + d = CartesianGrid((10, 10), cart(1, 1), T.((1, 1))) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ---------------- + # RECTILINEARGRID + # ---------------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + d = convert(RectilinearGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # --------------- + # STRUCTUREDGRID + # --------------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + d = convert(StructuredGrid, cartgrid(10, 10)) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ----------- + # SIMPLEMESH + # ----------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + p = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + d = SimpleMesh(p, c) + r, c = TB.apply(f, d) + @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) +end + @testitem "LengthUnit" setup = [Setup] begin @test !isaffine(LengthUnit(u"km")) @test !TB.isrevertible(LengthUnit(u"cm")) From 8225381c9eace63b7b8e03ce2ca00de2808758b6 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:15:55 -0300 Subject: [PATCH 383/423] Refactor `Shadow` implementation (#1105) * Refactor 'Shadow' implementation * Apply suggestions --- src/transforms/shadow.jl | 87 ++++++++++------------------------------ 1 file changed, 21 insertions(+), 66 deletions(-) diff --git a/src/transforms/shadow.jl b/src/transforms/shadow.jl index dd3ea2f63..777cddaf1 100644 --- a/src/transforms/shadow.jl +++ b/src/transforms/shadow.jl @@ -29,47 +29,34 @@ Shadow(dims::Symbol) = Shadow(string(dims)) parameters(t::Shadow) = (; dims=t.dims) -apply(t::Shadow, v::Vec) = _shadow(v, _sort(t.dims)), nothing - -apply(t::Shadow, g::GeometryOrDomain) = _shadow(g, _sort(t.dims)), nothing +apply(t::Shadow, v::Vec) = v[_sort(t.dims)], nothing + +function apply(t::Shadow, g::GeometryOrDomain) + dims = _sort(t.dims) + m = Morphological() do coords + cart = convert(Cartesian, coords) + vals = CoordRefSystems.values(cart) + Cartesian{datum(coords)}(vals[dims]) + end + apply(m, g) +end # -------------- # SPECIAL CASES # -------------- -apply(::Shadow, ::Plane) = throw(ArgumentError("Shadow transform doesn't yet support planes")) - -apply(t::Shadow, e::Ellipsoid) = TransformedGeometry(e, t), nothing - -apply(t::Shadow, d::Disk) = TransformedGeometry(d, t), nothing - -apply(t::Shadow, c::Circle) = TransformedGeometry(c, t), nothing - -apply(t::Shadow, c::Cylinder) = TransformedGeometry(c, t), nothing - -apply(t::Shadow, c::CylinderSurface) = TransformedGeometry(c, t), nothing - -apply(t::Shadow, c::Cone) = TransformedGeometry(c, t), nothing - -apply(t::Shadow, c::ConeSurface) = TransformedGeometry(c, t), nothing - -apply(t::Shadow, f::Frustum) = TransformedGeometry(f, t), nothing - -apply(t::Shadow, f::FrustumSurface) = TransformedGeometry(f, t), nothing - -apply(t::Shadow, p::ParaboloidSurface) = TransformedGeometry(p, t), nothing - -apply(t::Shadow, tr::Torus) = TransformedGeometry(tr, t), nothing - -apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))), nothing - -apply(t::Shadow, g::CartesianGrid) = _shadow(g, _sort(t.dims)), nothing - -apply(t::Shadow, g::RegularGrid) = TransformedGrid(g, t), nothing +apply(t::Shadow, b::Box{<:𝔼}) = Box(t(minimum(b)), t(maximum(b))), nothing -apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing +apply(::Shadow, ::Plane) = throw(ArgumentError("Shadow transform doesn't yet support planes")) -apply(t::Shadow, g::StructuredGrid) = TransformedGrid(g, t), nothing +function apply(t::Shadow, g::CartesianGrid) + dims = _sort(t.dims) + sz = size(g)[dims] + or = t(minimum(g)) + sp = spacing(g)[dims] + of = offset(g)[dims] + CartesianGrid(sz, or, sp, of), nothing +end # ----------------- # HELPER FUNCTIONS @@ -88,35 +75,3 @@ function _index(d) end _sort(dims) = sort(SVector(dims)) - -_shadow(v::Vec, dims) = v[dims] - -function _shadow(p::Point, dims) - v = _shadow(to(p), dims) - c = Cartesian{datum(crs(p))}(v...) - Point(c) -end - -function _shadow(g::CartesianGrid, dims) - sz = size(g)[dims] - or = _shadow(minimum(g), dims) - sp = spacing(g)[dims] - of = offset(g)[dims] - CartesianGrid(sz, or, sp, of) -end - -# apply shadow transform recursively -@generated function _shadow(g::G, dims) where {G<:GeometryOrDomain} - ctor = constructor(G) - names = fieldnames(G) - exprs = (:(_shadow(g.$name, dims)) for name in names) - :($ctor($(exprs...))) -end - -# stop recursion at non-geometric types -_shadow(x, _) = x - -# special treatment for lists of geometries -_shadow(g::NTuple{<:Any,<:Geometry}, dims) = map(gᵢ -> _shadow(gᵢ, dims), g) -_shadow(g::AbstractVector{<:Geometry}, dims) = [_shadow(gᵢ, dims) for gᵢ in g] -_shadow(g::CircularVector{<:Geometry}, dims) = CircularVector([_shadow(gᵢ, dims) for gᵢ in g]) From 224065dbb58ce6d5cb25c1dd35fca65b407f99aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 11 Oct 2024 18:16:10 -0300 Subject: [PATCH 384/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 76c41e192..6a41fec1d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.20" +version = "0.51.21" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From c4a3d98ff081bb02cda6525e4a79c1b140e3a9e0 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Sat, 12 Oct 2024 13:59:13 +0200 Subject: [PATCH 385/423] Add `isparametrized` for `Cone` (#1106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add isparametrized for Cone * fix copy-paste error * Update test/predicates.jl --------- Co-authored-by: Júlio Hoffimann --- src/predicates/isparametrized.jl | 2 ++ test/predicates.jl | 1 + 2 files changed, 3 insertions(+) diff --git a/src/predicates/isparametrized.jl b/src/predicates/isparametrized.jl index bbb04ba3b..9a3816983 100644 --- a/src/predicates/isparametrized.jl +++ b/src/predicates/isparametrized.jl @@ -43,6 +43,8 @@ isparametrized(::Type{<:Cylinder}) = true isparametrized(::Type{<:CylinderSurface}) = true +isparametrized(::Type{<:Cone}) = true + isparametrized(::Type{<:ConeSurface}) = true isparametrized(::Type{<:FrustumSurface}) = true diff --git a/test/predicates.jl b/test/predicates.jl index 6f8f8f738..9e65c95ed 100644 --- a/test/predicates.jl +++ b/test/predicates.jl @@ -126,6 +126,7 @@ end @test isparametrized(ParametrizedCurve) @test isparametrized(Cylinder) @test isparametrized(CylinderSurface) + @test isparametrized(Cone) @test isparametrized(ConeSurface) @test isparametrized(ParaboloidSurface) @test isparametrized(Torus) From 47db1a78b37c4338517c379e531e141697a210a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 12 Oct 2024 08:59:35 -0300 Subject: [PATCH 386/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6a41fec1d..8cfad907d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.21" +version = "0.51.22" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From d803a4fcece809f5733df30b9dc3bc449cd69612 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:31:06 -0300 Subject: [PATCH 387/423] Use `MaxLengthDiscretization` to visualize geometries with globe manifold (#1109) * Use 'MaxLengthDiscretization' to visualize geometries with globe manifold * Remove unused code --- ext/geometryset.jl | 36 +++--------------------------------- ext/mesh.jl | 2 +- ext/utils.jl | 12 ++++++++++++ 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/ext/geometryset.jl b/ext/geometryset.jl index aa21804bf..ff7933373 100644 --- a/ext/geometryset.jl +++ b/ext/geometryset.jl @@ -26,25 +26,10 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{0}, ::Val, geoms::ObservableVector Makie.scatter!(plot, coords, color=colorant, marker=pointmarker, markersize=pointsize, overdraw=true) end -function vizgset!(plot, ::Type{<:🌐}, ::Val{1}, ::Val, geoms, colorant) - showpoints = plot[:showpoints] - - meshes = Makie.@lift begin - T = numtype(Meshes.lentype(first($geoms))) - method = MaxLengthDiscretization(T(1000) * u"km") - [discretize(g, method) for g in $geoms] - end - vizmany!(plot, meshes, colorant) - - if showpoints[] - vizfacets!(plot, geoms) - end -end - function vizgset!(plot, ::Type{<:𝔼}, ::Val{1}, ::Val, geoms, colorant) showpoints = plot[:showpoints] - meshes = Makie.@lift discretize.($geoms) + meshes = Makie.@lift _discretize.($geoms) vizmany!(plot, meshes, colorant) if showpoints[] @@ -75,7 +60,7 @@ end function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val, geoms, colorant) showsegments = plot[:showsegments] - meshes = Makie.@lift discretize.($geoms) + meshes = Makie.@lift _discretize.($geoms) vizmany!(plot, meshes, colorant) if showsegments[] @@ -102,23 +87,8 @@ function vizgset!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val{2}, geoms::ObservableVec end end -function vizgset!(plot, ::Type{<:🌐}, ::Val{2}, ::Val, geoms::ObservableVector{<:Box}, colorant) - showsegments = plot[:showsegments] - - meshes = Makie.@lift begin - T = numtype(Meshes.lentype(first($geoms))) - method = MaxLengthDiscretization(T(100) * u"km") - [discretize(g, method) for g in $geoms] - end - vizmany!(plot, meshes, colorant) - - if showsegments[] - vizfacets!(plot, geoms) - end -end - function vizgset!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val, geoms, colorant) - meshes = Makie.@lift discretize.(boundary.($geoms)) + meshes = Makie.@lift _discretize.(boundary.($geoms)) vizmany!(plot, meshes, colorant) end diff --git a/ext/mesh.jl b/ext/mesh.jl index dd4a208e6..846999968 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -156,7 +156,7 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{3}, ::Val) meshes = Makie.@lift let geoms = elements($mesh) bounds = boundary.(geoms) - discretize.(bounds) + _discretize.(bounds) end vizmany!(plot, meshes, color) end diff --git a/ext/utils.jl b/ext/utils.jl index ff1236807..33482aeca 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -46,3 +46,15 @@ end asmakie(p::Point) = Makie.Point{embeddim(p),numtype(lentype(p))}(ustrip.(Tuple(to(p)))) asmakie(v::Vec) = Makie.Vec{length(v),numtype(eltype(v))}(ustrip.(Tuple(v))) + +_discretize(geom) = discretize(geom) + +function _discretize(box::Box{🌐}) + T = numtype(Meshes.lentype(box)) + discretize(box, MaxLengthDiscretization(T(100) * u"km")) +end + +function _discretize(geom::Geometry{🌐}) + T = numtype(Meshes.lentype(geom)) + discretize(geom, MaxLengthDiscretization(T(1000) * u"km")) +end From d8b6054e552be9eeb3a042e4b3c2d278aa670aba Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:23:42 -0300 Subject: [PATCH 388/423] Rename `ManualDiscretization` to `ManualSimplexification` and add more methods (#1110) * Rename 'ManualDiscretization' to 'ManualSimplexification' and add more methods * Update tests * Update tests --- docs/src/algorithms/discretization.md | 6 ++--- src/Meshes.jl | 2 +- src/discretization.jl | 8 ++----- src/discretization/manual.jl | 26 ++++++++++++++++------ test/discretization.jl | 32 ++++++++++++++++++++++----- 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/docs/src/algorithms/discretization.md b/docs/src/algorithms/discretization.md index db2ddebb3..ffacec181 100644 --- a/docs/src/algorithms/discretization.md +++ b/docs/src/algorithms/discretization.md @@ -157,16 +157,16 @@ mesh = discretize(sphere, RegularDiscretization(10,10)) viz(mesh, showsegments = true) ``` -## ManualDiscretization +## ManualSimplexification ```@docs -ManualDiscretization +ManualSimplexification ``` ```@example discretization box = Box((0., 0., 0.), (1., 1., 1.)) -mesh = discretize(box, ManualDiscretization()) +mesh = discretize(box, ManualSimplexification()) viz(mesh, colors = 1:nelements(mesh)) ``` diff --git a/src/Meshes.jl b/src/Meshes.jl index 65f7c1fe4..02514fe24 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -506,7 +506,7 @@ export DehnTriangulation, HeldTriangulation, DelaunayTriangulation, - ManualDiscretization, + ManualSimplexification, RegularDiscretization, MaxLengthDiscretization, discretize, diff --git a/src/discretization.jl b/src/discretization.jl index 590ff84e5..f4a7ed77d 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -184,11 +184,7 @@ function simplexify end simplexify(geometry) = simplexify(discretize(geometry)) -simplexify(box::Box{𝔼{1}}) = SimpleMesh(collect(extrema(box)), GridTopology(1)) - -simplexify(box::Box{𝔼{2}}) = discretize(box, FanTriangulation()) - -simplexify(box::Box{𝔼{3}}) = discretize(box, ManualDiscretization()) +simplexify(box::Box) = discretize(box, ManualSimplexification()) function simplexify(chain::Chain) np = nvertices(chain) + isclosed(chain) @@ -210,7 +206,7 @@ simplexify(circle::Circle) = discretize(circle, RegularDiscretization(50)) simplexify(poly::Polygon) = discretize(poly, nvertices(poly) > 5000 ? DelaunayTriangulation() : DehnTriangulation()) -simplexify(poly::Polyhedron) = discretize(poly, ManualDiscretization()) +simplexify(poly::Polyhedron) = discretize(poly, ManualSimplexification()) simplexify(multi::Multi) = mapreduce(simplexify, merge, parent(multi)) diff --git a/src/discretization/manual.jl b/src/discretization/manual.jl index 2cd86eeab..7429791f1 100644 --- a/src/discretization/manual.jl +++ b/src/discretization/manual.jl @@ -3,28 +3,40 @@ # ------------------------------------------------------------------ """ - ManualDiscretization() + ManualSimplexification() -Discretize geometries manually using indices of vertices. +Simplexify geometries manually using indices of vertices. """ -struct ManualDiscretization <: DiscretizationMethod end +struct ManualSimplexification <: DiscretizationMethod end -function discretize(box::Box, ::ManualDiscretization) +discretize(box::Box{𝔼{1}}, ::ManualSimplexification) = SimpleMesh(collect(extrema(box)), GridTopology(1)) + +function discretize(box::Box{𝔼{2}}, ::ManualSimplexification) + indices = [(1, 2, 3), (1, 3, 4)] + SimpleMesh(pointify(box), connect.(indices, Triangle)) +end + +function discretize(box::Box{𝔼{3}}, ::ManualSimplexification) indices = [(1, 5, 6, 8), (1, 3, 4, 8), (1, 3, 6, 8), (1, 2, 3, 6), (3, 6, 7, 8)] SimpleMesh(pointify(box), connect.(indices, Tetrahedron)) end -function discretize(hexa::Hexahedron, ::ManualDiscretization) +function discretize(box::Box{🌐}, ::ManualSimplexification) + indices = [(1, 2, 3), (1, 3, 4)] + SimpleMesh(pointify(box), connect.(indices, Triangle)) +end + +function discretize(hexa::Hexahedron, ::ManualSimplexification) indices = [(1, 5, 6, 8), (1, 3, 4, 8), (1, 3, 6, 8), (1, 2, 3, 6), (3, 6, 7, 8)] SimpleMesh(pointify(hexa), connect.(indices, Tetrahedron)) end -function discretize(pyramid::Pyramid, ::ManualDiscretization) +function discretize(pyramid::Pyramid, ::ManualSimplexification) indices = [(1, 2, 4, 5), (3, 4, 2, 5)] SimpleMesh(pointify(pyramid), connect.(indices, Tetrahedron)) end -function discretize(wedge::Wedge, ::ManualDiscretization) +function discretize(wedge::Wedge, ::ManualSimplexification) indices = [(1, 2, 3, 4), (4, 5, 6, 2), (4, 5, 6, 3)] SimpleMesh(pointify(wedge), connect.(indices, Tetrahedron)) end diff --git a/test/discretization.jl b/test/discretization.jl index 17613141a..099d7fbbc 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -309,14 +309,36 @@ end @test mesh[1] == Triangle(cart(0, 0), cart(0, 0), cart(0, 0)) end -@testitem "ManualDiscretization" setup = [Setup] begin +@testitem "ManualSimplexification" setup = [Setup] begin + box = Box(cart(0), cart(1)) + mesh = discretize(box, ManualSimplexification()) + @test nvertices(mesh) == 2 + @test nelements(mesh) == 1 + @test eltype(mesh) <: Segment + + box = Box(cart(0, 0), cart(1, 1)) + mesh = discretize(box, ManualSimplexification()) + @test nvertices(mesh) == 4 + @test nelements(mesh) == 2 + @test eltype(mesh) <: Triangle + + box = Box(cart(0, 0, 0), cart(1, 1, 1)) + mesh = discretize(box, ManualSimplexification()) + @test nvertices(mesh) == 8 + @test nelements(mesh) == 5 + @test eltype(mesh) <: Tetrahedron + + box = Box(latlon(0, 0), latlon(45, 45)) + mesh = discretize(box, ManualSimplexification()) + @test nvertices(mesh) == 4 + @test nelements(mesh) == 2 + @test eltype(mesh) <: Triangle + box = Box(cart(0, 0, 0), cart(1, 1, 1)) hexa = Hexahedron(pointify(box)...) - bmesh = discretize(box, ManualDiscretization()) - hmesh = discretize(hexa, ManualDiscretization()) + bmesh = discretize(box, ManualSimplexification()) + hmesh = discretize(hexa, ManualSimplexification()) @test bmesh == hmesh - @test nvertices(bmesh) == 8 - @test nelements(bmesh) == 5 end @testitem "RegularDiscretization" setup = [Setup] begin From b4d6a38f73b97bc5692ad180d65ba8915618cd3a Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:19:50 -0300 Subject: [PATCH 389/423] Fix `Proj` methods & Add `MaxLengthDiscretization` method for `TransformedGeometry` (#1111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix 'Proj' methods & Add 'MaxLengthDiscretization' method for 'TransformedGeometry' * Fix ambiguities * Fix typo * Fix code * Update tests * Fix more tests * Apply suggestions from code review Co-authored-by: Júlio Hoffimann --------- Co-authored-by: Júlio Hoffimann --- src/discretization/maxlength.jl | 3 +++ src/transforms/morphological.jl | 3 +++ src/transforms/proj.jl | 8 ++++++-- test/discretization.jl | 8 ++++++++ test/sets.jl | 10 ++++------ test/transforms.jl | 26 ++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/discretization/maxlength.jl b/src/discretization/maxlength.jl index 9490b2106..99bb8ba5b 100644 --- a/src/discretization/maxlength.jl +++ b/src/discretization/maxlength.jl @@ -29,6 +29,9 @@ discretize(chain::Chain, method::MaxLengthDiscretization) = discretize(multi::Multi, method::MaxLengthDiscretization) = _iterativerefinement(multi, method) +discretize(geometry::TransformedGeometry, method::MaxLengthDiscretization) = + transform(geometry)(discretize(parent(geometry), method)) + discretize(geometry::Geometry, method::MaxLengthDiscretization) = _iterativerefinement(geometry, method) # ----------------- diff --git a/src/transforms/morphological.jl b/src/transforms/morphological.jl index 3084a2c8e..0868a780a 100644 --- a/src/transforms/morphological.jl +++ b/src/transforms/morphological.jl @@ -32,6 +32,9 @@ applycoord(::Morphological, v::Vec) = v applycoord(t::Morphological, g::Geometry) = TransformedGeometry(g, t) +# method to fix ambiguities +applycoord(t::Morphological, g::TransformedGeometry) = TransformedGeometry(g, t) + applycoord(t::Morphological, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Morphological, g::RectilinearGrid) = TransformedGrid(g, t) diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 7891207ad..88b5d35e0 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -51,9 +51,13 @@ applycoord(::Proj, v::Vec) = v # SPECIAL CASES # -------------- -applycoord(t::Proj{<:Projected}, g::Primitive{<:🌐}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Projected}, g::Geometry{<:🌐}) = TransformedGeometry(g, t) -applycoord(t::Proj{<:Geographic}, g::Primitive{<:𝔼}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Geographic}, g::Geometry{<:𝔼}) = TransformedGeometry(g, t) + +# methods to fix ambiguities +applycoord(t::Proj{<:Projected}, g::TransformedGeometry{<:🌐}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Geographic}, g::TransformedGeometry{<:𝔼}) = TransformedGeometry(g, t) applycoord(t::Proj, g::RegularGrid) = TransformedGrid(g, t) diff --git a/test/discretization.jl b/test/discretization.jl index 099d7fbbc..9e2b07a69 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -519,6 +519,14 @@ end @test nelements(mesh) == 256 @test eltype(mesh) <: Triangle @test nvertices.(mesh) ⊆ [3] + + box = Box(latlon(0, 0), latlon(45, 45)) + tbox = TransformedGeometry(box, Proj(Mercator)) + mesh = discretize(tbox, MaxLengthDiscretization(T(1e5))) + @test nvertices(mesh) == 52 * 52 + @test nelements(mesh) == 51 * 51 + @test eltype(mesh) <: Quadrangle + @test nvertices.(mesh) ⊆ [4] end @testitem "Discretize" setup = [Setup] begin diff --git a/test/sets.jl b/test/sets.jl index d253653ea..2a12fbb4c 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -30,24 +30,22 @@ q = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) |> Proj(WebMercator) geoms = [s, t, q] gset = GeometrySet(geoms) - @test eltype(gset) <: Polytope @test crs(gset) <: LatLon gset = GeometrySet(g for g in geoms) - @test eltype(gset) <: Polytope @test crs(gset) <: LatLon geoms = [t, s, q] gset = GeometrySet(geoms) - @test eltype(gset) <: Polytope + @test eltype(gset) <: TransformedGeometry @test crs(gset) <: PlateCarree gset = GeometrySet(g for g in geoms) - @test eltype(gset) <: Polytope + @test eltype(gset) <: TransformedGeometry @test crs(gset) <: PlateCarree geoms = [q, s, t] gset = GeometrySet(geoms) - @test eltype(gset) <: Polytope + @test eltype(gset) <: TransformedGeometry @test crs(gset) <: WebMercator gset = GeometrySet(g for g in geoms) - @test eltype(gset) <: Polytope + @test eltype(gset) <: TransformedGeometry @test crs(gset) <: WebMercator # conversion diff --git a/test/transforms.jl b/test/transforms.jl index d23c6b59e..a6f268069 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1221,6 +1221,22 @@ end r, c = TB.apply(f, g) @test r ≈ Cylinder(Point(Cylindrical(T(0), T(0), T(0))), Point(Cylindrical(T(√2), T(π / 4), T(1)))) + # -------------------- + # TRANSFORMEDGEOMETRY + # -------------------- + + f = Proj(Mercator) + b = Box(latlon(0, 0), latlon(45, 45)) + g = TransformedGeometry(b, Identity()) + r, c = TB.apply(f, g) + @test r ≈ Box(f(minimum(b)), f(maximum(b))) + + f = Proj(LatLon) + b = Box(merc(0, 0), merc(1, 1)) + g = TransformedGeometry(b, Identity()) + r, c = TB.apply(f, g) + @test r ≈ Box(f(minimum(b)), f(maximum(b))) + # --------- # POINTSET # --------- @@ -1388,6 +1404,16 @@ end r, c = TB.apply(f, g) @test r ≈ Multi([f(t), f(t)]) + # -------------------- + # TRANSFORMEDGEOMETRY + # -------------------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x))) + b = Box(cart(0, 0), cart(1, 1)) + g = TransformedGeometry(b, Identity()) + r, c = TB.apply(f, g) + @test r ≈ Quadrangle(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0)) + # --------- # POINTSET # --------- From 01fd51ed06de1a58898dcf799b0f25f46c7c734a Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:01:22 -0300 Subject: [PATCH 390/423] Update the default discretization method of `TransformedGeometry` (#1113) * Update the default discretization method of 'TransformedGeometry' * Update include order * Add tests * Apply suggestions * Apply suggestions * Update implementation * Apply suggestions --- src/discretization.jl | 14 +++++++++++--- src/geometries/transformedgeom.jl | 8 ++++++++ test/discretization.jl | 7 +++++++ test/transformedgeoms.jl | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index f4a7ed77d..f9877848a 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -68,7 +68,15 @@ discretize(parsurf::ParaboloidSurface) = discretize(parsurf, RegularDiscretizati discretize(multi::Multi) = mapreduce(discretize, merge, parent(multi)) -discretize(tgeom::TransformedGeometry) = transform(tgeom)(discretize(parent(tgeom))) +function discretize(geometry::TransformedGeometry) + T = numtype(lentype(geometry)) + mesh = if hasdistortedboundary(geometry) + discretize(parent(geometry), MaxLengthDiscretization(T(1000) * u"km")) + else + discretize(parent(geometry)) + end + transform(geometry)(mesh) +end discretize(mesh::Mesh) = mesh @@ -79,8 +87,8 @@ discretize(mesh::Mesh) = mesh discretize(multi::Multi, method::DiscretizationMethod) = mapreduce(geom -> discretize(geom, method), merge, parent(multi)) -discretize(tgeom::TransformedGeometry, method::DiscretizationMethod) = - transform(tgeom)(discretize(parent(tgeom), method)) +discretize(geometry::TransformedGeometry, method::DiscretizationMethod) = + transform(geometry)(discretize(parent(geometry), method)) # ----------------- # BOUNDARY METHODS diff --git a/src/geometries/transformedgeom.jl b/src/geometries/transformedgeom.jl index b3d460c05..2a0dafa63 100644 --- a/src/geometries/transformedgeom.jl +++ b/src/geometries/transformedgeom.jl @@ -39,6 +39,14 @@ Base.parent(g::TransformedGeometry) = g.geometry transform(g::TransformedGeometry) = g.transform +hasdistortedboundary(g::TransformedGeometry) = _hasdistortion(manifold(g), manifold(parent(g))) + +_hasdistortion(::Type, ::Type) = false + +_hasdistortion(::Type{<:𝔼}, ::Type{<:🌐}) = true + +_hasdistortion(::Type{<:🌐}, ::Type{<:𝔼}) = true + # --------- # GEOMETRY # --------- diff --git a/test/discretization.jl b/test/discretization.jl index 9e2b07a69..acd707cd2 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -554,6 +554,13 @@ end @test !(eltype(mesh) <: Quadrangle) @test nelements(mesh) == 200 + box = Box(latlon(0, 0), latlon(45, 45)) + tbox = TransformedGeometry(box, Proj(Mercator)) + mesh = discretize(tbox) + @test nvertices(mesh) == 49 + @test nelements(mesh) == 36 + @test eltype(mesh) <: Quadrangle + grid = CartesianGrid(10) @test discretize(grid) == grid diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl index 62ad809ff..c8cbfeb39 100644 --- a/test/transformedgeoms.jl +++ b/test/transformedgeoms.jl @@ -53,6 +53,24 @@ equaltest(tp) isapproxtest(tp) + # has distorted boundary + b = Box(cart(0, 0), cart(1, 1)) + t = Translate(T(1), T(2)) + tb = TransformedGeometry(b, t) + @test !Meshes.hasdistortedboundary(tb) + b = Box(latlon(0, 0), latlon(1, 1)) + t = Proj(Mercator) + tb = TransformedGeometry(b, t) + @test Meshes.hasdistortedboundary(tb) + b = Box(merc(0, 0), merc(1, 1)) + t = Proj(LatLon) + tb = TransformedGeometry(b, t) + @test Meshes.hasdistortedboundary(tb) + b = Box(latlon(0, 0), latlon(1, 1)) + t = Morphological(c -> Cartesian(ustrip(c.lon), ustrip(c.lat))) + tb = TransformedGeometry(b, t) + @test Meshes.hasdistortedboundary(tb) + b = Box(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) tb = TransformedGeometry(b, t) From c47cbc071992f2d972f09307d8c52353e8da6abd Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:01:12 -0300 Subject: [PATCH 391/423] Update `Affine` and `Scale` methods (#1115) * Update 'Affine' and 'Scale' implementations * Add tests * Update code * Update tests * Update tests --- src/transforms/affine.jl | 18 ++++++ src/transforms/scale.jl | 4 ++ test/transforms.jl | 117 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/src/transforms/affine.jl b/src/transforms/affine.jl index 4a60fa3d5..bda86e237 100644 --- a/src/transforms/affine.jl +++ b/src/transforms/affine.jl @@ -63,6 +63,24 @@ applycoord(t::Affine, v::Vec) = t.A * v applycoord(t::Affine, b::Box) = TransformedGeometry(b, t) +applycoord(t::Affine, b::Ball) = TransformedGeometry(b, t) + +applycoord(t::Affine, s::Sphere) = TransformedGeometry(s, t) + +applycoord(t::Affine, e::Ellipsoid) = TransformedGeometry(e, t) + +applycoord(t::Affine, d::Disk) = TransformedGeometry(d, t) + +applycoord(t::Affine, c::Circle) = TransformedGeometry(c, t) + +applycoord(t::Affine, c::Cylinder) = TransformedGeometry(c, t) + +applycoord(t::Affine, c::CylinderSurface) = TransformedGeometry(c, t) + +applycoord(t::Affine, p::ParaboloidSurface) = TransformedGeometry(p, t) + +applycoord(t::Affine, tr::Torus) = TransformedGeometry(tr, t) + applycoord(t::Affine, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Affine, g::RectilinearGrid) = TransformedGrid(g, t) diff --git a/src/transforms/scale.jl b/src/transforms/scale.jl index 2eeb09d3a..b5fc489d8 100644 --- a/src/transforms/scale.jl +++ b/src/transforms/scale.jl @@ -48,6 +48,8 @@ applycoord(t::Scale, v::Vec) = t.factors .* v applycoord(t::Scale, b::Ball) = TransformedGeometry(b, t) +applycoord(t::Scale{1}, b::Ball) = Ball(applycoord(t, center(b)), t.factors[1] * radius(b)) + applycoord(t::Scale, s::Sphere) = TransformedGeometry(s, t) applycoord(t::Scale{1}, s::Sphere) = Sphere(applycoord(t, center(s)), t.factors[1] * radius(s)) @@ -56,6 +58,8 @@ applycoord(t::Scale{3}, s::Sphere{𝔼{3}}) = Ellipsoid(t.factors .* radius(s), applycoord(t::Scale, e::Ellipsoid) = TransformedGeometry(e, t) +applycoord(t::Scale{1}, e::Ellipsoid) = Ellipsoid(t.factors[1] .* radii(e), applycoord(t, center(e)), rotation(e)) + applycoord(t::Scale, d::Disk) = TransformedGeometry(d, t) applycoord(t::Scale, c::Circle) = TransformedGeometry(c, t) diff --git a/test/transforms.jl b/test/transforms.jl index a6f268069..aaf72b5d7 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -505,6 +505,101 @@ end h = TB.revert(f, r, c) @test h ≈ convert(Hexahedron, g) + # ----- + # BALL + # ----- + + f = Affine(Diagonal(T[1, 2]), T[1, 1]) + g = Ball(cart(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------- + # SPHERE + # ------- + + f = Affine(Diagonal(T[1, 2]), T[1, 1]) + g = Sphere(cart(1, 2), T(3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ---------- + # ELLIPSOID + # ---------- + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = Ellipsoid(T.((4, 5, 6)), cart(1, 2, 3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ----- + # DISK + # ----- + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = Disk(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------- + # CIRCLE + # ------- + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = Circle(Plane(cart(0, 0, 0), vector(0, 0, 1)), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ---------------- + # CYLINDERSURFACE + # ---------------- + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = CylinderSurface(T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------------------ + # PARABOLOIDSURFACE + # ------------------ + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = ParaboloidSurface(cart(0, 0, 0), T(1), T(2)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test discretize(TB.revert(f, r, c)) ≈ m + + # ------ + # TORUS + # ------ + + f = Affine(Diagonal(T[1, 2, 3]), T[1, 1, 1]) + g = Torus(cart(1, 1, 1), vector(1, 0, 0), T(2), T(1)) + m = discretize(g) + r, c = TB.apply(f, g) + @test discretize(r) ≈ f(m) + @test centroid(r) ≈ f(centroid(g)) + @test discretize(TB.revert(f, r, c)) ≈ m + # --------- # TRIANGLE # --------- @@ -708,6 +803,20 @@ end @test centroid(r) ≈ f(centroid(g)) @test discretize(TB.revert(f, r, c)) ≈ m + f = Scale(T(2)) + g = Ball(cart(1, 2), T(3)) + r, c = TB.apply(f, g) + @test r isa Ball + @test r ≈ Ball(cart(2, 4), T(6)) + @test TB.revert(f, r, c) ≈ g + + f = Scale(T(2)) + g = Ball(cart(1, 2, 3), T(4)) + r, c = TB.apply(f, g) + @test r isa Ball + @test r ≈ Ball(cart(2, 4, 6), T(8)) + @test TB.revert(f, r, c) ≈ g + # ------- # SPHERE # ------- @@ -753,6 +862,14 @@ end @test centroid(r) ≈ f(centroid(g)) @test discretize(TB.revert(f, r, c)) ≈ m + f = Scale(T(2)) + g = Ellipsoid(T.((4, 5, 6)), cart(1, 2, 3)) + m = discretize(g) + r, c = TB.apply(f, g) + @test r isa Ellipsoid + @test r ≈ Ellipsoid(T.((8, 10, 12)), cart(2, 4, 6)) + @test TB.revert(f, r, c) ≈ g + # ----- # DISK # ----- From 9bd78f2633ff07dea37b9085e5411446cc17e677 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:01:26 -0300 Subject: [PATCH 392/423] Update `pointify` and `boundary` of `TransformedGeometry` (#1114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update 'pointify' and 'boundary' of 'TransformedGeometry' * Update src/boundary.jl Co-authored-by: Júlio Hoffimann * Fix tests --------- Co-authored-by: Júlio Hoffimann --- src/boundary.jl | 6 +++++- src/pointification.jl | 3 ++- test/pointification.jl | 12 ++++++++++++ test/transformedgeoms.jl | 10 ++++++++++ test/transforms.jl | 4 ++-- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/boundary.jl b/src/boundary.jl index 4ee981d8a..a4b85c262 100644 --- a/src/boundary.jl +++ b/src/boundary.jl @@ -129,4 +129,8 @@ function boundary(m::Multi) isempty(valid) ? nothing : reduce(merge, valid) end -boundary(g::TransformedGeometry) = transform(g)(boundary(parent(g))) +function boundary(g::TransformedGeometry) + b = boundary(parent(g)) + t = transform(g) + hasdistortedboundary(g) ? TransformedGeometry(b, t) : t(b) +end diff --git a/src/pointification.jl b/src/pointification.jl index 464dd875c..c45f82164 100644 --- a/src/pointification.jl +++ b/src/pointification.jl @@ -20,7 +20,8 @@ pointify(p::Polytope) = collect(vertices(p)) pointify(m::Multi) = pointify(parent(m)) -pointify(g::TransformedGeometry) = map(transform(g), pointify(parent(g))) +pointify(g::TransformedGeometry) = + hasdistortedboundary(g) ? pointify(discretize(g)) : map(transform(g), pointify(parent(g))) pointify(geoms) = mapreduce(pointify, vcat, geoms) diff --git a/test/pointification.jl b/test/pointification.jl index 9dfb09fe8..54db36c35 100644 --- a/test/pointification.jl +++ b/test/pointification.jl @@ -31,6 +31,18 @@ points = pointify(multi) @test points == [pointify(tri); pointify(quad)] + box = Box(cart(0, 0), cart(1, 1)) + trans = Translate(T(1), T(2)) + tbox = TransformedGeometry(box, trans) + points = pointify(tbox) + @test points == trans.(pointify(box)) + + box = Box(latlon(0, 0), latlon(45, 45)) + trans = Proj(Mercator) + tbox = TransformedGeometry(box, trans) + points = pointify(tbox) + @test points == pointify(discretize(tbox)) + tri = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) quad = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) gset = GeometrySet([tri, quad]) diff --git a/test/transformedgeoms.jl b/test/transformedgeoms.jl index c8cbfeb39..ad1fd027f 100644 --- a/test/transformedgeoms.jl +++ b/test/transformedgeoms.jl @@ -71,6 +71,16 @@ tb = TransformedGeometry(b, t) @test Meshes.hasdistortedboundary(tb) + # boundary + b = Box(cart(0, 0), cart(1, 1)) + t = Translate(T(1), T(2)) + tb = TransformedGeometry(b, t) + @test boundary(tb) == t(boundary(b)) + b = Box(latlon(0, 0), latlon(1, 1)) + t = Proj(Mercator) + tb = TransformedGeometry(b, t) + @test boundary(tb) == TransformedGeometry(boundary(b), t) + b = Box(cart(0, 0), cart(1, 1)) t = Translate(T(1), T(2)) tb = TransformedGeometry(b, t) diff --git a/test/transforms.jl b/test/transforms.jl index aaf72b5d7..c91c1a024 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1346,13 +1346,13 @@ end b = Box(latlon(0, 0), latlon(45, 45)) g = TransformedGeometry(b, Identity()) r, c = TB.apply(f, g) - @test r ≈ Box(f(minimum(b)), f(maximum(b))) + @test r ≈ TransformedGeometry(b, f) f = Proj(LatLon) b = Box(merc(0, 0), merc(1, 1)) g = TransformedGeometry(b, Identity()) r, c = TB.apply(f, g) - @test r ≈ Box(f(minimum(b)), f(maximum(b))) + @test r ≈ TransformedGeometry(b, f) # --------- # POINTSET From eb495a4bf64cec13215ff430be15288a7a901cb8 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:32:51 -0300 Subject: [PATCH 393/423] Viz on Globe: Only discretize boxes and chains for now (#1116) --- ext/utils.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/utils.jl b/ext/utils.jl index 33482aeca..c39b78a71 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -54,7 +54,7 @@ function _discretize(box::Box{🌐}) discretize(box, MaxLengthDiscretization(T(100) * u"km")) end -function _discretize(geom::Geometry{🌐}) - T = numtype(Meshes.lentype(geom)) - discretize(geom, MaxLengthDiscretization(T(1000) * u"km")) +function _discretize(chain::Chain{🌐}) + T = numtype(Meshes.lentype(chain)) + discretize(chain, MaxLengthDiscretization(T(1000) * u"km")) end From c10515237fa0a31a2864c741a05c0517fdb409ad Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:42:09 -0300 Subject: [PATCH 394/423] Add `boundary` option to `Proj` and `Morphological` tranforms (#1117) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add 'boundary' option to 'Proj' and 'Morphological' tranforms * Update docstring * Fix typo * Apply suggestions from code review Co-authored-by: Júlio Hoffimann --------- Co-authored-by: Júlio Hoffimann --- src/transforms/morphological.jl | 35 +++++++++++++++++++---- src/transforms/proj.jl | 36 ++++++++++++++---------- test/sets.jl | 10 ++++--- test/transforms.jl | 50 +++++++++++++++++++++++++++++---- 4 files changed, 100 insertions(+), 31 deletions(-) diff --git a/src/transforms/morphological.jl b/src/transforms/morphological.jl index 0868a780a..578e2a161 100644 --- a/src/transforms/morphological.jl +++ b/src/transforms/morphological.jl @@ -3,24 +3,34 @@ # ------------------------------------------------------------------ """ - Morphological(fun) + Morphological(fun; boundary=false) Morphological transform given by a function `fun` that maps the coordinates of a geometry or a domain to new coordinates (`coords -> newcoords`). +Optionally, the transform samples the `boundary` of +polytopes, if this option is `true`, to handle distortions +that occur in manifold conversions. + # Examples ```julia ball = Ball((0, 0), 1) ball |> Morphological(c -> Cartesian(c.x + c.y, c.y, c.x - c.y)) + +triangle = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) +triangle |> Morphological(c -> LatLonAlt(c.lat, c.lon, 0.0m), boundary=true) ``` """ -struct Morphological{F<:Function} <: CoordinateTransform +struct Morphological{Boundary,F<:Function} <: CoordinateTransform fun::F + Morphological{Boundary}(fun::F) where {Boundary,F<:Function} = new{Boundary,F}(fun) end -parameters(t::Morphological) = (; fun=t.fun) +Morphological(fun; boundary=false) = Morphological{boundary}(fun) + +parameters(t::Morphological{Boundary}) where {Boundary} = (fun=t.fun, boundary=Boundary) applycoord(t::Morphological, p::Point) = Point(t.fun(coords(p))) @@ -30,13 +40,26 @@ applycoord(::Morphological, v::Vec) = v # SPECIAL CASES # -------------- -applycoord(t::Morphological, g::Geometry) = TransformedGeometry(g, t) +applycoord(t::Morphological, g::Primitive) = TransformedGeometry(g, t) -# method to fix ambiguities -applycoord(t::Morphological, g::TransformedGeometry) = TransformedGeometry(g, t) +applycoord(t::Morphological{true}, g::Polytope) = TransformedGeometry(g, t) applycoord(t::Morphological, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Morphological, g::RectilinearGrid) = TransformedGrid(g, t) applycoord(t::Morphological, g::StructuredGrid) = TransformedGrid(g, t) + +# ----------- +# IO METHODS +# ----------- + +Base.show(io::IO, t::Morphological{Boundary}) where {Boundary} = + print(io, "Morphological(fun: $(t.fun), boundary: $Boundary)") + +function Base.show(io::IO, ::MIME"text/plain", t::Morphological{Boundary}) where {Boundary} + summary(io, t) + println(io) + println(io, "├─ fun: $(t.fun)") + print(io, "└─ boundary: $Boundary") +end diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 88b5d35e0..160ef0ae2 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -3,12 +3,16 @@ # ------------------------------------------------------------------ """ - Proj(CRS) - Proj(code) + Proj(CRS; boundary=false) + Proj(code; boundary=false) Convert the coordinates of geometry or domain to a given coordinate reference system `CRS` or EPSG/ESRI `code`. +Optionally, the transform samples the `boundary` of +polytopes, if this option is `true`, to handle distortions +that occur in manifold conversions. + ## Examples ```julia @@ -17,17 +21,18 @@ Proj(WebMercator) Proj(Mercator{WGS84Latest}) Proj(EPSG{3395}) Proj(ESRI{54017}) +Proj(Robinson, boundary=true) ``` """ -struct Proj{CRS} <: CoordinateTransform end +struct Proj{CRS,Boundary} <: CoordinateTransform end -Proj(CRS) = Proj{CRS}() +Proj(CRS; boundary=false) = Proj{CRS,boundary}() -Proj(code::Type{<:EPSG}) = Proj{CoordRefSystems.get(code)}() +Proj(code::Type{<:EPSG}; kwargs...) = Proj(CoordRefSystems.get(code); kwargs...) -Proj(code::Type{<:ESRI}) = Proj{CoordRefSystems.get(code)}() +Proj(code::Type{<:ESRI}; kwargs...) = Proj(CoordRefSystems.get(code); kwargs...) -parameters(::Proj{CRS}) where {CRS} = (; CRS) +parameters(::Proj{CRS,Boundary}) where {CRS,Boundary} = (CRS=CRS, boundary=Boundary) # avoid constructing a new geometry or domain when the CRS is the same function apply(t::Proj{CRS}, g::GeometryOrDomain) where {CRS} @@ -51,13 +56,13 @@ applycoord(::Proj, v::Vec) = v # SPECIAL CASES # -------------- -applycoord(t::Proj{<:Projected}, g::Geometry{<:🌐}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Projected}, g::Primitive{<:🌐}) = TransformedGeometry(g, t) + +applycoord(t::Proj{<:Geographic}, g::Primitive{<:𝔼}) = TransformedGeometry(g, t) -applycoord(t::Proj{<:Geographic}, g::Geometry{<:𝔼}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Projected,true}, g::Polytope{K,<:🌐}) where {K} = TransformedGeometry(g, t) -# methods to fix ambiguities -applycoord(t::Proj{<:Projected}, g::TransformedGeometry{<:🌐}) = TransformedGeometry(g, t) -applycoord(t::Proj{<:Geographic}, g::TransformedGeometry{<:𝔼}) = TransformedGeometry(g, t) +applycoord(t::Proj{<:Geographic,true}, g::Polytope{K,<:𝔼}) where {K} = TransformedGeometry(g, t) applycoord(t::Proj, g::RegularGrid) = TransformedGrid(g, t) @@ -69,12 +74,13 @@ applycoord(t::Proj, g::StructuredGrid) = TransformedGrid(g, t) # IO METHODS # ----------- -Base.show(io::IO, ::Proj{CRS}) where {CRS} = print(io, "Proj(CRS: $CRS)") +Base.show(io::IO, ::Proj{CRS,Boundary}) where {CRS,Boundary} = print(io, "Proj(CRS: $CRS, boundary: $Boundary)") -function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS}) where {CRS} +function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS,Boundary}) where {CRS,Boundary} summary(io, t) println(io) - print(io, "└─ CRS: $CRS") + println(io, "├─ CRS: $CRS") + print(io, "└─ boundary: $Boundary") end # ----------------- diff --git a/test/sets.jl b/test/sets.jl index 2a12fbb4c..d253653ea 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -30,22 +30,24 @@ q = Quadrangle(latlon(0, 0), latlon(0, 1), latlon(1, 1), latlon(1, 0)) |> Proj(WebMercator) geoms = [s, t, q] gset = GeometrySet(geoms) + @test eltype(gset) <: Polytope @test crs(gset) <: LatLon gset = GeometrySet(g for g in geoms) + @test eltype(gset) <: Polytope @test crs(gset) <: LatLon geoms = [t, s, q] gset = GeometrySet(geoms) - @test eltype(gset) <: TransformedGeometry + @test eltype(gset) <: Polytope @test crs(gset) <: PlateCarree gset = GeometrySet(g for g in geoms) - @test eltype(gset) <: TransformedGeometry + @test eltype(gset) <: Polytope @test crs(gset) <: PlateCarree geoms = [q, s, t] gset = GeometrySet(geoms) - @test eltype(gset) <: TransformedGeometry + @test eltype(gset) <: Polytope @test crs(gset) <: WebMercator gset = GeometrySet(g for g in geoms) - @test eltype(gset) <: TransformedGeometry + @test eltype(gset) <: Polytope @test crs(gset) <: WebMercator # conversion diff --git a/test/transforms.jl b/test/transforms.jl index c91c1a024..ee2c1c289 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1251,14 +1251,15 @@ end @test !isaffine(Proj(Polar)) @test !TB.isrevertible(Proj(Polar)) @test !TB.isinvertible(Proj(Polar)) - @test TB.parameters(Proj(Polar)) == (; CRS=Polar) - @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) - @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) + @test TB.parameters(Proj(Polar)) == (CRS=Polar, boundary=false) + @test TB.parameters(Proj(EPSG{3395})) == (CRS=Mercator{WGS84Latest}, boundary=false) + @test TB.parameters(Proj(ESRI{54017})) == (CRS=Behrmann{WGS84Latest}, boundary=false) f = Proj(Mercator) - @test sprint(show, f) == "Proj(CRS: CoordRefSystems.Mercator)" + @test sprint(show, f) == "Proj(CRS: CoordRefSystems.Mercator, boundary: false)" @test sprint(show, MIME"text/plain"(), f) == """ Proj transform - └─ CRS: CoordRefSystems.Mercator""" + ├─ CRS: CoordRefSystems.Mercator + └─ boundary: false""" # ---- # VEC @@ -1457,6 +1458,26 @@ end f = Proj(crs(merc(0, 0))) r, c = TB.apply(f, d) @test r === d + + # ---------------- + # BOUNDARY OPTION + # ---------------- + + f = Proj(Mercator, boundary=false) + g = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) + r, c = TB.apply(f, g) + @test r isa Triangle + f = Proj(Mercator, boundary=true) + r, c = TB.apply(f, g) + @test r isa TransformedGeometry + + f = Proj(LatLon, boundary=false) + g = Triangle(merc(0, 0), merc(1, 0), merc(1, 1)) + r, c = TB.apply(f, g) + @test r isa Triangle + f = Proj(LatLon, boundary=true) + r, c = TB.apply(f, g) + @test r isa TransformedGeometry end @testitem "Morphological" setup = [Setup] begin @@ -1464,7 +1485,12 @@ end @test !isaffine(f) @test !TB.isrevertible(f) @test !TB.isinvertible(f) - @test TB.parameters(f) == (; fun=f.fun) + @test TB.parameters(f) == (fun=f.fun, boundary=false) + @test sprint(show, f) == "Morphological(fun: $(f.fun), boundary: false)" + @test sprint(show, MIME"text/plain"(), f) == """ + Morphological transform + ├─ fun: $(f.fun) + └─ boundary: false""" # ---- # VEC @@ -1590,6 +1616,18 @@ end d = SimpleMesh(p, c) r, c = TB.apply(f, d) @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) + + # ---------------- + # BOUNDARY OPTION + # ---------------- + + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x)), boundary=false) + g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) + r, c = TB.apply(f, g) + @test r isa Triangle + f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x)), boundary=true) + r, c = TB.apply(f, g) + @test r isa TransformedGeometry end @testitem "LengthUnit" setup = [Setup] begin From fa7fffd6d7adb1804f9aca8b9af15d01ccc088eb Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:57:44 -0300 Subject: [PATCH 395/423] Remove 'boundary' option in favor of using 'TransformedGeometry' explicitly (#1119) --- src/transforms/morphological.jl | 41 ++++++++------------------- src/transforms/proj.jl | 32 ++++++++++----------- test/transforms.jl | 50 ++++----------------------------- 3 files changed, 34 insertions(+), 89 deletions(-) diff --git a/src/transforms/morphological.jl b/src/transforms/morphological.jl index 578e2a161..5d10b944a 100644 --- a/src/transforms/morphological.jl +++ b/src/transforms/morphological.jl @@ -3,34 +3,33 @@ # ------------------------------------------------------------------ """ - Morphological(fun; boundary=false) + Morphological(fun) Morphological transform given by a function `fun` that maps the coordinates of a geometry or a domain to new coordinates (`coords -> newcoords`). -Optionally, the transform samples the `boundary` of -polytopes, if this option is `true`, to handle distortions -that occur in manifold conversions. - -# Examples +## Examples ```julia ball = Ball((0, 0), 1) ball |> Morphological(c -> Cartesian(c.x + c.y, c.y, c.x - c.y)) -triangle = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) -triangle |> Morphological(c -> LatLonAlt(c.lat, c.lon, 0.0m), boundary=true) +triangle = Triangle(Point(LatLon(0, 0)), Point(LatLon(0, 45)), Point(LatLon(45, 0))) +triangle |> Morphological(c -> LatLonAlt(c.lat, c.lon, 0.0m)) ``` + +### Notes + +* By default, only the vertices of the polytopes are transformed, + disregarding distortions that occur in manifold conversions. + To handle this case, use [`TransformedGeometry`](@ref). """ -struct Morphological{Boundary,F<:Function} <: CoordinateTransform +struct Morphological{F<:Function} <: CoordinateTransform fun::F - Morphological{Boundary}(fun::F) where {Boundary,F<:Function} = new{Boundary,F}(fun) end -Morphological(fun; boundary=false) = Morphological{boundary}(fun) - -parameters(t::Morphological{Boundary}) where {Boundary} = (fun=t.fun, boundary=Boundary) +parameters(t::Morphological) = (; fun=t.fun) applycoord(t::Morphological, p::Point) = Point(t.fun(coords(p))) @@ -42,24 +41,8 @@ applycoord(::Morphological, v::Vec) = v applycoord(t::Morphological, g::Primitive) = TransformedGeometry(g, t) -applycoord(t::Morphological{true}, g::Polytope) = TransformedGeometry(g, t) - applycoord(t::Morphological, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Morphological, g::RectilinearGrid) = TransformedGrid(g, t) applycoord(t::Morphological, g::StructuredGrid) = TransformedGrid(g, t) - -# ----------- -# IO METHODS -# ----------- - -Base.show(io::IO, t::Morphological{Boundary}) where {Boundary} = - print(io, "Morphological(fun: $(t.fun), boundary: $Boundary)") - -function Base.show(io::IO, ::MIME"text/plain", t::Morphological{Boundary}) where {Boundary} - summary(io, t) - println(io) - println(io, "├─ fun: $(t.fun)") - print(io, "└─ boundary: $Boundary") -end diff --git a/src/transforms/proj.jl b/src/transforms/proj.jl index 160ef0ae2..6ca5ae2db 100644 --- a/src/transforms/proj.jl +++ b/src/transforms/proj.jl @@ -3,8 +3,8 @@ # ------------------------------------------------------------------ """ - Proj(CRS; boundary=false) - Proj(code; boundary=false) + Proj(CRS) + Proj(code) Convert the coordinates of geometry or domain to a given coordinate reference system `CRS` or EPSG/ESRI `code`. @@ -21,18 +21,23 @@ Proj(WebMercator) Proj(Mercator{WGS84Latest}) Proj(EPSG{3395}) Proj(ESRI{54017}) -Proj(Robinson, boundary=true) ``` + +### Notes + +* By default, only the vertices of the polytopes are transformed, + disregarding distortions that occur in manifold conversions. + To handle this case, use [`TransformedGeometry`](@ref). """ -struct Proj{CRS,Boundary} <: CoordinateTransform end +struct Proj{CRS} <: CoordinateTransform end -Proj(CRS; boundary=false) = Proj{CRS,boundary}() +Proj(CRS) = Proj{CRS}() -Proj(code::Type{<:EPSG}; kwargs...) = Proj(CoordRefSystems.get(code); kwargs...) +Proj(code::Type{<:EPSG}) = Proj(CoordRefSystems.get(code)) -Proj(code::Type{<:ESRI}; kwargs...) = Proj(CoordRefSystems.get(code); kwargs...) +Proj(code::Type{<:ESRI}) = Proj(CoordRefSystems.get(code)) -parameters(::Proj{CRS,Boundary}) where {CRS,Boundary} = (CRS=CRS, boundary=Boundary) +parameters(::Proj{CRS}) where {CRS} = (; CRS) # avoid constructing a new geometry or domain when the CRS is the same function apply(t::Proj{CRS}, g::GeometryOrDomain) where {CRS} @@ -60,10 +65,6 @@ applycoord(t::Proj{<:Projected}, g::Primitive{<:🌐}) = TransformedGeometry(g, applycoord(t::Proj{<:Geographic}, g::Primitive{<:𝔼}) = TransformedGeometry(g, t) -applycoord(t::Proj{<:Projected,true}, g::Polytope{K,<:🌐}) where {K} = TransformedGeometry(g, t) - -applycoord(t::Proj{<:Geographic,true}, g::Polytope{K,<:𝔼}) where {K} = TransformedGeometry(g, t) - applycoord(t::Proj, g::RegularGrid) = TransformedGrid(g, t) applycoord(t::Proj, g::RectilinearGrid) = TransformedGrid(g, t) @@ -74,13 +75,12 @@ applycoord(t::Proj, g::StructuredGrid) = TransformedGrid(g, t) # IO METHODS # ----------- -Base.show(io::IO, ::Proj{CRS,Boundary}) where {CRS,Boundary} = print(io, "Proj(CRS: $CRS, boundary: $Boundary)") +Base.show(io::IO, ::Proj{CRS}) where {CRS} = print(io, "Proj(CRS: $CRS)") -function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS,Boundary}) where {CRS,Boundary} +function Base.show(io::IO, ::MIME"text/plain", t::Proj{CRS}) where {CRS} summary(io, t) println(io) - println(io, "├─ CRS: $CRS") - print(io, "└─ boundary: $Boundary") + print(io, "└─ CRS: $CRS") end # ----------------- diff --git a/test/transforms.jl b/test/transforms.jl index ee2c1c289..c91c1a024 100644 --- a/test/transforms.jl +++ b/test/transforms.jl @@ -1251,15 +1251,14 @@ end @test !isaffine(Proj(Polar)) @test !TB.isrevertible(Proj(Polar)) @test !TB.isinvertible(Proj(Polar)) - @test TB.parameters(Proj(Polar)) == (CRS=Polar, boundary=false) - @test TB.parameters(Proj(EPSG{3395})) == (CRS=Mercator{WGS84Latest}, boundary=false) - @test TB.parameters(Proj(ESRI{54017})) == (CRS=Behrmann{WGS84Latest}, boundary=false) + @test TB.parameters(Proj(Polar)) == (; CRS=Polar) + @test TB.parameters(Proj(EPSG{3395})) == (; CRS=Mercator{WGS84Latest}) + @test TB.parameters(Proj(ESRI{54017})) == (; CRS=Behrmann{WGS84Latest}) f = Proj(Mercator) - @test sprint(show, f) == "Proj(CRS: CoordRefSystems.Mercator, boundary: false)" + @test sprint(show, f) == "Proj(CRS: CoordRefSystems.Mercator)" @test sprint(show, MIME"text/plain"(), f) == """ Proj transform - ├─ CRS: CoordRefSystems.Mercator - └─ boundary: false""" + └─ CRS: CoordRefSystems.Mercator""" # ---- # VEC @@ -1458,26 +1457,6 @@ end f = Proj(crs(merc(0, 0))) r, c = TB.apply(f, d) @test r === d - - # ---------------- - # BOUNDARY OPTION - # ---------------- - - f = Proj(Mercator, boundary=false) - g = Triangle(latlon(0, 0), latlon(0, 45), latlon(45, 0)) - r, c = TB.apply(f, g) - @test r isa Triangle - f = Proj(Mercator, boundary=true) - r, c = TB.apply(f, g) - @test r isa TransformedGeometry - - f = Proj(LatLon, boundary=false) - g = Triangle(merc(0, 0), merc(1, 0), merc(1, 1)) - r, c = TB.apply(f, g) - @test r isa Triangle - f = Proj(LatLon, boundary=true) - r, c = TB.apply(f, g) - @test r isa TransformedGeometry end @testitem "Morphological" setup = [Setup] begin @@ -1485,12 +1464,7 @@ end @test !isaffine(f) @test !TB.isrevertible(f) @test !TB.isinvertible(f) - @test TB.parameters(f) == (fun=f.fun, boundary=false) - @test sprint(show, f) == "Morphological(fun: $(f.fun), boundary: false)" - @test sprint(show, MIME"text/plain"(), f) == """ - Morphological transform - ├─ fun: $(f.fun) - └─ boundary: false""" + @test TB.parameters(f) == (; fun=f.fun) # ---- # VEC @@ -1616,18 +1590,6 @@ end d = SimpleMesh(p, c) r, c = TB.apply(f, d) @test r ≈ SimpleMesh(f.(vertices(d)), topology(d)) - - # ---------------- - # BOUNDARY OPTION - # ---------------- - - f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x)), boundary=false) - g = Triangle(cart(0, 0), cart(1, 0), cart(1, 1)) - r, c = TB.apply(f, g) - @test r isa Triangle - f = Morphological(c -> Cartesian(c.x, c.y, zero(c.x)), boundary=true) - r, c = TB.apply(f, g) - @test r isa TransformedGeometry end @testitem "LengthUnit" setup = [Setup] begin From 5ac24466d9c1d9bd0dc3590fec32f4072898e918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 17 Oct 2024 14:04:56 -0300 Subject: [PATCH 396/423] Bump version --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 8cfad907d..1f39aa191 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.51.22" +version = "0.52.0" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" @@ -24,7 +24,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] Bessels = "0.2" CircularArrays = "1.3" -Colorfy = "0.1, 1" +Colorfy = "1.0" CoordRefSystems = "0.15" DelaunayTriangulation = "1.0" Distances = "0.10" From 1ba393fa02a35ec9ebbd1b7ce8f93e3cc99fec10 Mon Sep 17 00:00:00 2001 From: Elias Carvalho Date: Fri, 25 Oct 2024 13:53:53 -0300 Subject: [PATCH 397/423] Remove unused code --- src/sampling/mindistance.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sampling/mindistance.jl b/src/sampling/mindistance.jl index b95b22685..89740d5b1 100644 --- a/src/sampling/mindistance.jl +++ b/src/sampling/mindistance.jl @@ -1,4 +1,3 @@ -using Base: BitSignedSmall # ------------------------------------------------------------------ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ From dfaf12def63ff67104a1f44254ad421cbb509238 Mon Sep 17 00:00:00 2001 From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> Date: Fri, 25 Oct 2024 21:19:28 +0200 Subject: [PATCH 398/423] Add `MinDistanceSampling` for 3D Ball (#1121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add MinDistanceSampling for 3D Ball * Update src/sampling/mindistance.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * add test * Minor adjustments * Fix method ambiguity --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Júlio Hoffimann --- src/sampling/mindistance.jl | 10 +++++++--- test/sampling.jl | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sampling/mindistance.jl b/src/sampling/mindistance.jl index 89740d5b1..50e87d008 100644 --- a/src/sampling/mindistance.jl +++ b/src/sampling/mindistance.jl @@ -37,7 +37,11 @@ MinDistanceSampling(α, ρ, δ, metric) = MinDistanceSampling(addunit(α, u"m"), MinDistanceSampling(α::T; ρ=T(0.65), δ=100, metric=Euclidean()) where {T} = MinDistanceSampling(α, ρ, δ, metric) -function sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) +sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) = _sample(rng, d, method) + +sample(rng::AbstractRNG, b::Ball, method::MinDistanceSampling) = _sample(rng, b, method) + +function _sample(rng::AbstractRNG, obj, method::MinDistanceSampling) # retrieve parameters α = method.α ρ = method.ρ @@ -45,7 +49,7 @@ function sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) m = method.metric # total volume/area of the object - V = sum(measure, d) + V = measure(obj) # expected number of Poisson samples # for relative radius (Lagae & Dutré 2007) @@ -55,7 +59,7 @@ function sample(rng::AbstractRNG, d::Domain, method::MinDistanceSampling) O = ceil(Int, δ * ustrip(N)) # oversample the object - points = sample(rng, d, HomogeneousSampling(O)) + points = sample(rng, obj, HomogeneousSampling(O)) # collect points into point set 𝒫 = PointSet(collect(points)) diff --git a/test/sampling.jl b/test/sampling.jl index 39c79cf98..a3451f267 100644 --- a/test/sampling.jl +++ b/test/sampling.jl @@ -392,6 +392,12 @@ end poly = PolyArea(cart.([(-44.20065308, -21.12284851), (-44.20324135, -21.122799875), (-44.20582962, -21.12275124)])) ps = sample(poly, MinDistanceSampling(3.2423333333753135e-5)) @test length(ps) > 0 + + ball = Ball(cart(10, 10), T(3)) + ps = sample(ball, MinDistanceSampling(1.0)) + n = length(ps) + @test all(∈(ball), ps) + @test all(norm(ps[i] - ps[j]) ≥ T(1.0) * u"m" for i in 1:n for j in (i + 1):n) end @testitem "FibonacciSampling" setup = [Setup] begin From d8be1a95fc89b6b443ba08bafd33c54e55844435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 25 Oct 2024 17:22:42 -0300 Subject: [PATCH 399/423] Implement indexable api for topology (#1122) * Implement indexable api for topology * Update src/topologies.jl Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> * Update src/topologies.jl Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> * Apply suggestions to Domain --------- Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --- src/domains.jl | 4 ++-- src/topologies.jl | 20 ++++++++++++++++++++ test/topologies.jl | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/domains.jl b/src/domains.jl index be6d4d729..028717df7 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -36,7 +36,7 @@ Base.isapprox(d1::Domain, d2::Domain; kwargs...) = Base.getindex(d::Domain, ind::Int) = element(d, ind) -Base.getindex(d::Domain, inds::AbstractVector) = [element(d, ind) for ind in inds] +Base.getindex(d::Domain, inds::AbstractVector) = [d[ind] for ind in inds] Base.firstindex(d::Domain) = 1 @@ -48,7 +48,7 @@ Base.iterate(d::Domain, state=1) = state > nelements(d) ? nothing : (d[state], s Base.eltype(d::Domain) = eltype([d[i] for i in 1:nelements(d)]) -Base.keys(d::Domain) = 1:nelements(d) +Base.keys(d::Domain) = Base.OneTo(nelements(d)) Base.parent(d::Domain) = d diff --git a/src/topologies.jl b/src/topologies.jl index 0661d8f71..9e45168be 100644 --- a/src/topologies.jl +++ b/src/topologies.jl @@ -132,6 +132,26 @@ Return the number of facets of the `topology`. """ function nfacets(::Topology) end +# ---------- +# FALLBACKS +# ---------- + +Base.getindex(t::Topology, ind::Int) = element(t, ind) + +Base.getindex(t::Topology, inds::AbstractVector) = [t[ind] for ind in inds] + +Base.firstindex(t::Topology) = 1 + +Base.lastindex(t::Topology) = nelements(t) + +Base.length(t::Topology) = nelements(t) + +Base.iterate(t::Topology, state=1) = state > nelements(t) ? nothing : (t[state], state + 1) + +Base.eltype(t::Topology) = eltype([t[i] for i in 1:nelements(t)]) + +Base.keys(t::Topology) = Base.OneTo(nelements(t)) + # ---------------- # IMPLEMENTATIONS # ---------------- diff --git a/test/topologies.jl b/test/topologies.jl index fdcd51366..3e51a0b7e 100644 --- a/test/topologies.jl +++ b/test/topologies.jl @@ -353,6 +353,17 @@ @test element(t, 1) == connect((1, 2, 5, 4, 13, 14, 17, 16), Hexahedron) @test element(t, 2) == connect((2, 3, 6, 5, 14, 15, 18, 17), Hexahedron) @test element(t, 24) == connect((44, 45, 48, 47, 8, 9, 12, 11), Hexahedron) + + # indexable api + t = GridTopology(10, 10) + @test t[begin] == connect((1, 2, 13, 12), Quadrangle) + @test t[end] == connect((109, 110, 121, 120), Quadrangle) + @test t[10] == connect((10, 11, 22, 21), Quadrangle) + @test length(t) == 100 + @test eltype(t) == Connectivity{Quadrangle,4} + for e in t + @test e isa Connectivity{Quadrangle,4} + end end @testitem "HalfEdgeTopology" setup = [Setup] begin @@ -483,6 +494,18 @@ end t = HalfEdgeTopology(e) n = collect(elements(t)) @test n == connect.([(5, 4, 1), (6, 2, 4), (6, 5, 3), (4, 5, 6)]) + + # indexable api + g = GridTopology(10, 10) + t = convert(HalfEdgeTopology, g) + @test t[begin] == connect((13, 12, 1, 2), Quadrangle) + @test t[end] == connect((110, 121, 120, 109), Quadrangle) + @test t[10] == connect((22, 21, 10, 11), Quadrangle) + @test length(t) == 100 + @test eltype(t) == Connectivity{Quadrangle,4} + for e in t + @test e isa Connectivity{Quadrangle,4} + end end @testitem "SimpleTopology" setup = [Setup] begin @@ -538,4 +561,16 @@ end @test nfaces(t, 2) == 4 @test nfaces(t, 1) == 12 @test nfaces(t, 0) == 9 + + # indexable api + g = GridTopology(10, 10) + t = convert(SimpleTopology, g) + @test t[begin] == connect((1, 2, 13, 12), Quadrangle) + @test t[end] == connect((109, 110, 121, 120), Quadrangle) + @test t[10] == connect((10, 11, 22, 21), Quadrangle) + @test length(t) == 100 + @test eltype(t) == Connectivity{Quadrangle,4} + for e in t + @test e isa Connectivity{Quadrangle,4} + end end From 0ca5a1410447c70dcd4299aa13909549fb51343d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 25 Oct 2024 17:22:56 -0300 Subject: [PATCH 400/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1f39aa191..0924c4ae8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.0" +version = "0.52.1" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 267358ecfd05c393603967bd3b691a56bfb5f9e4 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:05:05 -0300 Subject: [PATCH 401/423] Use `TiledIteration.TileIterator` in `RegularCoarsening` (#1123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use 'TiledIteration.TileIterator' in 'RegularCoarsening' * Add tests * Update comments * Update src/coarsening/regular.jl Co-authored-by: Júlio Hoffimann * Add more tests * Apply suggestions --------- Co-authored-by: Júlio Hoffimann --- Project.toml | 2 ++ src/Meshes.jl | 1 + src/coarsening/regular.jl | 31 ++++++++++++++++++++++++------- test/coarsening.jl | 27 +++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 0924c4ae8..e07048bf3 100644 --- a/Project.toml +++ b/Project.toml @@ -18,6 +18,7 @@ ScopedValues = "7e506255-f358-4e82-b7e4-beb19740aa63" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +TiledIteration = "06e1c1a7-607b-532d-9fad-de7d9aa2abac" TransformsBase = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" @@ -37,6 +38,7 @@ ScopedValues = "1.2" SparseArrays = "1.9" StaticArrays = "1.0" StatsBase = "0.33, 0.34" +TiledIteration = "0.5" TransformsBase = "1.6" Unitful = "1.17" julia = "1.9" diff --git a/src/Meshes.jl b/src/Meshes.jl index 02514fe24..e7b067e91 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -20,6 +20,7 @@ using Distances: Haversine, SphericalAngle using Distances: evaluate, result_type using Rotations: Rotation, QuatRotation, Angle2d using Rotations: rotation_between +using TiledIteration: TileIterator using CoordRefSystems: Basic, Projected, Geographic using NearestNeighbors: KDTree, BallTree using NearestNeighbors: knn, inrange diff --git a/src/coarsening/regular.jl b/src/coarsening/regular.jl index ad1f46c43..e485c29c6 100644 --- a/src/coarsening/regular.jl +++ b/src/coarsening/regular.jl @@ -22,26 +22,43 @@ RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors) function coarsen(grid::OrthoRegularGrid, method::RegularCoarsening) factors = fitdims(method.factors, paramdim(grid)) - RegularGrid(minimum(grid), maximum(grid), dims=size(grid) .÷ factors) + dims = _coarsesize(grid, factors) + RegularGrid(minimum(grid), maximum(grid), dims=dims) end function coarsen(grid::RectilinearGrid, method::RegularCoarsening) factors = fitdims(method.factors, paramdim(grid)) - dims = vsize(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid)) + inds = _coarseinds(grid, factors) xyzₛ = xyz(grid) - xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], paramdim(grid)) + xyzₜ = ntuple(i -> xyzₛ[i][inds[i]], paramdim(grid)) RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ) end function coarsen(grid::StructuredGrid, method::RegularCoarsening) factors = fitdims(method.factors, paramdim(grid)) - dims = vsize(grid) - rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid)) + inds = _coarseinds(grid, factors) XYZₛ = XYZ(grid) - XYZₜ = ntuple(i -> XYZₛ[i][rngs...], paramdim(grid)) + XYZₜ = ntuple(i -> XYZₛ[i][inds...], paramdim(grid)) StructuredGrid{manifold(grid),crs(grid)}(XYZₜ) end coarsen(grid::TransformedGrid, method::RegularCoarsening) = TransformedGrid(coarsen(parent(grid), method), transform(grid)) + +# ----------------- +# HELPER FUNCTIONS +# ----------------- + +function _coarsesize(grid, factors) + dims = size(grid) + axes = ntuple(i -> 1:dims[i], paramdim(grid)) + size(TileIterator(axes, factors)) +end + +_coarsevsize(grid, factors) = _coarsesize(grid, factors) .+ .!isperiodic(grid) + +function _coarseinds(grid, factors) + dims = vsize(grid) + tdims = _coarsevsize(grid, factors) + ntuple(i -> floor.(Int, range(start=1, stop=dims[i], length=tdims[i])), paramdim(grid)) +end diff --git a/test/coarsening.jl b/test/coarsening.jl index 49dbe4af9..fb09f956e 100644 --- a/test/coarsening.jl +++ b/test/coarsening.jl @@ -16,6 +16,22 @@ tgrid = CartesianGrid(cart(0.0, 0.0), cart(10.0, 10.0), dims=(10, 5)) @test coarsen(grid, RegularCoarsening(2, 4)) == tgrid + # non-multiple dimensions + grid = CartesianGrid(cart(0, 0), cart(13, 17), dims=(13, 17)) + tgrid = CartesianGrid(cart(0, 0), cart(13, 17), dims=(3, 6)) + @test coarsen(grid, RegularCoarsening(5, 3)) == tgrid + rgrid = convert(RectilinearGrid, grid) + @test size(coarsen(rgrid, RegularCoarsening(5, 3))) == (3, 6) + sgrid = convert(StructuredGrid, grid) + @test size(coarsen(sgrid, RegularCoarsening(5, 3))) == (3, 6) + tfgrid = TransformedGrid(grid, Identity()) + @test size(coarsen(tfgrid, RegularCoarsening(5, 3))) == (3, 6) + + # large grid + grid = CartesianGrid(cart(0, 0), cart(16200, 8100), dims=(16200, 8100)) + tgrid = CartesianGrid(cart(0, 0), cart(16200, 8100), dims=(203, 203)) + @test coarsen(grid, RegularCoarsening(80, 40)) == tgrid + # 3D grids grid = cartgrid(100, 100, 100) tgrid = CartesianGrid(minimum(grid), maximum(grid), dims=(50, 25, 20)) @@ -28,4 +44,15 @@ @test coarsen(sgrid, RegularCoarsening(2, 4, 5)) == tsgrid tfgrid = TransformedGrid(grid, Identity()) @test coarsen(tfgrid, RegularCoarsening(2, 4, 5)) == coarsen(grid, RegularCoarsening(2, 4, 5)) + + # non-multiple dimensions + grid = CartesianGrid(cart(0, 0, 0), cart(13, 17, 23), dims=(13, 17, 23)) + tgrid = CartesianGrid(cart(0, 0, 0), cart(13, 17, 23), dims=(2, 4, 8)) + @test coarsen(grid, RegularCoarsening(7, 5, 3)) == tgrid + rgrid = convert(RectilinearGrid, grid) + @test size(coarsen(rgrid, RegularCoarsening(7, 5, 3))) == (2, 4, 8) + sgrid = convert(StructuredGrid, grid) + @test size(coarsen(sgrid, RegularCoarsening(7, 5, 3))) == (2, 4, 8) + tfgrid = TransformedGrid(grid, Identity()) + @test size(coarsen(tfgrid, RegularCoarsening(7, 5, 3))) == (2, 4, 8) end From ed0649896e58cb811d1a500b67b05b0c5bf75df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 6 Nov 2024 16:05:19 -0300 Subject: [PATCH 402/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e07048bf3..2ac8bb7b4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.1" +version = "0.52.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 294bdda17224bce573058b86583f97514bdd4584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 6 Nov 2024 18:26:43 -0300 Subject: [PATCH 403/423] Replace Base.OneTo(n) by 1:n for consistency (#1124) --- src/domains.jl | 2 +- src/topologies.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains.jl b/src/domains.jl index 028717df7..c47c4af2c 100644 --- a/src/domains.jl +++ b/src/domains.jl @@ -48,7 +48,7 @@ Base.iterate(d::Domain, state=1) = state > nelements(d) ? nothing : (d[state], s Base.eltype(d::Domain) = eltype([d[i] for i in 1:nelements(d)]) -Base.keys(d::Domain) = Base.OneTo(nelements(d)) +Base.keys(d::Domain) = 1:nelements(d) Base.parent(d::Domain) = d diff --git a/src/topologies.jl b/src/topologies.jl index 9e45168be..11e2f0416 100644 --- a/src/topologies.jl +++ b/src/topologies.jl @@ -150,7 +150,7 @@ Base.iterate(t::Topology, state=1) = state > nelements(t) ? nothing : (t[state], Base.eltype(t::Topology) = eltype([t[i] for i in 1:nelements(t)]) -Base.keys(t::Topology) = Base.OneTo(nelements(t)) +Base.keys(t::Topology) = 1:nelements(t) # ---------------- # IMPLEMENTATIONS From f91b4ba8d894013366712c66dca85ccdda598159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 11 Nov 2024 11:10:14 -0300 Subject: [PATCH 404/423] Refactor tests --- test/indices.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/indices.jl b/test/indices.jl index 18644e6f7..6d5f2ccc4 100644 --- a/test/indices.jl +++ b/test/indices.jl @@ -4,14 +4,14 @@ v = view(g, b) @test v == CartesianGrid(cart(0, 0), cart(6, 6), dims=(6, 6)) - p = PointSet(collect(vertices(g))) + p = PointSet(vertices(g)) v = view(p, b) @test centroid(v, 1) == cart(1, 1) @test centroid(v, nelements(v)) == cart(5, 5) # boxes g = cartgrid(10, 10) - p = PointSet(collect(vertices(g))) + p = PointSet(vertices(g)) b = Ball(cart(0, 0), T(2)) v = view(g, b) @test nelements(v) == 4 From 1bdff3bff5be938ff5b06d33fbdf4694542340d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 11 Nov 2024 11:10:28 -0300 Subject: [PATCH 405/423] Refactor docstrings --- src/projecting.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/projecting.jl b/src/projecting.jl index 38297b514..0246ba966 100644 --- a/src/projecting.jl +++ b/src/projecting.jl @@ -5,8 +5,8 @@ """ proj2D(geometry) -Project 3D `geometry` onto a 2D plane of -maximum variance using singular values. +Project 3D `geometry` onto a 2D plane of maximum +variance using singular value decomposition. """ function proj2D end From 20fecedb6cf86bbc3026a6df496cce98bc12a3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 11 Nov 2024 13:02:20 -0300 Subject: [PATCH 406/423] Improve docstring of BallSearch and KBallSearch --- src/neighborsearch/ball.jl | 4 +++- src/neighborsearch/kball.jl | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/neighborsearch/ball.jl b/src/neighborsearch/ball.jl index 73516a974..f967cfd86 100644 --- a/src/neighborsearch/ball.jl +++ b/src/neighborsearch/ball.jl @@ -5,7 +5,9 @@ """ BallSearch(domain, ball) -A method for searching neighbors in `domain` inside `ball`. +A method for searching neighbors in `domain` inside metric `ball`. + +See [`MetricBall`](@ref) for additional details. """ struct BallSearch{D<:Domain,B<:MetricBall,T} <: NeighborSearchMethod # input fields diff --git a/src/neighborsearch/kball.jl b/src/neighborsearch/kball.jl index 18d32a588..53c0a3e4f 100644 --- a/src/neighborsearch/kball.jl +++ b/src/neighborsearch/kball.jl @@ -6,7 +6,9 @@ KBallSearch(domain, k, ball) A method that searches `k` nearest neighbors and then filters -these neighbors using a norm `ball`. +these neighbors using a metric `ball`. + +See [`MetricBall`](@ref) for additional details. """ struct KBallSearch{D<:Domain,B<:MetricBall,T} <: BoundedNeighborSearchMethod # input fields From ed88c403b2b62aa8a6535ef420d675d2a18efec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 11 Nov 2024 14:53:38 -0300 Subject: [PATCH 407/423] Improve docstring of search methods --- docs/src/algorithms/neighborsearch.md | 1 + src/neighborsearch.jl | 58 +++++++++++++++------------ 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/docs/src/algorithms/neighborsearch.md b/docs/src/algorithms/neighborsearch.md index 0ed36cd69..c77c0eecd 100644 --- a/docs/src/algorithms/neighborsearch.md +++ b/docs/src/algorithms/neighborsearch.md @@ -10,6 +10,7 @@ point of reference. This can be performed with search methods: ```@docs NeighborSearchMethod +BoundedNeighborSearchMethod search search! searchdists diff --git a/src/neighborsearch.jl b/src/neighborsearch.jl index 6927ff45f..ae0f22ae3 100644 --- a/src/neighborsearch.jl +++ b/src/neighborsearch.jl @@ -10,53 +10,61 @@ A method for searching neighbors given a reference point. abstract type NeighborSearchMethod end """ - search!(neighbors, pₒ, method; mask=nothing) + search(pₒ, method, mask=nothing) -Update `neighbors` of point `pₒ` using `method` and return -number of neighbors found. Optionally, specify a `mask` for -all indices of the domain. +Return neighbors of point `pₒ` using `method`. Optionally, +specify a `mask` for all indices of the domain. """ -function search! end +function search end """ - searchdists!(neighbors, distances, pₒ, method; mask=nothing) + BoundedNeighborSearchMethod -Update `neighbors` and `distances` of point `pₒ` using `method` -and return number of neighbors found. Optionally, specify a -`mask` for all indices of the domain. +A method for searching neighbors with the property that the number of neighbors +is bounded above by a known constant (e.g. k-nearest neighbors). """ -function searchdists! end +abstract type BoundedNeighborSearchMethod <: NeighborSearchMethod end """ - search(pₒ, method, mask=nothing) + maxneighbors(method) -Return neighbors of point `pₒ` using `method`. Optionally, -specify a `mask` for all indices of the domain. +Return the maximum number of neighbors obtained with bounded search `method`. + +See [`BoundedNeighborSearchMethod`](@ref) for additional details. """ -function search end +function maxneighbors end """ - searchdists(pₒ, method, mask=nothing) + search!(neighbors, pₒ, method; mask=nothing) -Return neighbors and distances of point `pₒ` using `method`. -Optionally, specify a `mask` for all indices of the domain. +Update `neighbors` of point `pₒ` using bounded search `method` and return +number of neighbors found. Optionally, specify a `mask` for all indices of +the domain. + +See [`BoundedNeighborSearchMethod`](@ref) for additional details. """ -function searchdists end +function search! end """ - BoundedNeighborSearchMethod + searchdists!(neighbors, distances, pₒ, method; mask=nothing) -A method for searching neighbors with the property that the number of neighbors -is bounded above by a known constant (e.g. k-nearest neighbors). +Update `neighbors` and `distances` of point `pₒ` using bounded search `method` +and return number of neighbors found. Optionally, specify a `mask` for all +indices of the domain. + +See [`BoundedNeighborSearchMethod`](@ref) for additional details. """ -abstract type BoundedNeighborSearchMethod <: NeighborSearchMethod end +function searchdists! end """ - maxneighbors(method) + searchdists(pₒ, method, mask=nothing) -Return the maximum number of neighbors obtained with `method`. +Return neighbors and distances of point `pₒ` using bounded search `method`. +Optionally, specify a `mask` for all indices of the domain. + +See [`BoundedNeighborSearchMethod`](@ref) for additional details. """ -function maxneighbors end +function searchdists end # ---------- # FALLBACKS From e81928ef03bb48afcf13da9f0d5de1c73f64e931 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:39:44 -0300 Subject: [PATCH 408/423] Use `SVector` in `Polytope` instead of `NTuple` (#1130) * Use 'SVector' in 'Polytope' instead of 'NTuple' * Update constructors * Fix constructors * Fix tests --- src/geometries/polytopes.jl | 5 +++-- src/geometries/polytopes/ngon.jl | 8 +++++--- src/transforms.jl | 2 +- test/hulls.jl | 4 ++-- test/meshes.jl | 2 +- test/polytopes.jl | 13 +++++++------ test/testutils.jl | 2 +- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/geometries/polytopes.jl b/src/geometries/polytopes.jl index e8c030610..3785a6d39 100644 --- a/src/geometries/polytopes.jl +++ b/src/geometries/polytopes.jl @@ -32,13 +32,13 @@ macro polytope(type, K, N) structexpr = if K == 3 quote struct $type{C<:CRS,Mₚ<:Manifold} <: Polytope{$K,𝔼{3},C} - vertices::NTuple{$N,Point{Mₚ,C}} + vertices::SVector{$N,Point{Mₚ,C}} end end else quote struct $type{M<:Manifold,C<:CRS} <: Polytope{$K,M,C} - vertices::NTuple{$N,Point{M,C}} + vertices::SVector{$N,Point{M,C}} end end end @@ -46,6 +46,7 @@ macro polytope(type, K, N) expr = quote $Base.@__doc__ $structexpr + $type(vertices::NTuple{$N,P}) where {P<:Point} = $type(SVector(vertices)) $type(vertices::Vararg{Tuple,$N}) = $type(Point.(vertices)) $type(vertices::Vararg{P,$N}) where {P<:Point} = $type(vertices) end diff --git a/src/geometries/polytopes/ngon.jl b/src/geometries/polytopes/ngon.jl index 9d0012a6b..604750d80 100644 --- a/src/geometries/polytopes/ngon.jl +++ b/src/geometries/polytopes/ngon.jl @@ -21,7 +21,7 @@ are `Triangle` (N=3), `Quadrangle` (N=4), `Pentagon` (N=5), etc. `Heptagon`, `Octagon`, `Nonagon`, `Decagon`. """ struct Ngon{N,M<:Manifold,C<:CRS} <: Polygon{M,C} - vertices::NTuple{N,Point{M,C}} + vertices::SVector{N,Point{M,C}} function Ngon{N,M,C}(vertices) where {N,M<:Manifold,C<:CRS} if N < 3 throw(ArgumentError("the number of vertices must be greater than or equal to 3")) @@ -30,11 +30,13 @@ struct Ngon{N,M<:Manifold,C<:CRS} <: Polygon{M,C} end end -Ngon{N}(vertices::NTuple{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon{N}(vertices::SVector{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon{N}(vertices::NTuple{N,P}) where {N,P<:Point} = Ngon{N}(SVector(vertices)) Ngon{N}(vertices::Vararg{P,N}) where {N,P<:Point} = Ngon{N}(vertices) Ngon{N}(vertices::Vararg{Tuple,N}) where {N} = Ngon{N}(Point.(vertices)) -Ngon(vertices::NTuple{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon(vertices::SVector{N,Point{M,C}}) where {N,M<:Manifold,C<:CRS} = Ngon{N,M,C}(vertices) +Ngon(vertices::NTuple{N,P}) where {N,P<:Point} = Ngon(SVector(vertices)) Ngon(vertices::P...) where {P<:Point} = Ngon(vertices) Ngon(vertices::Tuple...) = Ngon(Point.(vertices)) diff --git a/src/transforms.jl b/src/transforms.jl index dfc64048a..be49b250a 100644 --- a/src/transforms.jl +++ b/src/transforms.jl @@ -79,7 +79,7 @@ applycoord(t::CoordinateTransform, g::TransformedGeometry) = TransformedGeometry applycoord(t::CoordinateTransform, m::TransformedMesh) = TransformedMesh(m, t) # special treatment for lists of geometries -applycoord(t::CoordinateTransform, g::NTuple{<:Any,<:Geometry}) = map(gᵢ -> applycoord(t, gᵢ), g) +applycoord(t::CoordinateTransform, g::StaticVector{<:Any,<:Geometry}) = map(gᵢ -> applycoord(t, gᵢ), g) applycoord(t::CoordinateTransform, g::AbstractVector{<:Geometry}) = [applycoord(t, gᵢ) for gᵢ in g] applycoord(t::CoordinateTransform, g::CircularVector{<:Geometry}) = CircularVector([applycoord(t, gᵢ) for gᵢ in g]) diff --git a/test/hulls.jl b/test/hulls.jl index 968c499e8..dfa4476cc 100644 --- a/test/hulls.jl +++ b/test/hulls.jl @@ -90,11 +90,11 @@ # degenerate cases points = [cart(0, 0), cart(1, 0), cart(2, 0)] chull = hull(points, method) - @test vertices(chull) == (cart(0, 0), cart(2, 0)) + @test vertices(chull) == SVector(cart(0, 0), cart(2, 0)) points = [cart(0, 0), cart(1, 0), cart(2, 0), cart(10, 0), cart(100, 0)] chull = hull(points, method) - @test vertices(chull) == (cart(0, 0), cart(100, 0)) + @test vertices(chull) == SVector(cart(0, 0), cart(100, 0)) # partially collinear points = [ diff --git a/test/meshes.jl b/test/meshes.jl index 49f6fcca7..ce1070f8b 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -244,7 +244,7 @@ end @test spacing(grid) == (T(5) * u"m", T(5) * u"m", T(5) * u"m") @test nelements(grid) == 20 * 10 * 5 @test eltype(grid) <: Hexahedron - @test vertices(grid[1]) == ( + @test vertices(grid[1]) == SVector( cart(0, 0, 0), cart(5, 0, 0), cart(5, 5, 0), diff --git a/test/polytopes.jl b/test/polytopes.jl index 6b710f047..8f6a43b26 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -293,13 +293,14 @@ end @testitem "Ngons" setup = [Setup] begin pts = (cart(0, 0), cart(1, 0), cart(0, 1)) tups = (T.((0, 0)), T.((1, 0)), T.((0, 1))) + verts = SVector(pts) @test paramdim(Ngon) == 2 - @test vertices(Ngon(pts)) == pts - @test vertices(Ngon(pts...)) == pts - @test vertices(Ngon(tups...)) == pts - @test vertices(Ngon{3}(pts)) == pts - @test vertices(Ngon{3}(pts...)) == pts - @test vertices(Ngon{3}(tups...)) == pts + @test vertices(Ngon(pts)) == verts + @test vertices(Ngon(pts...)) == verts + @test vertices(Ngon(tups...)) == verts + @test vertices(Ngon{3}(pts)) == verts + @test vertices(Ngon{3}(pts...)) == verts + @test vertices(Ngon{3}(tups...)) == verts NGONS = [Triangle, Quadrangle, Pentagon, Hexagon, Heptagon, Octagon, Nonagon, Decagon] NVERT = 3:10 diff --git a/test/testutils.jl b/test/testutils.jl index d1f2f3530..d12c2f374 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -114,7 +114,7 @@ withprecision(T, v::Vec) = numconvert.(T, v) withprecision(T, p::Point) = Meshes.withcrs(p, withprecision(T, to(p))) withprecision(T, len::Meshes.Len) = numconvert(T, len) withprecision(T, lens::NTuple{Dim,Meshes.Len}) where {Dim} = numconvert.(T, lens) -withprecision(T, geoms::NTuple{Dim,<:Geometry}) where {Dim} = withprecision.(T, geoms) +withprecision(T, geoms::StaticVector{Dim,<:Geometry}) where {Dim} = withprecision.(T, geoms) withprecision(T, geoms::AbstractVector{<:Geometry}) = [withprecision(T, g) for g in geoms] withprecision(T, geoms::CircularVector{<:Geometry}) = CircularVector([withprecision(T, g) for g in geoms]) @generated function withprecision(T, g::G) where {G<:Meshes.GeometryOrDomain} From 2bef7a68e290c1c0c2b6a752117b3d5d82f30436 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Wed, 13 Nov 2024 07:00:20 -0300 Subject: [PATCH 409/423] Remove unnecessary uses of `collect` (#1132) * Remove unnecessary uses of 'collect' * Fix code * Remove comments --- src/projecting.jl | 2 +- src/simplification/douglaspeucker.jl | 2 +- src/simplification/selinger.jl | 2 +- src/transforms/repair.jl | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/projecting.jl b/src/projecting.jl index 0246ba966..6f0e8c46c 100644 --- a/src/projecting.jl +++ b/src/projecting.jl @@ -14,7 +14,7 @@ proj2D(r::Rope) = Ring(proj2D(vertices(r))) proj2D(r::Ring) = Ring(proj2D(vertices(r))) -proj2D(p::Ngon) = Ngon(proj2D(collect(vertices(p)))...) +proj2D(p::Ngon) = Ngon(proj2D(vertices(p))) proj2D(p::PolyArea) = PolyArea(proj2D.(rings(p))) diff --git a/src/simplification/douglaspeucker.jl b/src/simplification/douglaspeucker.jl index 519915203..d6bba5c10 100644 --- a/src/simplification/douglaspeucker.jl +++ b/src/simplification/douglaspeucker.jl @@ -24,7 +24,7 @@ end DouglasPeuckerSimplification(τ) = DouglasPeuckerSimplification(addunit(τ, u"m")) function simplify(chain::Chain, method::DouglasPeuckerSimplification) - verts = _douglaspeucker(vertices(chain), method.τ) |> collect + verts = _douglaspeucker(vertices(chain), method.τ) isclosed(chain) ? Ring(verts) : Rope(verts) end diff --git a/src/simplification/selinger.jl b/src/simplification/selinger.jl index ed155b7a9..856f1d14b 100644 --- a/src/simplification/selinger.jl +++ b/src/simplification/selinger.jl @@ -69,7 +69,7 @@ function simplify(chain::Chain, method::SelingerSimplification) end end - Ring(collect(v[bestpath[begin:(end - 1)]])) + Ring(v[bestpath[begin:(end - 1)]]) end function dijkstra(I, s, t) diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index fc79d239b..e8ef77c58 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -98,8 +98,6 @@ function apply(::Repair{8}, ring::Ring) Ring(v), nothing end -repair8(v) = repair8(collect(v)) - repair8(v::AbstractVector) = repair8(CircularVector(v)) function repair8(v::CircularVector{<:Point}) From efce1a2bf3a394c57d039f6e3b155f48642f69ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 14 Nov 2024 08:42:37 -0300 Subject: [PATCH 410/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 2ac8bb7b4..a9ba050d2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.2" +version = "0.52.3" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 17df47115b5722ed71ff326b052bbf037be7f21e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 06:55:28 -0300 Subject: [PATCH 411/423] Bump codecov/codecov-action from 4 to 5 (#1134) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 37fb1374f..39a024047 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -54,7 +54,7 @@ jobs: env: FLOAT_TYPE: ${{ matrix.float }} - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: file: lcov.info token: ${{ secrets.CODECOV_TOKEN }} From dff33a192bd060c1afa110ed459a6532c5627be2 Mon Sep 17 00:00:00 2001 From: cschen Date: Mon, 18 Nov 2024 21:26:17 +0100 Subject: [PATCH 412/423] Introducing iteration on vertices (#1133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial draft for vertices iteration * added small (temporary) comments for better initial digestion, will remove later * refactor into `eachvertex` * fix: typo in the export list * fix: Multi -> MultiPolytope, no importing of base methods * revert to simple generators. Rewrite some function in term of the iterator instead. * add tests, first part * fix: change test * typo Co-authored-by: Júlio Hoffimann * fix: use first instead of getindex, * revive the custom iterator * add typecheck to avoid stack overflows. * better tests * fix tests * revert Multi constructor * Update src/geometries/multigeom.jl * Update src/geometries/multigeom.jl * Cleanup * *removed double definition of the `eachvertex` function *eltype returns the specific point based on manifold and tranformation. *added missing `IteratorEltype` trait * * improved allocation testing * small cleanup * Update src/geometries/multigeom.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update src/geometries/multigeom.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/meshes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/meshes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/meshes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/meshes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/testutils.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/polytopes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/polytopes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/polytopes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/multigeoms.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Update test/meshes.jl Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> * Apply suggestions from code review * Add more tests * Update tests * Simplify tests * Simplify tests * Apply suggestions * Update test/testutils.jl Co-authored-by: Júlio Hoffimann * Apply suggestions * Fix tests --------- Co-authored-by: Júlio Hoffimann Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Co-authored-by: Elias Carvalho --- src/Meshes.jl | 2 ++ src/domains/meshes.jl | 9 ++++++++- src/geometries/multigeom.jl | 6 ++++-- src/geometries/polytopes.jl | 9 ++++++++- test/meshes.jl | 30 ++++++++++++++++++++++++++++++ test/multigeoms.jl | 16 ++++++++++++++++ test/polytopes.jl | 10 ++++++++++ test/testutils.jl | 14 ++++++++++++++ 8 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Meshes.jl b/src/Meshes.jl index e7b067e91..cf5b60247 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -221,6 +221,7 @@ export vertex, vertices, nvertices, + eachvertex, rings, segments, angles, @@ -313,6 +314,7 @@ export vertex, vertices, nvertices, + eachvertex, element, elements, nelements, diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index f98cad96b..5b3f73381 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -23,7 +23,7 @@ function vertex end Return the vertices of the `mesh`. """ -vertices(m::Mesh) = [vertex(m, ind) for ind in 1:nvertices(m)] +vertices(m::Mesh) = collect(eachvertex(m)) """ nvertices(mesh) @@ -32,6 +32,13 @@ Return the number of vertices of the `mesh`. """ nvertices(m::Mesh) = nvertices(topology(m)) +""" + eachvertex(mesh) + +Return an iterator for the vertices of the `mesh`. +""" +eachvertex(m::Mesh) = (vertex(m, i) for i in 1:nvertices(m)) + """ faces(mesh, rank) diff --git a/src/geometries/multigeom.jl b/src/geometries/multigeom.jl index 451e6f747..19696ae0a 100644 --- a/src/geometries/multigeom.jl +++ b/src/geometries/multigeom.jl @@ -48,12 +48,14 @@ Base.isapprox(m₁::Multi, m₂::Multi; atol=atol(lentype(m₁)), kwargs...) = # POLYTOPE # --------- -vertex(m::MultiPolytope, ind) = vertices(m)[ind] +vertex(m::MultiPolytope, ind) = first(Iterators.drop(eachvertex(m), ind - 1)) -vertices(m::MultiPolytope) = [vertex for geom in m.geoms for vertex in vertices(geom)] +vertices(m::MultiPolytope) = collect(eachvertex(m)) nvertices(m::MultiPolytope) = sum(nvertices, m.geoms) +eachvertex(m::MultiPolytope) = (v for g in m.geoms for v in eachvertex(g)) + Base.unique(m::MultiPolytope) = unique!(deepcopy(m)) function Base.unique!(m::MultiPolytope) diff --git a/src/geometries/polytopes.jl b/src/geometries/polytopes.jl index 3785a6d39..ac16288f9 100644 --- a/src/geometries/polytopes.jl +++ b/src/geometries/polytopes.jl @@ -247,10 +247,17 @@ vertices(p::Polytope) = p.vertices """ nvertices(polytope) -Return the number of vertices in the `polytope`. +Return the number of vertices of the `polytope`. """ nvertices(p::Polytope) = nvertices(typeof(p)) +""" + eachvertex(polytope) + +Return an iterator for the vertices of the `polytope`. +""" +eachvertex(p::Polytope) = (vertex(p, i) for i in 1:nvertices(p)) + """ unique(polytope) diff --git a/test/meshes.jl b/test/meshes.jl index ce1070f8b..031354233 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -127,6 +127,10 @@ @test minimum(sub) == Point(Polar(T(1), T(2))) @test maximum(sub) == Point(Polar(T(4), T(7))) + # vertex iteration + grid = RegularGrid((10, 10), cart(0, 0), T.((1, 1))) + vertextest(grid) + # type stability grid = RegularGrid((10, 20), Point(Polar(T(0), T(0))), T.((1, 1))) @inferred vertex(grid, (1, 1)) @@ -501,6 +505,12 @@ end @test topology(rg) == topology(cg) @test vertices(rg) == vertices(cg) + # vertex iteration + x = range(zero(T), stop=one(T), length=6) + y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] + grid = RectilinearGrid(x, y) + vertextest(grid) + # type stability x = range(zero(T), stop=one(T), length=6) * u"mm" y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"cm" @@ -682,6 +692,12 @@ end @test topology(sg) == topology(rg) @test vertices(sg) == vertices(rg) + # vertex iteration + X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) + Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) + grid = StructuredGrid(X, Y) + vertextest(grid) + # type stability X = repeat(range(zero(T), stop=one(T), length=6), 1, 6) * u"mm" Y = repeat(T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1) * u"cm" @@ -929,6 +945,12 @@ end @test vertex(mesh, 4) == points[4] @test vertex(mesh, 5) == points[5] + # vertex iteration + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + vertextest(mesh) + points = cart.([(0, 0), (1, 0), (0, 1), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) mesh = SimpleMesh(points, connec) @@ -988,6 +1010,14 @@ end trans2 = Translate(T(-10), T(-10)) @test TransformedMesh(TransformedMesh(grid, trans1), trans2) == TransformedMesh(grid, trans1 → trans2) + # vertex iteration + trans = Identity() + points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) + connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) + mesh = SimpleMesh(points, connec) + tmesh = TransformedMesh(mesh, trans) + vertextest(tmesh) + # transforms that change the Manifold and/or CRS points = latlon.([(0, 0), (0, 1), (1, 0), (1, 1), (0.5, 0.5)]) connec = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle) diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 0a585fc38..668582690 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -97,4 +97,20 @@ poly2 = PolyArea(merc.([(1, 1), (2, 1), (2, 2), (1, 2)])) multi = Multi([poly1, poly2]) @test crs(centroid(multi)) === crs(multi) + + # vertex iteration + ring1 = Ring(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + ring2 = Ring(cart.([(0, 0), (2, 0), (2, 2), (0, 2)])) + ring3 = Ring(cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)])) + ring4 = Ring(cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)])) + poly1 = PolyArea(ring1) + poly2 = PolyArea(ring2) + poly3 = PolyArea([ring1, ring3]) + poly4 = PolyArea([ring2, ring4]) + multi1 = Multi([ring1, ring2, ring3, ring4]) + multi2 = Multi([poly1, poly2]) + multi3 = Multi([poly3, poly4]) + vertextest(multi1) + vertextest(multi2) + vertextest(multi3, bytes=3100) end diff --git a/test/polytopes.jl b/test/polytopes.jl index 8f6a43b26..2f3f26ea8 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -42,6 +42,7 @@ s = Segment(cart(0, 0), cart(1, 1)) equaltest(s) isapproxtest(s) + vertextest(s) s = Segment(Point(1.0, 1.0, 1.0, 1.0), Point(2.0, 2.0, 2.0, 2.0)) @test all(Point(x, x, x, x) ∈ s for x in 1:0.01:2) @@ -118,10 +119,12 @@ end c = Rope(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(c) isapproxtest(c) + vertextest(c) c = Ring(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(c) isapproxtest(c) + vertextest(c) # circular equality c1 = Ring(cart.([(1, 1), (2, 2), (3, 3)])) @@ -352,6 +355,7 @@ end t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(t) isapproxtest(t) + vertextest(t) t = Triangle(cart(0, 0), cart(1, 0), cart(0, 1)) @test perimeter(t) ≈ T(1 + 1 + √2) * u"m" @@ -466,6 +470,7 @@ end q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) equaltest(q) isapproxtest(q) + vertextest(q) q = Quadrangle(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) @test_throws DomainError((T(1.2), T(1.2)), "q(u, v) is not defined for u, v outside [0, 1]².") q(T(1.2), T(1.2)) @@ -537,6 +542,7 @@ end p = PolyArea(cart(0, 0), cart(1, 0), cart(0, 1)) equaltest(p) isapproxtest(p) + vertextest(p) # COMMAND USED TO GENERATE TEST FILES (VARY --seed = 1, 2, ..., 5) # rpg --cluster 30 --algo 2opt --format line --seed 1 --output poly1 @@ -721,6 +727,7 @@ end t = Tetrahedron(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1)) equaltest(t) isapproxtest(t) + vertextest(t) # CRS propagation c1 = Cartesian{WGS84Latest}(T(0), T(0), T(0)) @@ -786,6 +793,7 @@ end ) equaltest(h) isapproxtest(h) + vertextest(t) h = Hexahedron( cart(0, 0, 0), @@ -904,6 +912,7 @@ end @test m[5] isa Triangle equaltest(p) isapproxtest(p) + vertextest(p) p = Pyramid(cart(0, 0, 0), cart(1, 0, 0), cart(1, 1, 0), cart(0, 1, 0), cart(0, 0, 1)) @test sprint(show, p) == "Pyramid((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 0.0 m, z: 1.0 m))" @@ -942,6 +951,7 @@ end @test m[5] isa Quadrangle equaltest(w) isapproxtest(w) + vertextest(w) w = Wedge(cart(0, 0, 0), cart(1, 0, 0), cart(0, 1, 0), cart(0, 0, 1), cart(1, 0, 1), cart(0, 1, 1)) @test sprint(show, w) == "Wedge((x: 0.0 m, y: 0.0 m, z: 0.0 m), ..., (x: 0.0 m, y: 1.0 m, z: 1.0 m))" diff --git a/test/testutils.jl b/test/testutils.jl index d12c2f374..9fbcf8f90 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -176,3 +176,17 @@ function _isapproxtest(g::Geometry, ::Val{3}) @test isapprox(g, Translate(0u"m", τ32, 0u"m")(g32), atol=1.1τ32) @test isapprox(g, Translate(0u"m", 0u"m", τ32)(g32), atol=1.1τ32) end + +function eachvertexalloc(g) + iterate(eachvertex(g)) # precompile run + @allocated for _ in eachvertex(g) + end +end + +function vertextest(g; bytes=0) + @test collect(eachvertex(g)) == vertices(g) + @test eachvertexalloc(g) ≤ bytes + # type stability + @test isconcretetype(eltype(vertices(g))) + @inferred vertices(g) +end From 1c6cf7a8ea8611da83604b5af8d18628a12b4b06 Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:26:33 -0300 Subject: [PATCH 413/423] Avoid allocations in `vertex` of `PolyArea` (#1135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Avoid allocations in 'vertex' of 'PolyArea' * Update 'vertextest' * Update src/geometries/polytopes/polyarea.jl Co-authored-by: Júlio Hoffimann * Update 'vertices' of 'PolyArea' --------- Co-authored-by: Júlio Hoffimann --- src/geometries/polytopes/polyarea.jl | 14 +++++++++++++- test/multigeoms.jl | 2 +- test/polytopes.jl | 20 +++++++++++++++++++- test/testutils.jl | 4 ++-- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/geometries/polytopes/polyarea.jl b/src/geometries/polytopes/polyarea.jl index 16c912b90..4980b9f91 100644 --- a/src/geometries/polytopes/polyarea.jl +++ b/src/geometries/polytopes/polyarea.jl @@ -31,7 +31,19 @@ PolyArea(outer...) = PolyArea(collect(outer)) Base.isapprox(p₁::PolyArea, p₂::PolyArea; atol=atol(lentype(p₁)), kwargs...) = length(p₁.rings) == length(p₂.rings) && all(isapprox(r₁, r₂; atol, kwargs...) for (r₁, r₂) in zip(p₁.rings, p₂.rings)) -vertices(p::PolyArea) = mapreduce(vertices, vcat, p.rings) +function vertex(p::PolyArea, ind) + offset = 0 + for r in p.rings + nverts = nvertices(r) + if ind ≤ offset + nverts + return vertex(r, ind - offset) + end + offset += nverts + end + throw(BoundsError(p, ind)) +end + +vertices(p::PolyArea) = collect(eachvertex(p)) nvertices(p::PolyArea) = mapreduce(nvertices, +, p.rings) diff --git a/test/multigeoms.jl b/test/multigeoms.jl index 668582690..5b1dddd6b 100644 --- a/test/multigeoms.jl +++ b/test/multigeoms.jl @@ -112,5 +112,5 @@ multi3 = Multi([poly3, poly4]) vertextest(multi1) vertextest(multi2) - vertextest(multi3, bytes=3100) + vertextest(multi3) end diff --git a/test/polytopes.jl b/test/polytopes.jl index 2f3f26ea8..2a50b8baa 100644 --- a/test/polytopes.jl +++ b/test/polytopes.jl @@ -652,9 +652,27 @@ end @test centroid(poly) == cart(0.5, 0.5) # single vertex access - poly = PolyArea(cart.([(0, 0), (1, 0), (1, 1), (0, 1)])) + outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) + hole1 = cart.([(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.4)]) + hole2 = cart.([(0.6, 0.2), (0.8, 0.2), (0.8, 0.4), (0.6, 0.4)]) + poly = PolyArea([outer, hole1, hole2]) @test vertex(poly, 1) == cart(0, 0) + @test vertex(poly, 2) == cart(1, 0) + @test vertex(poly, 3) == cart(1, 1) @test vertex(poly, 4) == cart(0, 1) + @test vertex(poly, 5) == cart(0.2, 0.2) + @test vertex(poly, 6) == cart(0.4, 0.2) + @test vertex(poly, 7) == cart(0.4, 0.4) + @test vertex(poly, 8) == cart(0.2, 0.4) + @test vertex(poly, 9) == cart(0.6, 0.2) + @test vertex(poly, 10) == cart(0.8, 0.2) + @test vertex(poly, 11) == cart(0.8, 0.4) + @test vertex(poly, 12) == cart(0.6, 0.4) + @test_throws BoundsError vertex(poly, 13) + # type stability + @inferred vertex(poly, 4) + @inferred vertex(poly, 8) + @inferred vertex(poly, 12) # point in polygonal area outer = cart.([(0, 0), (1, 0), (1, 1), (0, 1)]) diff --git a/test/testutils.jl b/test/testutils.jl index 9fbcf8f90..26c233dcf 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -183,9 +183,9 @@ function eachvertexalloc(g) end end -function vertextest(g; bytes=0) +function vertextest(g) @test collect(eachvertex(g)) == vertices(g) - @test eachvertexalloc(g) ≤ bytes + @test eachvertexalloc(g) == 0 # type stability @test isconcretetype(eltype(vertices(g))) @inferred vertices(g) From 9611b0d215dda03efc9d01bd36396afcd5442535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 18 Nov 2024 18:26:50 -0300 Subject: [PATCH 414/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a9ba050d2..a022ec84e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.3" +version = "0.52.4" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From 98f2f6f178aec213c42f17f10d33039fa2ad5b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Nov 2024 14:24:22 -0300 Subject: [PATCH 415/423] Use eachvertex in more places (#1137) --- ext/grid.jl | 2 +- ext/mesh.jl | 2 +- ext/utils.jl | 4 ++-- src/boundingboxes.jl | 4 ++-- src/discretization.jl | 3 +-- src/domains/meshes.jl | 3 ++- src/domains/meshes/transformedmesh.jl | 2 +- src/hulls.jl | 4 ++-- src/pointification.jl | 2 +- src/predicates/intersects.jl | 2 +- src/predicates/isconvex.jl | 8 ++++---- src/transforms/repair.jl | 2 +- src/transforms/smoothing.jl | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ext/grid.jl b/ext/grid.jl index 1ef418180..32c72de49 100644 --- a/ext/grid.jl +++ b/ext/grid.jl @@ -63,7 +63,7 @@ function vizgridfallback!(plot, M, pdim, edim) # decide whether or not to reverse connectivity list rfunc = Makie.@lift _reverse($grid) - verts = Makie.@lift map(asmakie, vertices($grid)) + verts = Makie.@lift map(asmakie, eachvertex($grid)) quads = Makie.@lift [GB.QuadFace($rfunc(indices(e))) for e in elements(topology($grid))] dims = Makie.@lift size($grid) diff --git a/ext/mesh.jl b/ext/mesh.jl index 846999968..0cdc8bdd6 100644 --- a/ext/mesh.jl +++ b/ext/mesh.jl @@ -64,7 +64,7 @@ function vizmesh!(plot, ::Type{<:𝔼}, ::Val{2}, ::Val) dim = embeddim($mesh) nvert = nvertices($mesh) nelem = nelements($mesh) - verts = vertices($mesh) + verts = eachvertex($mesh) topo = topology($mesh) elems = elements(topo) diff --git a/ext/utils.jl b/ext/utils.jl index c39b78a71..59df6ef12 100644 --- a/ext/utils.jl +++ b/ext/utils.jl @@ -34,9 +34,9 @@ asmakie(multis::AbstractVector{<:Multi}) = mapreduce(m -> asmakie.(parent(m)), v function asmakie(poly::Polygon) rs = rings(poly) - outer = [asmakie(p) for p in vertices(first(rs))] + outer = map(asmakie, eachvertex(rs[1])) if hasholes(poly) - inners = map(i -> [asmakie(p) for p in vertices(rs[i])], 2:length(rs)) + inners = map(i -> map(asmakie, eachvertex(rs[i])), 2:length(rs)) Makie.Polygon(outer, inners) else Makie.Polygon(outer) diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 8098b5892..51e824dc0 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -13,7 +13,7 @@ function boundingbox end # FALLBACKS # ---------- -boundingbox(p::Polytope) = _pboxes(vertices(p)) +boundingbox(p::Polytope) = _pboxes(eachvertex(p)) boundingbox(p::Primitive) = boundingbox(boundary(p)) @@ -78,7 +78,7 @@ boundingbox(g::TransformedGrid{<:Any,<:Any,<:OrthoRegularGrid}) = boundingbox(pa boundingbox(g::TransformedGrid{<:Any,<:Any,<:OrthoRectilinearGrid}) = boundingbox(parent(g)) |> transform(g) |> boundingbox -boundingbox(m::Mesh) = _pboxes(vertices(m)) +boundingbox(m::Mesh) = _pboxes(eachvertex(m)) # ---------------- # IMPLEMENTATIONS diff --git a/src/discretization.jl b/src/discretization.jl index f9877848a..143ad3801 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -105,8 +105,7 @@ function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) # handle degenerate polygons if nvertices(cpoly) == 1 - v = first(vertices(cpoly)) - points = [v, v, v] + points = fill(vertex(cpoly, 1), 3) connec = [connect((1, 2, 3))] return SimpleMesh(points, connec) end diff --git a/src/domains/meshes.jl b/src/domains/meshes.jl index 5b3f73381..ee1b78a4b 100644 --- a/src/domains/meshes.jl +++ b/src/domains/meshes.jl @@ -227,8 +227,9 @@ function Base.getindex(g::Grid, I::CartesianIndices) odims = size(g) cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1) inds = vec(LinearIndices(odims .+ 1)[cinds]) + points = [vertex(g, ind) for ind in inds] periodic = isperiodic(topology(g)) .&& dims .== odims - SimpleMesh(vertices(g)[inds], GridTopology(dims, periodic)) + SimpleMesh(points, GridTopology(dims, periodic)) end _asrange(::Int, r::UnitRange{Int}) = r diff --git a/src/domains/meshes/transformedmesh.jl b/src/domains/meshes/transformedmesh.jl index 0c32800b2..170bba589 100644 --- a/src/domains/meshes/transformedmesh.jl +++ b/src/domains/meshes/transformedmesh.jl @@ -18,7 +18,7 @@ struct TransformedMesh{M<:Manifold,C<:CRS,TP<:Topology,MS<:Mesh,TR<:Transform} < end function TransformedMesh(m::Mesh, t::Transform) - p = t(first(vertices(m))) + p = t(vertex(m, 1)) TransformedMesh{manifold(p),crs(p)}(m, t) end diff --git a/src/hulls.jl b/src/hulls.jl index 05b2c9765..1d3611600 100644 --- a/src/hulls.jl +++ b/src/hulls.jl @@ -38,7 +38,7 @@ function convexhull end # FALLBACKS # ---------- -convexhull(p::Polytope) = _pconvexhull(vertices(p)) +convexhull(p::Polytope) = _pconvexhull(eachvertex(p)) convexhull(p::Primitive) = convexhull(boundary(p)) @@ -62,7 +62,7 @@ convexhull(t::Triangle) = t convexhull(g::Grid) = Box(extrema(g)...) -convexhull(m::Mesh) = _pconvexhull(vertices(m)) +convexhull(m::Mesh) = _pconvexhull(eachvertex(m)) # ---------------- # IMPLEMENTATIONS diff --git a/src/pointification.jl b/src/pointification.jl index c45f82164..bcccde16f 100644 --- a/src/pointification.jl +++ b/src/pointification.jl @@ -16,7 +16,7 @@ function pointify end pointify(p::Primitive) = pointify(boundary(p)) -pointify(p::Polytope) = collect(vertices(p)) +pointify(p::Polytope) = collect(eachvertex(p)) pointify(m::Multi) = pointify(parent(m)) diff --git a/src/predicates/intersects.jl b/src/predicates/intersects.jl index 8e0fdf3b6..1f35c7ec9 100644 --- a/src/predicates/intersects.jl +++ b/src/predicates/intersects.jl @@ -61,7 +61,7 @@ intersects(s::Segment, c::Chain) = intersects(c, s) intersects(c₁::Chain, c₂::Chain) = intersects(segments(c₁), segments(c₂)) -intersects(c::Chain, g::Geometry) = any(∈(g), vertices(c)) || intersects(c, boundary(g)) +intersects(c::Chain, g::Geometry) = any(∈(g), eachvertex(c)) || intersects(c, boundary(g)) intersects(g::Geometry, c::Chain) = intersects(c, g) diff --git a/src/predicates/isconvex.jl b/src/predicates/isconvex.jl index 9c4efac41..5bf015718 100644 --- a/src/predicates/isconvex.jl +++ b/src/predicates/isconvex.jl @@ -60,7 +60,7 @@ isconvex(::Tetrahedron) = true isconvex(p::Polygon) = _isconvex(p, Val(embeddim(p))) -_isconvex(p::Polygon, ::Val{2}) = Set(vertices(convexhull(p))) == Set(vertices(p)) +_isconvex(p::Polygon, ::Val{2}) = Set(eachvertex(convexhull(p))) == Set(eachvertex(p)) _isconvex(p::Polygon, ::Val{3}) = isconvex(proj2D(p)) @@ -71,8 +71,8 @@ isconvex(m::Multi) = isapproxequal(measure(convexhull(m)), measure(m)) # -------------- function isconvex(q::Quadrangle) - v = vertices(q) - d1 = Segment(v[1], v[3]) - d2 = Segment(v[2], v[4]) + A, B, C, D = vertices(q) + d1 = Segment(A, C) + d2 = Segment(B, D) intersects(d1, d2) end diff --git a/src/transforms/repair.jl b/src/transforms/repair.jl index e8ef77c58..d315e795f 100644 --- a/src/transforms/repair.jl +++ b/src/transforms/repair.jl @@ -63,7 +63,7 @@ function apply(::Repair{1}, mesh::Mesh) ntuple(i -> inds[elem[i]], length(elem)) end - points = vertices(mesh)[seen] + points = [vertex(mesh, ind) for ind in seen] connec = connect.(elems) diff --git a/src/transforms/smoothing.jl b/src/transforms/smoothing.jl index fdecd8085..f707bf947 100644 --- a/src/transforms/smoothing.jl +++ b/src/transforms/smoothing.jl @@ -44,7 +44,7 @@ _laplacian(mesh) = laplacematrix(mesh, kind=:uniform) function _smooth(mesh, L, n, λ, μ; revert=false) # retrieve vertices - points = vertices(mesh) + points = eachvertex(mesh) # matrix with coordinates (nvertices x ndims) X = reduce(hcat, to.(points)) |> transpose From da27c68c68ae391ec5ab99e973d3424279a0b5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Nov 2024 17:23:08 -0300 Subject: [PATCH 416/423] Refactor collect(vertices(...)) (#1139) --- src/discretization.jl | 6 +++--- src/discretization/dehn.jl | 6 +----- src/discretization/delaunay.jl | 2 +- src/discretization/fan.jl | 2 +- src/discretization/held.jl | 4 ++-- test/discretization.jl | 16 ++++++++++++---- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/discretization.jl b/src/discretization.jl index 143ad3801..6108fba4d 100644 --- a/src/discretization.jl +++ b/src/discretization.jl @@ -152,8 +152,8 @@ function discretize(polygon::Polygon, method::BoundaryTriangulationMethod) end function discretizewithin(ring::Ring, method::BoundaryTriangulationMethod) - # collect vertices to get rid of static containers - points = collect(vertices(ring)) + # retrieve vertices of ring + points = collect(eachvertex(ring)) # discretize within 2D ring with given method ring2D = Ring(_proj2D(manifold(ring), points)) @@ -197,7 +197,7 @@ function simplexify(chain::Chain) np = nvertices(chain) + isclosed(chain) ip = isperiodic(chain) - points = collect(vertices(chain)) + points = collect(eachvertex(chain)) topo = GridTopology((np - 1,), ip) SimpleMesh(points, topo) diff --git a/src/discretization/dehn.jl b/src/discretization/dehn.jl index 40867877c..b04f3b380 100644 --- a/src/discretization/dehn.jl +++ b/src/discretization/dehn.jl @@ -22,12 +22,8 @@ with small number of vertices. struct DehnTriangulation <: BoundaryTriangulationMethod end function discretizewithin(ring::Ring{𝔼{2}}, ::DehnTriangulation) - # points on resulting mesh - points = collect(vertices(ring)) - - # Dehn's recursion + points = collect(eachvertex(ring)) connec = dehn1899(points, 1:length(points)) - SimpleMesh(points, connec) end diff --git a/src/discretization/delaunay.jl b/src/discretization/delaunay.jl index 12e7ebd05..fba17590d 100644 --- a/src/discretization/delaunay.jl +++ b/src/discretization/delaunay.jl @@ -25,7 +25,7 @@ end DelaunayTriangulation(rng=Random.default_rng()) = DelaunayTriangulation(rng) function discretizewithin(ring::Ring{𝔼{2}}, method::DelaunayTriangulation) - points = vertices(ring) + points = collect(eachvertex(ring)) coords = map(p -> ustrip.(to(p)), points) bnodes = [1:nvertices(ring); 1] triang = triangulate(coords, boundary_nodes=bnodes, rng=method.rng) diff --git a/src/discretization/fan.jl b/src/discretization/fan.jl index 196f2e66e..3e4bef8ce 100644 --- a/src/discretization/fan.jl +++ b/src/discretization/fan.jl @@ -12,7 +12,7 @@ See [https://en.wikipedia.org/wiki/Fan_triangulation] struct FanTriangulation <: BoundaryTriangulationMethod end function discretizewithin(ring::Ring, ::FanTriangulation) - points = collect(vertices(ring)) + points = collect(eachvertex(ring)) connec = [connect((1, i, i + 1)) for i in 2:(nvertices(ring) - 1)] SimpleMesh(points, connec) end diff --git a/src/discretization/held.jl b/src/discretization/held.jl index 7e80c31c5..875e9d17a 100644 --- a/src/discretization/held.jl +++ b/src/discretization/held.jl @@ -45,10 +45,10 @@ function discretizewithin(ring::Ring{𝔼{2}}, method::HeldTriangulation) 𝒫 = ℛ |> StdCoords() # points of resulting mesh - points = collect(vertices(ℛ)) + points = collect(eachvertex(ℛ)) # standardized points for algorithm - stdpts = collect(vertices(𝒫)) + stdpts = collect(eachvertex(𝒫)) # keep track of global indices I = CircularVector(1:nvertices(𝒫)) diff --git a/test/discretization.jl b/test/discretization.jl index acd707cd2..0bd88b158 100644 --- a/test/discretization.jl +++ b/test/discretization.jl @@ -8,6 +8,10 @@ @test eltype(mesh) <: Triangle @test vertices(mesh) == pts @test collect(elements(mesh)) == tris + + # type stability tests + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @inferred discretize(poly, FanTriangulation()) end @testitem "DehnTriangulation" setup = [Setup] begin @@ -26,10 +30,6 @@ end @test nelements(mesh) == 6 @test eltype(mesh) <: Triangle - # type stability tests - poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) - @inferred discretize(poly, DehnTriangulation()) - octa = Octagon( cart(0.2, 0.2, 0.0), cart(0.5, -0.5, 0.0), @@ -44,6 +44,10 @@ end @test nvertices(mesh) == 8 @test nelements(mesh) == 6 @test eltype(mesh) <: Triangle + + # type stability tests + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @inferred discretize(poly, DehnTriangulation()) end @testitem "HeldTriangulation" setup = [Setup] begin @@ -128,6 +132,10 @@ end mesh = discretize(poly, HeldTriangulation(rng)) @test nvertices(mesh) == 5 @test nelements(mesh) == 3 + + # type stability tests + poly = PolyArea(cart(0, 0), cart(1, 0), cart(1, 1), cart(0, 1)) + @inferred discretize(poly, HeldTriangulation()) end @testitem "DelaunayTriangulation" setup = [Setup] begin From a2ea3b9e6bc4cc7e95a487d08b213d4671f4d1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 19 Nov 2024 17:32:00 -0300 Subject: [PATCH 417/423] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a022ec84e..13ab98393 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.4" +version = "0.52.5" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From f45744d983b87c567032df5fe266a24e31a41fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 20 Nov 2024 09:53:54 -0300 Subject: [PATCH 418/423] Fix isapprox for Line (#1141) --- src/geometries/primitives/line.jl | 3 ++- src/geometries/primitives/plane.jl | 4 ++-- test/primitives.jl | 12 ++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/geometries/primitives/line.jl b/src/geometries/primitives/line.jl index 14e895c60..97fbd8c1f 100644 --- a/src/geometries/primitives/line.jl +++ b/src/geometries/primitives/line.jl @@ -21,6 +21,7 @@ paramdim(::Type{<:Line}) = 1 ==(l₁::Line, l₂::Line) = l₁.a ∈ l₂ && l₁.b ∈ l₂ && l₂.a ∈ l₁ && l₂.b ∈ l₁ Base.isapprox(l₁::Line, l₂::Line; atol=atol(lentype(l₁)), kwargs...) = - isapprox(l₁.a, l₂.a; atol, kwargs...) && isapprox(l₁.b, l₂.b; atol, kwargs...) + isapproxzero(norm(ucross(l₁.b - l₁.a, l₂.b - l₂.a)); atol, kwargs...) && + isapproxzero(norm(ucross(l₁.b - l₂.a, l₂.b - l₂.a)); atol, kwargs...) (l::Line)(t) = coordsum((l.a, l.b), weights=((1 - t), t)) diff --git a/src/geometries/primitives/plane.jl b/src/geometries/primitives/plane.jl index 1c539a4f2..0be6b6434 100644 --- a/src/geometries/primitives/plane.jl +++ b/src/geometries/primitives/plane.jl @@ -44,8 +44,8 @@ normal(p::Plane) = unormalize(ucross(p.u, p.v)) p₁(0, 0) ∈ p₂ && p₁(1, 0) ∈ p₂ && p₁(0, 1) ∈ p₂ && p₂(0, 0) ∈ p₁ && p₂(1, 0) ∈ p₁ && p₂(0, 1) ∈ p₁ Base.isapprox(p₁::Plane, p₂::Plane; atol=atol(lentype(p₁)), kwargs...) = + isapproxzero(norm(ucross(normal(p₁), normal(p₂))); atol, kwargs...) && isapproxzero(udot(p₁(0, 0) - p₂(0, 0), normal(p₂)); atol, kwargs...) && - isapproxzero(udot(p₂(0, 0) - p₁(0, 0), normal(p₁)); atol, kwargs...) && - isapproxzero(norm(ucross(normal(p₁), normal(p₂))); atol, kwargs...) + isapproxzero(udot(p₂(0, 0) - p₁(0, 0), normal(p₁)); atol, kwargs...) (p::Plane)(u, v) = p.p + u * p.u + v * p.v diff --git a/test/primitives.jl b/test/primitives.jl index bc3c36d58..1ef66b29d 100644 --- a/test/primitives.jl +++ b/test/primitives.jl @@ -276,6 +276,18 @@ end @test l(T(0.75)) == latlon(45, 67.5) @test l(T(1)) == latlon(45, 90) + # https://github.com/JuliaGeometry/Meshes.jl/issues/1138 + l1 = Line(cart(0, 0), cart(1, 0)) + l2 = Line(cart(0, 0), cart(2, 0)) + l3 = Line(cart(0, 0), cart(-1, 0)) + l4 = Line(cart(0, 0), cart(0, 1)) + l5 = Line(cart(0, 0), cart(0, 2)) + l6 = Line(cart(0, 0), cart(0, -1)) + @test l1 == l2 == l3 + @test l1 ≈ l2 ≈ l3 + @test l4 == l5 == l6 + @test l4 ≈ l5 ≈ l6 + l = Line(cart(0, 0), cart(1, 1)) @test sprint(show, l) == "Line(a: (x: 0.0 m, y: 0.0 m), b: (x: 1.0 m, y: 1.0 m))" if T === Float32 From e9b6fde24a23b4001c213d7cfbf6f60f46a35c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 20 Nov 2024 09:54:17 -0300 Subject: [PATCH 419/423] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 13ab98393..0f05c7249 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Meshes" uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" authors = ["Júlio Hoffimann and contributors"] -version = "0.52.5" +version = "0.52.6" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" From ffd4fb7cc7a61748a80dc0435a1ff09628e2c0df Mon Sep 17 00:00:00 2001 From: souma4 Date: Thu, 21 Nov 2024 13:47:16 -0700 Subject: [PATCH 420/423] new branch for Bentley Ottman --- src/utils/sweepline.jl | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/utils/sweepline.jl diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl new file mode 100644 index 000000000..7a148986b --- /dev/null +++ b/src/utils/sweepline.jl @@ -0,0 +1,76 @@ +# Implementation of Bentley-Ottmann algorith +# https://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm + +using BinaryTrees + + +""" + bentleyottmann(segments) + +Compute pairwise intersections between n `segments` +in O(n⋅log(n)) time using Bentley-Ottmann sweep line +algorithm. +""" +function bentleyottmann(segments) + # adjust vertices of segments + segs = map(segments) do s + a, b = extrema(s) + a > b ? reverse(s) : s + end + + # retrieve relevant info + s = first(segs) + p = minimum(s) + P = typeof(p) + S = Tuple{P,P} + + # initialization + 𝒬 = AVLTree{P}() + 𝒯 = AVLTree{S}() + ℒ = Dict{P,Vector{S}}() + 𝒰 = Dict{P,Vector{S}}() + 𝒞 = Dict{P,Vector{S}}() + for s in segs + a, b = extrema(s) + BinaryTrees.insert!(𝒬, a) + BinaryTrees.insert!(𝒬, b) + haskey(ℒ, a) ? push!(ℒ[a], (a, b)) : (ℒ[a] = [(a, b)]) + haskey(𝒰, b) ? push!(𝒰[b], (a, b)) : (𝒰[b] = [(a, b)]) + haskey(ℒ, b) || (ℒ[b] = S[]) + haskey(𝒰, a) || (𝒰[a] = S[]) + haskey(𝒞, a) || (𝒞[a] = S[]) + haskey(𝒞, b) || (𝒞[b] = S[]) + end + m = Point(-Inf, -Inf) + M = Point(Inf, Inf) + BinaryTrees.insert!(𝒯, (m, m)) + BinaryTrees.insert!(𝒯, (M, M)) + + # sweep line + I = Dict{P,Vector{S}}() + while !isnothing(BinaryTrees.root(𝒬)) + p = _key(BinaryTrees.root(𝒬)) + BinaryTrees.delete!(𝒬, p) + handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) + end + I +end + +function handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) + ss = ℒ[p] ∪ 𝒰[p] ∪ 𝒞[p] + if length(ss) > 1 + I[p] = ss + end + for s in ℒ[p] ∪ 𝒞[p] + BinaryTrees.delete!(𝒯, s) + end + for s in 𝒰[p] ∪ 𝒞[p] + BinaryTrees.insert!(𝒯, s) + end + if length(𝒰[p] ∪ 𝒞[p]) == 0 + # n = BinaryTrees.search(𝒯, p) + else + end +end + +_key(node::BinaryTrees.AVLNode) = node.key \ No newline at end of file From 1c3f2499ed8dc11d1ed19393addefd970fbbc301 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 29 Dec 2024 15:16:01 -0700 Subject: [PATCH 421/423] updating sweepline algorithm with AVLTrees, needs more work --- src/utils/sweepline.jl | 74 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 7a148986b..ddfb7429f 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -25,8 +25,8 @@ function bentleyottmann(segments) S = Tuple{P,P} # initialization - 𝒬 = AVLTree{P}() - 𝒯 = AVLTree{S}() + 𝒬 = BinaryTrees.AVLTree{P}() + 𝒯 = BinaryTrees.AVLTree{S}() ℒ = Dict{P,Vector{S}}() 𝒰 = Dict{P,Vector{S}}() 𝒞 = Dict{P,Vector{S}}() @@ -57,20 +57,72 @@ function bentleyottmann(segments) end function handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) - ss = ℒ[p] ∪ 𝒰[p] ∪ 𝒞[p] - if length(ss) > 1 - I[p] = ss + # Segments that start, end, or intersect at p + start_segments = ℒ[p] + end_segments = 𝒰[p] + intersection_segments = 𝒞[p] + + # If there are multiple segments intersecting at p, record the intersection + if length(start_segments ∪ end_segments ∪ intersection_segments) > 1 + I[p] = start_segments ∪ end_segments ∪ intersection_segments end - for s in ℒ[p] ∪ 𝒞[p] + + # Remove segments that end at p from the status structure + for s in end_segments ∪ intersection_segments BinaryTrees.delete!(𝒯, s) end - for s in 𝒰[p] ∪ 𝒞[p] + + # Insert segments that start at p into the status structure + for s in start_segments ∪ intersection_segments BinaryTrees.insert!(𝒯, s) end - if length(𝒰[p] ∪ 𝒞[p]) == 0 - # n = BinaryTrees.search(𝒯, p) - else + node = BinaryTrees.root(𝒬) + + # Find new event points caused by the insertion or deletion of segments + for s in start_segments + s = Segment(s) + pred = BinaryTrees.left(node) + succ = BinaryTrees.right(node) + ns = Segment(pred, succ) + if pred !== nothing + new_geom, new_type = _newevent(s, ns) + if new_geom == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + end + end + if succ !== nothing + new_geom, new_type = _newevent(s, ns) + if new_type == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + end + end + end + + for s in end_segments + s = Segment(s) + pred = BinaryTrees.left(node) + succ = BinaryTrees.right(node) + ns = Segment(pred, succ) + if pred !== nothing && succ !== nothing + new_geom, new_type = _newevent(s, ns) + if new_type == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + end + end end end -_key(node::BinaryTrees.AVLNode) = node.key \ No newline at end of file +_key(node::BinaryTrees.AVLNode) = node.key +_geom(intersect::Intersection) = intersect.geom +_type(intersect::Intersection) = intersect.type +function _newevent(s₁::Segment, s₂::Segment) + new_event = intersection(s₁, s₂) + _geom(new_event), _type(new_event) +end + + +function Segment(node₁::BinaryTrees.BinaryNode, node₂::BinaryTrees.BinaryNode) + node₁ = _key(node₁) + node₂ = _key(node₂) + Segment((node₁, node₂)) +end From 692843199764e92ef6e5bd3b8e3d7dc915bfa53f Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 10 Feb 2025 14:42:33 -0700 Subject: [PATCH 422/423] Major commit to BentleyOttman Algorithm. I managed to get a dictionary of all vertices and the segments related to that point. There are three ways of handling this in my opinion. 1. return the dictionary and build a separate method that reconstructs the polygon 2. return all points, build a new polygon based on that (dubious on this one) 3. build the polygon with new intersections. I lean the first one because if we want to implement this into a clip algorithm, we'd need the dictionary of vertices so we can track which points belong to which polygon. This would enable two utility methods. One that takes the dictionary ofsegments and makes one polygon, then one that takes dictionary of segments + both polygons to reconstruct each polygon --- src/utils/sweepline.jl | 182 ++++++++++++++++++++++++++++++++--------- test/utils.jl | 23 ++++++ 2 files changed, 168 insertions(+), 37 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index ddfb7429f..27425e241 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -3,7 +3,6 @@ using BinaryTrees - """ bentleyottmann(segments) @@ -41,15 +40,11 @@ function bentleyottmann(segments) haskey(𝒞, a) || (𝒞[a] = S[]) haskey(𝒞, b) || (𝒞[b] = S[]) end - m = Point(-Inf, -Inf) - M = Point(Inf, Inf) - BinaryTrees.insert!(𝒯, (m, m)) - BinaryTrees.insert!(𝒯, (M, M)) # sweep line I = Dict{P,Vector{S}}() while !isnothing(BinaryTrees.root(𝒬)) - p = _key(BinaryTrees.root(𝒬)) + p = _key(_leftmost(BinaryTrees.root(𝒬))) BinaryTrees.delete!(𝒬, p) handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) end @@ -57,70 +52,183 @@ function bentleyottmann(segments) end function handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) - # Segments that start, end, or intersect at p - start_segments = ℒ[p] - end_segments = 𝒰[p] - intersection_segments = 𝒞[p] + start_segments = get(ℒ, p, S[]) + end_segments = get(𝒰, p, S[]) + intersection_segments = get(𝒞, p, S[]) + _process_start_segments!(start_segments, 𝒬, 𝒯, 𝒞) + _process_end_segments!(end_segments, 𝒬, 𝒯, 𝒞) + _process_intersection_segments!(intersection_segments, 𝒬, 𝒯, 𝒞) - # If there are multiple segments intersecting at p, record the intersection if length(start_segments ∪ end_segments ∪ intersection_segments) > 1 I[p] = start_segments ∪ end_segments ∪ intersection_segments end +end - # Remove segments that end at p from the status structure - for s in end_segments ∪ intersection_segments - BinaryTrees.delete!(𝒯, s) - end - - # Insert segments that start at p into the status structure - for s in start_segments ∪ intersection_segments - BinaryTrees.insert!(𝒯, s) - end - node = BinaryTrees.root(𝒬) - - # Find new event points caused by the insertion or deletion of segments +function _process_start_segments!(start_segments, 𝒬, 𝒯, 𝒞) + [BinaryTrees.insert!(𝒯, s) for s in start_segments] for s in start_segments + above, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) s = Segment(s) - pred = BinaryTrees.left(node) - succ = BinaryTrees.right(node) - ns = Segment(pred, succ) - if pred !== nothing - new_geom, new_type = _newevent(s, ns) - if new_geom == IntersectionType(0) + if above !== nothing && below !== nothing + new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) + if new_type == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(above), _key(below)) : (𝒞[new_geom] = [_key(above), _key(below)]) end end - if succ !== nothing - new_geom, new_type = _newevent(s, ns) + if below !== nothing + new_geom, new_type = _newevent(Segment(_key(below)), s) if new_type == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(below), _segdata(s)) : (𝒞[new_geom] = [_key(below), _segdata(s)]) + end + end + if above !== nothing + new_geom, new_type = _newevent(s, Segment(_key(above))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _segdata(s), _key(above)) : (𝒞[new_geom] = [_segdata(s), _key(above)]) end end end +end +function _process_end_segments!(end_segments, 𝒬, 𝒯, 𝒞) for s in end_segments + above, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) + BinaryTrees.delete!(𝒯, s) s = Segment(s) - pred = BinaryTrees.left(node) - succ = BinaryTrees.right(node) - ns = Segment(pred, succ) - if pred !== nothing && succ !== nothing - new_geom, new_type = _newevent(s, ns) + if above !== nothing && below !== nothing + new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) if new_type == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(above), _key(below)) : (𝒞[new_geom] = [_key(above), _key(below)]) end end end end +function _process_intersection_segments!(intersection_segments, 𝒬, 𝒯, 𝒞) + for s in intersection_segments + _, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) + if below !== nothing + # Swap positions of s and t in 𝒯 + BinaryTrees.delete!(𝒯, s) + BinaryTrees.delete!(𝒯, _key(below)) + BinaryTrees.insert!(𝒯, _key(below)) + BinaryTrees.insert!(𝒯, s) + + # Find segments r and u + _, r = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, _key(below))) + u, _ = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) + + # Remove crossing points rs and tu from event queue + if r !== nothing + new_geom, new_type = _newevent(Segment(_key(r)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.delete!(𝒬, new_geom) + end + end + if u !== nothing + new_geom, new_type = _newevent(Segment(_key(u)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.delete!(𝒬, new_geom) + end + end + + # Add crossing points rt and su to event queue + if r !== nothing + new_geom, new_type = _newevent(Segment(_key(r)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(r), _key(below)) : (𝒞[new_geom] = [_key(r), _key(below)]) + end + end + if u !== nothing + new_geom, new_type = _newevent(Segment(_key(u)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.insert!(𝒬, new_geom) + haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(u), s) : (𝒞[new_geom] = [_key(u), s]) + end + end + end + end +end + +_segdata(seg::Segment) = seg.vertices.data _key(node::BinaryTrees.AVLNode) = node.key +_key(node::Nothing) = nothing +_leftmost(node::BinaryTrees.AVLNode) = node.left === nothing ? node : _leftmost(node.left) _geom(intersect::Intersection) = intersect.geom _type(intersect::Intersection) = intersect.type function _newevent(s₁::Segment, s₂::Segment) new_event = intersection(s₁, s₂) - _geom(new_event), _type(new_event) + if new_event !== nothing + _geom(new_event), _type(new_event) + else + nothing, nothing + end +end + +# Helper: return the leftmost node (minimum) in a subtree. +function _bst_minimum(node::BinaryTrees.AVLNode) + while node.left !== nothing + node = node.left + end + return node end +# Helper: return the rightmost node (maximum) in a subtree. +function _bst_maximum(node::BinaryTrees.AVLNode) + while node.right !== nothing + node = node.right + end + return node +end + +""" + find_above_below(root, x) + +Find the node above and below `x` in the binary search tree rooted at `root`. +Returns a tuple `(above, below)` where `above` is the node with the smallest key +greater than `x.key` and `below` is the node with the largest key smaller than `x.key`. +If `x` is not found, returns the best candidates for `above` and `below`. +""" +function find_above_below(root::BinaryTrees.AVLNode, x::BinaryTrees.AVLNode) + above = nothing + below = nothing + current = root + # Traverse from the root to the target node, updating candidates. + while current !== nothing && current.key != x.key + if x.key < current.key + # current is a potential above (successor) + above = current + current = current.left + else # x.key > current.key + # current is a potential below (predecessor) + below = current + current = current.right + end + end + + # If the node wasn't found, return the best candidate values + if current === nothing + return (above, below) + end + # Found the node with key equal to x.key. + # Now, if there is a left subtree, the true below (predecessor) is the maximum in that subtree. + if current.left !== nothing + below = _bst_maximum(current.left) + end + # Similarly, if there is a right subtree, the true above (successor) is the minimum in that subtree. + if current.right !== nothing + above = _bst_minimum(current.right) + end + + (above, below) +end +find_above_below(root::BinaryTrees.AVLNode, x::Nothing) = (nothing, nothing) function Segment(node₁::BinaryTrees.BinaryNode, node₂::BinaryTrees.BinaryNode) node₁ = _key(node₁) node₂ = _key(node₂) diff --git a/test/utils.jl b/test/utils.jl index 825897847..c766e3de4 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -54,4 +54,27 @@ c = (T(30), T(60)) p = latlon(c) |> Proj(Cartesian) @inferred Meshes.withcrs(p, c, LatLon) + + # test bentley-ottmann algorithm + S = [ + Segment(cart(0, 0), cart(1, 1)), + Segment(cart(1, 0), cart(0, 1)), + Segment(cart(0, 0), cart(0, 1)), + Segment(cart(0, 0), cart(1, 0)), + Segment(cart(0, 1), cart(1, 1)), + Segment(cart(1, 0), cart(1, 1)) + ] + I = bentleyottmann(S) + # new vertices + NS = [ + Segment(cart(0, 0), cart(0.5, 0.5)), + Segment(cart(0.5, 0.5), cart(1, 1)), + Segment(cart(1, 0), cart(0, 1)), + Segment(cart(1, 0), cart(0.5, 0.5)), + Segment(cart(0.5, 0.5), cart(0, 1)), + Segment(cart(0, 0), cart(0, 1)), + Segment(cart(0, 0), cart(1, 0)), + Segment(cart(1, 0), cart(1, 1)) + ] + @test I == NS end From 31f65e86f15524f6976001389e3a9295d103b909 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Feb 2025 09:44:54 -0700 Subject: [PATCH 423/423] Revert "Major commit to BentleyOttman Algorithm. I managed to get a dictionary of all vertices and the segments related to that point. There are three ways of handling this in my opinion. 1. return the dictionary and build a separate method that reconstructs the polygon 2. return all points, build a new polygon based on that (dubious on this one) 3. build the polygon with new intersections. I lean the first one because if we want to implement this into a clip algorithm, we'd need the dictionary of vertices so we can track which points belong to which polygon. This would enable two utility methods. One that takes the dictionary ofsegments and makes one polygon, then one that takes dictionary of segments + both polygons to reconstruct each polygon" This reverts commit 692843199764e92ef6e5bd3b8e3d7dc915bfa53f. --- src/utils/sweepline.jl | 182 +++++++++-------------------------------- test/utils.jl | 23 ------ 2 files changed, 37 insertions(+), 168 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 27425e241..ddfb7429f 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -3,6 +3,7 @@ using BinaryTrees + """ bentleyottmann(segments) @@ -40,11 +41,15 @@ function bentleyottmann(segments) haskey(𝒞, a) || (𝒞[a] = S[]) haskey(𝒞, b) || (𝒞[b] = S[]) end + m = Point(-Inf, -Inf) + M = Point(Inf, Inf) + BinaryTrees.insert!(𝒯, (m, m)) + BinaryTrees.insert!(𝒯, (M, M)) # sweep line I = Dict{P,Vector{S}}() while !isnothing(BinaryTrees.root(𝒬)) - p = _key(_leftmost(BinaryTrees.root(𝒬))) + p = _key(BinaryTrees.root(𝒬)) BinaryTrees.delete!(𝒬, p) handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) end @@ -52,183 +57,70 @@ function bentleyottmann(segments) end function handle!(I, p, 𝒬, 𝒯, ℒ, 𝒰, 𝒞) - start_segments = get(ℒ, p, S[]) - end_segments = get(𝒰, p, S[]) - intersection_segments = get(𝒞, p, S[]) - _process_start_segments!(start_segments, 𝒬, 𝒯, 𝒞) - _process_end_segments!(end_segments, 𝒬, 𝒯, 𝒞) - _process_intersection_segments!(intersection_segments, 𝒬, 𝒯, 𝒞) + # Segments that start, end, or intersect at p + start_segments = ℒ[p] + end_segments = 𝒰[p] + intersection_segments = 𝒞[p] + # If there are multiple segments intersecting at p, record the intersection if length(start_segments ∪ end_segments ∪ intersection_segments) > 1 I[p] = start_segments ∪ end_segments ∪ intersection_segments end -end -function _process_start_segments!(start_segments, 𝒬, 𝒯, 𝒞) - [BinaryTrees.insert!(𝒯, s) for s in start_segments] + # Remove segments that end at p from the status structure + for s in end_segments ∪ intersection_segments + BinaryTrees.delete!(𝒯, s) + end + + # Insert segments that start at p into the status structure + for s in start_segments ∪ intersection_segments + BinaryTrees.insert!(𝒯, s) + end + node = BinaryTrees.root(𝒬) + + # Find new event points caused by the insertion or deletion of segments for s in start_segments - above, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) s = Segment(s) - if above !== nothing && below !== nothing - new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) - if new_type == IntersectionType(0) + pred = BinaryTrees.left(node) + succ = BinaryTrees.right(node) + ns = Segment(pred, succ) + if pred !== nothing + new_geom, new_type = _newevent(s, ns) + if new_geom == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(above), _key(below)) : (𝒞[new_geom] = [_key(above), _key(below)]) end end - if below !== nothing - new_geom, new_type = _newevent(Segment(_key(below)), s) + if succ !== nothing + new_geom, new_type = _newevent(s, ns) if new_type == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(below), _segdata(s)) : (𝒞[new_geom] = [_key(below), _segdata(s)]) - end - end - if above !== nothing - new_geom, new_type = _newevent(s, Segment(_key(above))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _segdata(s), _key(above)) : (𝒞[new_geom] = [_segdata(s), _key(above)]) end end end -end -function _process_end_segments!(end_segments, 𝒬, 𝒯, 𝒞) for s in end_segments - above, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) - BinaryTrees.delete!(𝒯, s) s = Segment(s) - if above !== nothing && below !== nothing - new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) + pred = BinaryTrees.left(node) + succ = BinaryTrees.right(node) + ns = Segment(pred, succ) + if pred !== nothing && succ !== nothing + new_geom, new_type = _newevent(s, ns) if new_type == IntersectionType(0) BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(above), _key(below)) : (𝒞[new_geom] = [_key(above), _key(below)]) end end end end -function _process_intersection_segments!(intersection_segments, 𝒬, 𝒯, 𝒞) - for s in intersection_segments - _, below = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) - if below !== nothing - # Swap positions of s and t in 𝒯 - BinaryTrees.delete!(𝒯, s) - BinaryTrees.delete!(𝒯, _key(below)) - BinaryTrees.insert!(𝒯, _key(below)) - BinaryTrees.insert!(𝒯, s) - - # Find segments r and u - _, r = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, _key(below))) - u, _ = find_above_below(BinaryTrees.root(𝒯), BinaryTrees.search(𝒯, s)) - - # Remove crossing points rs and tu from event queue - if r !== nothing - new_geom, new_type = _newevent(Segment(_key(r)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.delete!(𝒬, new_geom) - end - end - if u !== nothing - new_geom, new_type = _newevent(Segment(_key(u)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.delete!(𝒬, new_geom) - end - end - - # Add crossing points rt and su to event queue - if r !== nothing - new_geom, new_type = _newevent(Segment(_key(r)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(r), _key(below)) : (𝒞[new_geom] = [_key(r), _key(below)]) - end - end - if u !== nothing - new_geom, new_type = _newevent(Segment(_key(u)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.insert!(𝒬, new_geom) - haskey(𝒞, new_geom) ? push!(𝒞[new_geom], _key(u), s) : (𝒞[new_geom] = [_key(u), s]) - end - end - end - end -end - -_segdata(seg::Segment) = seg.vertices.data _key(node::BinaryTrees.AVLNode) = node.key -_key(node::Nothing) = nothing -_leftmost(node::BinaryTrees.AVLNode) = node.left === nothing ? node : _leftmost(node.left) _geom(intersect::Intersection) = intersect.geom _type(intersect::Intersection) = intersect.type function _newevent(s₁::Segment, s₂::Segment) new_event = intersection(s₁, s₂) - if new_event !== nothing - _geom(new_event), _type(new_event) - else - nothing, nothing - end -end - -# Helper: return the leftmost node (minimum) in a subtree. -function _bst_minimum(node::BinaryTrees.AVLNode) - while node.left !== nothing - node = node.left - end - return node + _geom(new_event), _type(new_event) end -# Helper: return the rightmost node (maximum) in a subtree. -function _bst_maximum(node::BinaryTrees.AVLNode) - while node.right !== nothing - node = node.right - end - return node -end - -""" - find_above_below(root, x) - -Find the node above and below `x` in the binary search tree rooted at `root`. -Returns a tuple `(above, below)` where `above` is the node with the smallest key -greater than `x.key` and `below` is the node with the largest key smaller than `x.key`. -If `x` is not found, returns the best candidates for `above` and `below`. -""" -function find_above_below(root::BinaryTrees.AVLNode, x::BinaryTrees.AVLNode) - above = nothing - below = nothing - current = root - # Traverse from the root to the target node, updating candidates. - while current !== nothing && current.key != x.key - if x.key < current.key - # current is a potential above (successor) - above = current - current = current.left - else # x.key > current.key - # current is a potential below (predecessor) - below = current - current = current.right - end - end - - # If the node wasn't found, return the best candidate values - if current === nothing - return (above, below) - end - # Found the node with key equal to x.key. - # Now, if there is a left subtree, the true below (predecessor) is the maximum in that subtree. - if current.left !== nothing - below = _bst_maximum(current.left) - end - # Similarly, if there is a right subtree, the true above (successor) is the minimum in that subtree. - if current.right !== nothing - above = _bst_minimum(current.right) - end - - (above, below) -end -find_above_below(root::BinaryTrees.AVLNode, x::Nothing) = (nothing, nothing) function Segment(node₁::BinaryTrees.BinaryNode, node₂::BinaryTrees.BinaryNode) node₁ = _key(node₁) node₂ = _key(node₂) diff --git a/test/utils.jl b/test/utils.jl index c766e3de4..825897847 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -54,27 +54,4 @@ c = (T(30), T(60)) p = latlon(c) |> Proj(Cartesian) @inferred Meshes.withcrs(p, c, LatLon) - - # test bentley-ottmann algorithm - S = [ - Segment(cart(0, 0), cart(1, 1)), - Segment(cart(1, 0), cart(0, 1)), - Segment(cart(0, 0), cart(0, 1)), - Segment(cart(0, 0), cart(1, 0)), - Segment(cart(0, 1), cart(1, 1)), - Segment(cart(1, 0), cart(1, 1)) - ] - I = bentleyottmann(S) - # new vertices - NS = [ - Segment(cart(0, 0), cart(0.5, 0.5)), - Segment(cart(0.5, 0.5), cart(1, 1)), - Segment(cart(1, 0), cart(0, 1)), - Segment(cart(1, 0), cart(0.5, 0.5)), - Segment(cart(0.5, 0.5), cart(0, 1)), - Segment(cart(0, 0), cart(0, 1)), - Segment(cart(0, 0), cart(1, 0)), - Segment(cart(1, 0), cart(1, 1)) - ] - @test I == NS end