Mercurial > repos > public > sbplib_julia
comparison src/Grids/geometry.jl @ 2079:118c09b168f5 default tip
Merge feature/grids/geometry_functions
| author | Jonatan Werpers <jonatan@werpers.com> |
|---|---|
| date | Wed, 18 Feb 2026 21:33:00 +0100 |
| parents | 6797a6cb1da7 |
| children |
comparison
equal
deleted
inserted
replaced
| 2067:a8ea4f94f3c4 | 2079:118c09b168f5 |
|---|---|
| 1 struct Line{PT} | |
| 2 p::PT | |
| 3 tangent::PT | |
| 4 | |
| 5 Line{PT}(p::PT, tangent::PT) where PT = new{PT}(p,tangent) | |
| 6 end | |
| 7 | |
| 8 | |
| 9 """ | |
| 10 Line(p,t) | |
| 11 | |
| 12 A line, as a callable object, starting at `p` with tangent `t`. | |
| 13 The parametrization is ``l(s) = p + st``. | |
| 14 | |
| 15 # Example | |
| 16 ```julia-repl | |
| 17 julia> l = Grids.Line([1,1],[2,1]) | |
| 18 Diffinitive.Grids.Line{StaticArraysCore.SVector{2, Int64}}([1, 1], [2, 1]) | |
| 19 | |
| 20 julia> l(0) | |
| 21 2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2): | |
| 22 1 | |
| 23 1 | |
| 24 | |
| 25 julia> l(1) | |
| 26 2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2): | |
| 27 3 | |
| 28 2 | |
| 29 ``` | |
| 30 | |
| 31 See also: [`LineSegment`](@ref). | |
| 32 """ | |
| 33 function Line(p, t) | |
| 34 p = SVector{length(p)}(p) | |
| 35 t = SVector{length(t)}(t) | |
| 36 p, t = promote(p, t) | |
| 37 | |
| 38 return Line{typeof(p)}(p,t) | |
| 39 end | |
| 40 | |
| 41 (c::Line)(s) = c.p + s*c.tangent | |
| 42 | |
| 43 Grids.jacobian(l::Line, t) = l.tangent | |
| 44 | |
| 45 struct LineSegment{PT} | |
| 46 a::PT | |
| 47 b::PT | |
| 48 | |
| 49 LineSegment{PT}(p::PT, tangent::PT) where PT = new{PT}(p,tangent) | |
| 50 end | |
| 51 | |
| 52 | |
| 53 """ | |
| 54 LineSegment(a,b) | |
| 55 | |
| 56 A line segment, as a callable object, from `a` to `b`. | |
| 57 The parametrization is ``l(s) = (1-s)a + s*b``. | |
| 58 | |
| 59 # Example | |
| 60 ```julia-repl | |
| 61 julia> l = Grids.LineSegment([1,1],[2,1]) | |
| 62 Diffinitive.Grids.LineSegment{StaticArraysCore.SVector{2, Int64}}([1, 1], [2, 1]) | |
| 63 | |
| 64 julia> l(0) | |
| 65 2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2): | |
| 66 1 | |
| 67 1 | |
| 68 | |
| 69 julia> l(0.5) | |
| 70 2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2): | |
| 71 1.5 | |
| 72 1.0 | |
| 73 | |
| 74 julia> l(1) | |
| 75 2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2): | |
| 76 2 | |
| 77 1 | |
| 78 ``` | |
| 79 | |
| 80 See also: [`Line`](@ref). | |
| 81 """ | |
| 82 function LineSegment(a, b) | |
| 83 a = SVector{length(a)}(a) | |
| 84 b = SVector{length(b)}(b) | |
| 85 a, b = promote(a, b) | |
| 86 | |
| 87 return LineSegment{typeof(a)}(a,b) | |
| 88 end | |
| 89 | |
| 90 (c::LineSegment)(s) = (1-s)*c.a + s*c.b | |
| 91 | |
| 92 Grids.jacobian(c::LineSegment, s) = c.b - c.a | |
| 93 | |
| 94 | |
| 95 """ | |
| 96 linesegments(ps...) | |
| 97 | |
| 98 An array of line segments between the points `ps[1]`, `ps[2]`, and so on. | |
| 99 | |
| 100 See also: [`polygon_edges`](@ref). | |
| 101 """ | |
| 102 function linesegments(ps...) | |
| 103 return [LineSegment(ps[i], ps[i+1]) for i ∈ 1:length(ps)-1] | |
| 104 end | |
| 105 | |
| 106 | |
| 107 """ | |
| 108 polygon_edges(ps...) | |
| 109 | |
| 110 An array of line segments between the points `ps[1]`, `ps[2]`, and so on | |
| 111 including the segment between `ps[end]` and `ps[1]`. | |
| 112 | |
| 113 See also: [`linesegments`](@ref). | |
| 114 """ | |
| 115 function polygon_edges(ps...) | |
| 116 n = length(ps) | |
| 117 return [LineSegment(ps[i], ps[mod1(i+1,n)]) for i ∈ eachindex(ps)] | |
| 118 end | |
| 119 | |
| 120 | |
| 121 struct Circle{PT,T} | |
| 122 c::PT | |
| 123 r::T | |
| 124 | |
| 125 Circle{PT,T}(c,r) where {PT,T} = new{PT,T}(c,r) | |
| 126 end | |
| 127 | |
| 128 """ | |
| 129 Circle(c,r) | |
| 130 | |
| 131 A circle with center `c` and radius `r` paramatrized with the angle to the x-axis. | |
| 132 | |
| 133 # Example | |
| 134 ```julia-repl | |
| 135 julia> c = Grids.Circle([1,1], 2) | |
| 136 Diffinitive.Grids.Circle{StaticArraysCore.SVector{2, Int64}, Int64}([1, 1], 2) | |
| 137 | |
| 138 julia> c(0) | |
| 139 2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2): | |
| 140 3.0 | |
| 141 1.0 | |
| 142 | |
| 143 julia> c(π/2) | |
| 144 2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2): | |
| 145 1.0000000000000002 | |
| 146 3.0 | |
| 147 ``` | |
| 148 """ | |
| 149 function Circle(c,r) | |
| 150 c = SVector{2}(c) | |
| 151 return Circle{typeof(c), typeof(r)}(c,r) | |
| 152 end | |
| 153 | |
| 154 function (C::Circle)(θ) | |
| 155 (;c, r) = C | |
| 156 c + r*@SVector[cos(θ), sin(θ)] | |
| 157 end | |
| 158 | |
| 159 function Grids.jacobian(C::Circle, θ) | |
| 160 (;r) = C | |
| 161 r*@SVector[-sin(θ), cos(θ)] | |
| 162 end | |
| 163 | |
| 164 | |
| 165 struct Arc{PT,T} | |
| 166 c::Circle{PT,T} | |
| 167 θ₀::T | |
| 168 θ₁::T | |
| 169 end | |
| 170 | |
| 171 """ | |
| 172 Arc(C::Circle, θ₀, θ₁) | |
| 173 | |
| 174 A circular arc as a callable object. The arc is around the circle `C` between | |
| 175 angles `θ₀` and `θ₁` and is paramatrized between 0 and 1. | |
| 176 | |
| 177 See also: [`arc`](@ref), [`Circle`](@ref). | |
| 178 """ | |
| 179 function Arc(C, θ₀, θ₁) | |
| 180 r, θ₀, θ₁ = promote(C.r, θ₀, θ₁) | |
| 181 | |
| 182 return Arc(Circle(C.c, r), θ₀, θ₁) | |
| 183 end | |
| 184 | |
| 185 function (A::Arc)(t) | |
| 186 (; θ₀, θ₁) = A | |
| 187 return A.c((1-t)*θ₀ + t*θ₁) | |
| 188 end | |
| 189 | |
| 190 function Grids.jacobian(A::Arc, t) | |
| 191 (;c, θ₀, θ₁) = A | |
| 192 return (θ₁-θ₀)*jacobian(c, t) | |
| 193 end | |
| 194 | |
| 195 | |
| 196 """ | |
| 197 arc(a,b,r) | |
| 198 | |
| 199 A circular arc between the points `a` and `b` with radius `abs(r)`. If `r > 0` | |
| 200 the arc goes counter clockwise and if `r<0` the arc goes clockwise. The arc is | |
| 201 parametrized such that if `A = arc(a,b,r)` then `A(0)` corresponds to `a` and | |
| 202 `A(1)` to `b`. | |
| 203 | |
| 204 See also: [`Arc`](@ref), [`Circle`](@ref). | |
| 205 """ | |
| 206 function arc(a,b,r) | |
| 207 if abs(r) < norm(b-a)/2 | |
| 208 throw(DomainError(r, "arc was called with radius r = $r smaller than half the distance between the points.")) | |
| 209 end | |
| 210 | |
| 211 R̂ = @SMatrix[0 -1; 1 0] | |
| 212 | |
| 213 α = sign(r)*√(r^2 - norm((b-a)/2)^2) | |
| 214 t̂ = R̂*(b-a)/norm(b-a) | |
| 215 | |
| 216 c = (a+b)/2 + α*t̂ | |
| 217 | |
| 218 ca = a-c | |
| 219 cb = b-c | |
| 220 θₐ = atan(ca[2],ca[1]) | |
| 221 θᵦ = atan(cb[2],cb[1]) | |
| 222 | |
| 223 Δθ = mod(θᵦ-θₐ+π, 2π)-π # Δθ in the interval (-π,π) | |
| 224 | |
| 225 if r > 0 | |
| 226 Δθ = abs(Δθ) | |
| 227 else | |
| 228 Δθ = -abs(Δθ) | |
| 229 end | |
| 230 | |
| 231 return Arc(Circle(c,abs(r)), θₐ, θₐ+Δθ) | |
| 232 end | |
| 233 | |
| 234 """ | |
| 235 TransfiniteInterpolationSurface(c₁, c₂, c₃, c₄) | |
| 236 | |
| 237 A surface defined by the transfinite interpolation of the curves `c₁`, `c₂`, `c₃`, and `c₄`. | |
| 238 The transfinite interpolation maps the unit square ([0,1]⊗[0,1]) to the patch delimited by the given curves. | |
| 239 The curves should encircle the patch counterclockwise. | |
| 240 | |
| 241 See https://en.wikipedia.org/wiki/Transfinite_interpolation for more information on transfinite interpolation. | |
| 242 """ | |
| 243 struct TransfiniteInterpolationSurface{T1,T2,T3,T4} | |
| 244 c₁::T1 | |
| 245 c₂::T2 | |
| 246 c₃::T3 | |
| 247 c₄::T4 | |
| 248 end | |
| 249 | |
| 250 function (s::TransfiniteInterpolationSurface)(u,v) | |
| 251 if (u,v) ∉ unitsquare() | |
| 252 throw(DomainError((u,v), "Transfinite interpolation was called with parameters outside the unit square.")) | |
| 253 end | |
| 254 c₁, c₂, c₃, c₄ = s.c₁, s.c₂, s.c₃, s.c₄ | |
| 255 P₀₀ = c₁(0) | |
| 256 P₁₀ = c₂(0) | |
| 257 P₁₁ = c₃(0) | |
| 258 P₀₁ = c₄(0) | |
| 259 return (1-v)*c₁(u) + u*c₂(v) + v*c₃(1-u) + (1-u)*c₄(1-v) - ( | |
| 260 (1-u)*(1-v)*P₀₀ + u*(1-v)*P₁₀ + u*v*P₁₁ + (1-u)*v*P₀₁ | |
| 261 ) | |
| 262 end | |
| 263 | |
| 264 function (s::TransfiniteInterpolationSurface)(ξ̄::AbstractArray) | |
| 265 s(ξ̄...) | |
| 266 end | |
| 267 | |
| 268 """ | |
| 269 check_transfiniteinterpolation(s::TransfiniteInterpolationSurface) | |
| 270 | |
| 271 Throw an error if the ends of the curves in the transfinite interpolation do not match. | |
| 272 """ | |
| 273 function check_transfiniteinterpolation(s::TransfiniteInterpolationSurface) | |
| 274 if check_transfiniteinterpolation(Bool, s) | |
| 275 return nothing | |
| 276 else | |
| 277 throw(ArgumentError("The end of each curve in the transfinite interpolation should be the same as the beginning of the next curve.")) | |
| 278 end | |
| 279 end | |
| 280 | |
| 281 """ | |
| 282 check_transfiniteinterpolation(Bool, s::TransfiniteInterpolationSurface) | |
| 283 | |
| 284 Return true if the ends of the curves in the transfinite interpolation match. | |
| 285 """ | |
| 286 function check_transfiniteinterpolation(::Type{Bool}, s::TransfiniteInterpolationSurface) | |
| 287 if !isapprox(s.c₁(1), s.c₂(0)) | |
| 288 return false | |
| 289 end | |
| 290 | |
| 291 if !isapprox(s.c₂(1), s.c₃(0)) | |
| 292 return false | |
| 293 end | |
| 294 | |
| 295 if !isapprox(s.c₃(1), s.c₄(0)) | |
| 296 return false | |
| 297 end | |
| 298 | |
| 299 if !isapprox(s.c₄(1), s.c₁(0)) | |
| 300 return false | |
| 301 end | |
| 302 | |
| 303 return true | |
| 304 end | |
| 305 | |
| 306 function Grids.jacobian(s::TransfiniteInterpolationSurface, ξ̄) | |
| 307 if ξ̄ ∉ unitsquare() | |
| 308 throw(DomainError(ξ̄, "Transfinite interpolation was called with parameters outside the unit square.")) | |
| 309 end | |
| 310 u, v = ξ̄ | |
| 311 | |
| 312 c₁, c₂, c₃, c₄ = s.c₁, s.c₂, s.c₃, s.c₄ | |
| 313 P₀₀ = c₁(0) | |
| 314 P₁₀ = c₂(0) | |
| 315 P₁₁ = c₃(0) | |
| 316 P₀₁ = c₄(0) | |
| 317 | |
| 318 ∂x̄∂ξ₁ = (1-v)*jacobian(c₁,u) + c₂(v) - v*jacobian(c₃,1-u) -c₄(1-v) - ( | |
| 319 -(1-v)*P₀₀ + (1-v)*P₁₀ + v*P₁₁ - v*P₀₁ | |
| 320 ) | |
| 321 | |
| 322 ∂x̄∂ξ₂ = -c₁(u) + u*jacobian(c₂,v) + c₃(1-u) - (1-u)*jacobian(c₄,1-v) - ( | |
| 323 -(1-u)*P₀₀ - u*P₁₀ + u*P₁₁ + (1-u)*P₀₁ | |
| 324 ) | |
| 325 | |
| 326 return [∂x̄∂ξ₁ ∂x̄∂ξ₂] | |
| 327 end |
