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