Skip to content

Commit 9dfe3a2

Browse files
authoredJun 13, 2024
Add Shadow transform (#885)
* Add 'Shadow' transform * Apply suggestions * Add tests * Add comments * Update tests * Apply suggestions * Apply suggestions * Apply suggestions * Apply suggestions
·
v0.54.6v0.45.0
1 parent b037009 commit 9dfe3a2

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed
 

‎src/Meshes.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ export
513513
StdCoords,
514514
Proj,
515515
LengthUnit,
516+
Shadow,
516517
Repair,
517518
Bridge,
518519
LambdaMuSmoothing,

‎src/transforms.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ include("transforms/stretch.jl")
9797
include("transforms/stdcoords.jl")
9898
include("transforms/proj.jl")
9999
include("transforms/lengthunit.jl")
100+
include("transforms/shadow.jl")
100101
include("transforms/repair.jl")
101102
include("transforms/bridge.jl")
102103
include("transforms/smoothing.jl")

‎src/transforms/shadow.jl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# ------------------------------------------------------------------
2+
# Licensed under the MIT License. See LICENSE in the project root.
3+
# ------------------------------------------------------------------
4+
5+
function _index(d)
6+
if d == 'x'
7+
1
8+
elseif d == 'y'
9+
2
10+
elseif d == 'z'
11+
3
12+
else
13+
throw(ArgumentError("'$d' isn't a valid dimension name"))
14+
end
15+
end
16+
17+
"""
18+
Shadow(dims)
19+
20+
Project the geometry or domain onto the given `dims`,
21+
producing a "shadow" of the original object.
22+
23+
## Examples
24+
25+
```julia
26+
Shadow(:xy)
27+
Shadow("xz")
28+
Shadow(1, 2)
29+
Shadow((1, 3))
30+
```
31+
"""
32+
struct Shadow{Dim} <: GeometricTransform
33+
dims::Dims{Dim}
34+
end
35+
36+
Shadow(dims::Int...) = Shadow(dims)
37+
38+
Shadow(dims::AbstractString) = Shadow(Dims(_index(d) for d in dims))
39+
40+
Shadow(dims::Symbol) = Shadow(string(dims))
41+
42+
parameters(t::Shadow) = (; dims=t.dims)
43+
44+
apply(t::Shadow, v::Vec) = _shadow(v, _sort(t.dims)), nothing
45+
46+
apply(t::Shadow, g::GeometryOrDomain) = _shadow(g, _sort(t.dims)), nothing
47+
48+
_sort(dims) = sort(SVector(dims))
49+
50+
_shadow(v::Vec, dims) = v[dims]
51+
52+
_shadow(p::Point, dims) = withdatum(p, to(p)[dims])
53+
54+
function _shadow(g::CartesianGrid, dims)
55+
sz = size(g)[dims]
56+
or = _shadow(minimum(g), dims)
57+
sp = spacing(g)[dims]
58+
of = offset(g)[dims]
59+
CartesianGrid(sz, or, sp, of)
60+
end
61+
62+
_shadow(g::RectilinearGrid, dims) = RectilinearGrid{datum(crs(g))}(xyz(g)[dims])
63+
64+
function _shadow(g::StructuredGrid, dims)
65+
ndims = length(size(g))
66+
inds = ntuple(i -> ifelse(i dims, :, 1), ndims)
67+
StructuredGrid{datum(crs(g))}(map(X -> X[inds...], XYZ(g)[dims]))
68+
end
69+
70+
# apply shadow transform recursively
71+
@generated function _shadow(g::G, dims) where {G<:GeometryOrDomain}
72+
ctor = constructor(G)
73+
names = fieldnames(G)
74+
exprs = (:(_shadow(g.$name, dims)) for name in names)
75+
:($ctor($(exprs...)))
76+
end
77+
78+
# stop recursion at non-geometric types
79+
_shadow(x, _) = x
80+
81+
# special treatment for lists of geometries
82+
_shadow(g::NTuple{<:Any,<:Geometry}, dims) = map(gᵢ -> _shadow(gᵢ, dims), g)
83+
_shadow(g::AbstractVector{<:Geometry}, dims) = tcollect(_shadow(gᵢ, dims) for gᵢ in g)
84+
_shadow(g::CircularVector{<:Geometry}, dims) = CircularVector(tcollect(_shadow(gᵢ, dims) for gᵢ in g))

‎test/transforms.jl

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,136 @@
12971297
@test r SimpleMesh(f.(vertices(d)), topology(d))
12981298
end
12991299

1300+
@testset "Shadow" begin
1301+
@test !isaffine(Shadow(:xy))
1302+
@test !TB.isrevertible(Shadow("xy"))
1303+
@test !TB.isinvertible(Shadow(:xy))
1304+
@test TB.parameters(Shadow("xy")) == (; dims=(1, 2))
1305+
@test TB.parameters(Shadow(:yx)) == (; dims=(2, 1))
1306+
@test TB.parameters(Shadow("xz")) == (; dims=(1, 3))
1307+
@test TB.parameters(Shadow(:yz)) == (; dims=(2, 3))
1308+
@test_throws ArgumentError Shadow(:xk)
1309+
1310+
# ----
1311+
# VEC
1312+
# ----
1313+
1314+
f = Shadow(:xy)
1315+
v = vector(1, 2, 3)
1316+
r, c = TB.apply(f, v)
1317+
@test r == vector(1, 2)
1318+
1319+
# ------
1320+
# POINT
1321+
# ------
1322+
1323+
f = Shadow(:xz)
1324+
g = point(1, 2, 3)
1325+
r, c = TB.apply(f, g)
1326+
@test r == point(1, 3)
1327+
1328+
# --------
1329+
# SEGMENT
1330+
# --------
1331+
1332+
f = Shadow(:yz)
1333+
g = Segment(point(1, 2, 3), point(4, 5, 6))
1334+
r, c = TB.apply(f, g)
1335+
@test r == Segment(point(2, 3), point(5, 6))
1336+
1337+
# ----
1338+
# BOX
1339+
# ----
1340+
1341+
f = Shadow(:xy)
1342+
g = Box(point(1, 2, 3), point(4, 5, 6))
1343+
r, c = TB.apply(f, g)
1344+
@test r isa Box
1345+
@test r == Box(point(1, 2), point(4, 5))
1346+
1347+
# ---------
1348+
# TRIANGLE
1349+
# ---------
1350+
1351+
f = Shadow(:xz)
1352+
g = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9))
1353+
r, c = TB.apply(f, g)
1354+
@test r == Triangle(point(1, 3), point(4, 6), point(7, 9))
1355+
1356+
# ----------
1357+
# MULTIGEOM
1358+
# ----------
1359+
1360+
f = Shadow(:yz)
1361+
t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9))
1362+
g = Multi([t, t])
1363+
r, c = TB.apply(f, g)
1364+
@test r == Multi([f(t), f(t)])
1365+
1366+
# ---------
1367+
# POINTSET
1368+
# ---------
1369+
1370+
f = Shadow(:xy)
1371+
d = PointSet([point(1, 2, 3), point(4, 5, 6), point(7, 8, 9)])
1372+
r, c = TB.apply(f, d)
1373+
@test r == PointSet([point(1, 2), point(4, 5), point(7, 8)])
1374+
1375+
# ------------
1376+
# GEOMETRYSET
1377+
# ------------
1378+
1379+
f = Shadow(:xz)
1380+
t = Triangle(point(1, 2, 3), point(4, 5, 6), point(7, 8, 9))
1381+
d = GeometrySet([t, t])
1382+
r, c = TB.apply(f, d)
1383+
@test r == GeometrySet([f(t), f(t)])
1384+
d = [t, t]
1385+
r, c = TB.apply(f, d)
1386+
@test all(r .== [f(t), f(t)])
1387+
1388+
# --------------
1389+
# CARTESIANGRID
1390+
# --------------
1391+
1392+
f = Shadow(:yz)
1393+
d = CartesianGrid((10, 11, 12), point(1, 2, 3), T.((1.0, 1.1, 1.2)))
1394+
r, c = TB.apply(f, d)
1395+
@test r isa CartesianGrid
1396+
@test r == CartesianGrid((11, 12), point(2, 3), T.((1.1, 1.2)))
1397+
1398+
# ----------------
1399+
# RECTILINEARGRID
1400+
# ----------------
1401+
1402+
f = Shadow(:xy)
1403+
d = convert(RectilinearGrid, cartgrid(10, 11, 12))
1404+
r, c = TB.apply(f, d)
1405+
@test r isa RectilinearGrid
1406+
@test r == RectilinearGrid(Meshes.xyz(d)[1], Meshes.xyz(d)[2])
1407+
1408+
# ---------------
1409+
# STRUCTUREDGRID
1410+
# ---------------
1411+
1412+
f = Shadow(:xz)
1413+
d = convert(StructuredGrid, cartgrid(10, 11, 12))
1414+
r, c = TB.apply(f, d)
1415+
@test r isa StructuredGrid
1416+
@test r == StructuredGrid(Meshes.XYZ(d)[1][:, 1, :], Meshes.XYZ(d)[3][:, 1, :])
1417+
1418+
# -----------
1419+
# SIMPLEMESH
1420+
# -----------
1421+
1422+
f = Shadow(:yz)
1423+
p = point.([(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (0, 0.5, 0.5)])
1424+
c = connect.([(1, 2, 5), (2, 4, 5), (4, 3, 5), (3, 1, 5)], Triangle)
1425+
d = SimpleMesh(p, c)
1426+
r, c = TB.apply(f, d)
1427+
@test r == SimpleMesh(f.(vertices(d)), topology(d))
1428+
end
1429+
13001430
@testset "Repair{0}" begin
13011431
@test !isaffine(Repair)
13021432
poly = PolyArea(point.([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1), (0, 1)]))

0 commit comments

Comments
 (0)
Please sign in to comment.