|
1 | 1 | ### This file contains public API ### |
2 | 2 |
|
3 | | -function equal_solid_angles(n) |
4 | | - # Distribution sectors along zenith angles |
5 | | - Δθ = π / 2 / n |
6 | | - uθₗ = 0.0:Δθ:π/2-Δθ |
7 | | - uθᵤ = Δθ:Δθ:π/2 |
8 | | - |
9 | | - # Calculate number of azimuth sectors and ΔΦ per zenith ring |
10 | | - fac = cos.(uθₗ) - cos.(uθᵤ) |
11 | | - n2 = n * n |
12 | | - ns = round.(fac ./ sum(fac) .* n2) |
13 | | - sum(ns) != n2 && (ns[end] = ns[end] + n2 - sum(ns)) |
14 | | - ΔΦs = 2π ./ ns |
15 | | - |
16 | | - # Generate coordinates of all sectors |
17 | | - c = 1 |
18 | | - θₗ = Vector{Float32}(undef, n2) |
19 | | - θᵤ = Vector{Float32}(undef, n2) |
20 | | - Φₗ = Vector{Float32}(undef, n2) |
21 | | - Φᵤ = Vector{Float32}(undef, n2) |
22 | | - for i = 1:n |
23 | | - ΔΦ = ΔΦs[i] |
24 | | - for j = 1:ns[i] |
25 | | - θₗ[c] = Δθ * (i - 1) |
26 | | - θᵤ[c] = Δθ * i |
27 | | - Φₗ[c] = ΔΦ * (j - 1) |
28 | | - Φᵤ[c] = ΔΦ * j |
29 | | - c += 1 |
| 3 | +function vertex_sphere(θ, φ, trans) |
| 4 | + FT = eltype(trans.linear) |
| 5 | + sθ, cθ = sincos(FT(θ)) |
| 6 | + sφ, cφ = sincos(FT(φ)) |
| 7 | + trans(Vec{FT}(sθ * cφ, sθ * sφ, 1 + cθ)) |
| 8 | +end |
| 9 | + |
| 10 | +struct EllipsoidVertices{FT,TT} |
| 11 | + n::Int |
| 12 | + dθ::FT |
| 13 | + dφ::FT |
| 14 | + trans::TT |
| 15 | +end |
| 16 | +function EllipsoidVertices(n, trans) |
| 17 | + FT = eltype(trans.linear) |
| 18 | + EllipsoidVertices(n, FT(π / n), FT(2π / n), trans) |
| 19 | +end |
| 20 | + |
| 21 | +function Base.iterate(c::EllipsoidVertices{FT,TT}, i::Int = 1)::Union{Nothing,Tuple{Vec{FT},Int64}} where {FT,TT} |
| 22 | + n = c.n |
| 23 | + i > 6n * (n - 1) && return nothing |
| 24 | + ntop = 3n |
| 25 | + nb = 6n # vertices per middle band |
| 26 | + if i <= ntop |
| 27 | + # Top cap: n fan triangles from north pole to first latitude ring |
| 28 | + j = div(i - 1, 3) |
| 29 | + v = mod(i - 1, 3) + 1 |
| 30 | + v == 1 && (return c.trans(Vec{FT}(0, 0, 2)), i + 1) |
| 31 | + v == 2 && (return vertex_sphere(c.dθ, j * c.dφ, c.trans), i + 1) |
| 32 | + v == 3 && (return vertex_sphere(c.dθ, (j + 1) * c.dφ, c.trans), i + 1) |
| 33 | + elseif i <= ntop + (n - 2) * nb |
| 34 | + # Middle bands: (n-2) latitude bands, each with n quads split into 2 triangles |
| 35 | + im = i - ntop - 1 |
| 36 | + iband = div(im, nb) |
| 37 | + iw = mod(im, nb) |
| 38 | + θu = (iband + 1) * c.dθ # upper ring (smaller θ) |
| 39 | + θl = (iband + 2) * c.dθ # lower ring (larger θ) |
| 40 | + if iw < 3n |
| 41 | + # First triangle of each quad: (top-left, bottom-left, bottom-right) |
| 42 | + j = div(iw, 3) |
| 43 | + v = mod(iw, 3) + 1 |
| 44 | + v == 1 && (return vertex_sphere(θu, j * c.dφ, c.trans), i + 1) |
| 45 | + v == 2 && (return vertex_sphere(θl, j * c.dφ, c.trans), i + 1) |
| 46 | + v == 3 && (return vertex_sphere(θl, (j + 1) * c.dφ, c.trans), i + 1) |
| 47 | + else |
| 48 | + # Second triangle of each quad: (top-left, bottom-right, top-right) |
| 49 | + iw2 = iw - 3n |
| 50 | + j = div(iw2, 3) |
| 51 | + v = mod(iw2, 3) + 1 |
| 52 | + v == 1 && (return vertex_sphere(θu, j * c.dφ, c.trans), i + 1) |
| 53 | + v == 2 && (return vertex_sphere(θl, (j + 1) * c.dφ, c.trans), i + 1) |
| 54 | + v == 3 && (return vertex_sphere(θu, (j + 1) * c.dφ, c.trans), i + 1) |
30 | 55 | end |
| 56 | + else |
| 57 | + # Bottom cap: n fan triangles from last latitude ring to south pole (reversed winding) |
| 58 | + ib = i - ntop - (n - 2) * nb - 1 |
| 59 | + j = div(ib, 3) |
| 60 | + v = mod(ib, 3) + 1 |
| 61 | + θlast = (n - 1) * c.dθ |
| 62 | + v == 1 && (return c.trans(Vec{FT}(0, 0, 0)), i + 1) |
| 63 | + v == 2 && (return vertex_sphere(θlast, (j + 1) * c.dφ, c.trans), i + 1) |
| 64 | + v == 3 && (return vertex_sphere(θlast, j * c.dφ, c.trans), i + 1) |
31 | 65 | end |
32 | | - |
33 | | - (θₗ, θᵤ, Φₗ, Φᵤ, ns) |
34 | 66 | end |
35 | 67 |
|
36 | | -# Every combination of theta and phi angles yields a vertex |
37 | | -# Then the vertex are mapped to the other hemisphere |
38 | | -# This means that we cannot have vertices at theta = 0 |
| 68 | +Base.length(c::EllipsoidVertices) = 6c.n * (c.n - 1) |
| 69 | +Base.eltype(::Type{EllipsoidVertices{FT,TT}}) where {FT,TT} = Vec{FT} |
39 | 70 |
|
40 | | -# Step 1: Turn the function above into an interator that generates |
41 | | -# vertices in both hemispheres (following a reasonable ordering) |
42 | 71 |
|
43 | | -# Step 2: Create the rules to connect these vertices into triangles |
| 72 | +""" |
| 73 | + Ellipsoid(;length = 1.0, width = 1.0, height = 1.0, n = 20) |
44 | 74 |
|
45 | | -# Step 3: Create the normals to each face (average theta and phi??) |
| 75 | +Create a solid ellipsoid with dimensions given by `length`, `width` and `height`, |
| 76 | +with the base on the z = 0 plane, discretized using a UV latitude-longitude mesh |
| 77 | +with `n` latitude and azimuth subdivisions. |
46 | 78 |
|
| 79 | +# Arguments |
| 80 | +- `length = 1.0`: The length of the ellipsoid along the z axis. |
| 81 | +- `width = 1.0`: The width of the ellipsoid along the y axis. |
| 82 | +- `height = 1.0`: The height of the ellipsoid along the x axis. |
| 83 | +- `n = 20`: Resolution (number of latitude and azimuth subdivisions). Must be >= 3. |
| 84 | + The mesh has `2n(n-1)` triangles. |
47 | 85 |
|
48 | | -# Scaled ellipsoid |
49 | | -function Ellipsoid(l::Number, w::Number, h::Number, n::Number) |
50 | | - @error "Ellipsoid not implemented yet" |
| 86 | +# Examples |
| 87 | +```jldoctest |
| 88 | +julia> Ellipsoid(;length = 1.0, width = 1.0, height = 1.0, n = 20); |
| 89 | +``` |
| 90 | +""" |
| 91 | +function Ellipsoid(; length::FT = 1.0, width::FT = 1.0, height::FT = 1.0, n::Int = 20) where {FT} |
| 92 | + trans = CT.LinearMap(CT.SDiagonal(height / FT(2), width / FT(2), length / FT(2))) |
| 93 | + Ellipsoid(trans; n = n) |
51 | 94 | end |
52 | 95 |
|
53 | | -# Create a ellipsoid from affine transformation |
54 | | -function Ellipsoid(trans::CT.AbstractAffineMap, nt::Number) |
55 | | - @error "Ellipsoid not implemented yet" |
| 96 | +# Create an ellipsoid from affine transformation |
| 97 | +function Ellipsoid(trans::CT.AbstractAffineMap; n::Int = 20) |
| 98 | + @assert n >= 3 "n must be at least 3" |
| 99 | + Primitive(trans, x -> EllipsoidVertices(n, x)) |
56 | 100 | end |
57 | 101 |
|
58 | | -# Create a ellipsoid from affine transformation and add it in-place to existing mesh |
59 | | -function Ellipsoid(m::Mesh, trans::CT.AbstractAffineMap, nt::Int) |
60 | | - @error "Ellipsoid not implemented yet" |
| 102 | +# Create an ellipsoid from affine transformation and add it in-place to existing mesh |
| 103 | +function Ellipsoid!(m::Mesh, trans::CT.AbstractAffineMap; n::Int = 20) |
| 104 | + @assert n >= 3 "n must be at least 3" |
| 105 | + Primitive!(m, trans, x -> EllipsoidVertices(n, x)) |
61 | 106 | end |
0 commit comments