deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
function_signed_distance.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2021 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
16
17#include <algorithm>
18
20
21namespace Functions
22{
23 namespace SignedDistance
24 {
25 template <int dim>
26 Sphere<dim>::Sphere(const Point<dim> &center, const double radius)
27 : center(center)
28 , radius(radius)
29 {
30 Assert(radius > 0, ExcMessage("Radius must be positive."));
31 }
32
33
34
35 template <int dim>
36 double
38 const unsigned int component) const
39 {
40 AssertIndexRange(component, this->n_components);
41 (void)component;
42
43 return point.distance(center) - radius;
44 }
45
46
47
48 template <int dim>
51 const unsigned int component) const
52 {
53 AssertIndexRange(component, this->n_components);
54 (void)component;
55
56 const Tensor<1, dim> center_to_point = point - center;
57 const Tensor<1, dim> grad = center_to_point / center_to_point.norm();
58 return grad;
59 }
60
61
62
63 template <int dim>
66 const unsigned int component) const
67 {
68 AssertIndexRange(component, this->n_components);
69 (void)component;
70
71 const Tensor<1, dim> center_to_point = point - center;
72 const double distance = center_to_point.norm();
73
74 const SymmetricTensor<2, dim> hess =
75 unit_symmetric_tensor<dim>() / distance -
76 symmetrize(outer_product(center_to_point, center_to_point)) /
77 Utilities::fixed_power<3>(distance);
78
79 return hess;
80 }
81
82
83
84 template <int dim>
85 Plane<dim>::Plane(const Point<dim> &point, const Tensor<1, dim> &normal)
86 : point_in_plane(point)
87 , normal(normal)
88 {
89 Assert(normal.norm() > 0, ExcMessage("Plane normal must not be 0."));
90 }
91
92
93
94 template <int dim>
95 double
97 const unsigned int component) const
98 {
99 AssertIndexRange(component, this->n_components);
100 (void)component;
101
102 return normal * (point - point_in_plane);
103 }
104
105
106
107 template <int dim>
109 Plane<dim>::gradient(const Point<dim> &, const unsigned int component) const
110 {
111 AssertIndexRange(component, this->n_components);
112 (void)component;
113
114 return normal;
115 }
116
117
118
119 template <int dim>
121 Plane<dim>::hessian(const Point<dim> &, const unsigned int component) const
122 {
123 AssertIndexRange(component, this->n_components);
124 (void)component;
125
127 }
128
129
130
131 template <int dim>
133 const std::array<double, dim> &radii,
134 const double tolerance,
135 const unsigned int max_iter)
136 : center(center)
137 , radii(radii)
138 , tolerance(tolerance)
139 , max_iter(max_iter)
140 {
141 for (unsigned int d = 0; d < dim; ++d)
142 Assert(radii[d] > 0, ExcMessage("All radii must be positive."));
143 }
144
145
146
147 template <int dim>
148 double
150 const unsigned int component) const
151 {
152 AssertIndexRange(component, this->n_components);
153 (void)component;
154
155 if (dim == 1)
156 return point.distance(center) - radii[0];
157 else if (dim == 2)
158 return compute_signed_distance_ellipse(point);
159 else
161
162 return 0.0;
163 }
164
165
166
167 template <int dim>
170 const unsigned int component) const
171 {
172 AssertIndexRange(component, this->n_components);
173 (void)component;
174
175 Tensor<1, dim> grad;
176 if (dim == 1)
177 grad = point - center;
178 else if (dim == 2)
179 {
180 const Point<dim> point_in_centered_coordinate_system =
181 Point<dim>(compute_closest_point_ellipse(point) - center);
182 grad = compute_analyical_normal_vector_on_ellipse(
183 point_in_centered_coordinate_system);
184 }
185 else
187
188 if (grad.norm() > 1e-12)
189 return grad / grad.norm();
190 else
191 return grad;
192 }
193
194
195
196 template <int dim>
197 double
199 {
200 double val = 0.0;
201 for (unsigned int d = 0; d < dim; ++d)
202 val += Utilities::fixed_power<2>((point[d] - center[d]) / radii[d]);
203 return val - 1.0;
204 }
205
206
207
208 template <int dim>
211 {
212 AssertDimension(dim, 2);
213
214 /*
215 * Function to compute the closest point on an ellipse (adopted from
216 * https://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/ and
217 * https://github.com/0xfaded/ellipse_demo):
218 *
219 * Since the ellipse is symmetric to the two major axes through its
220 * center, the point is moved so the center coincides with the origin and
221 * into the first quadrant.
222 * 1. Choose a point on the ellipse (x), here x = a*cos(pi/4) and y =
223 * b*sin(pi/4).
224 * 2. Find second point on the ellipse, that has the same distance.
225 * 3. Find midpoint on the ellipse (must be closer).
226 * 4. Repeat 2.-4. until convergence.
227 */
228 // get equivalent point in first quadrant of centered ellipse
229 const double px = std::abs(point[0] - center[0]);
230 const double py = std::abs(point[1] - center[1]);
231 const double sign_px = std::copysign(1.0, point[0] - center[0]);
232 const double sign_py = std::copysign(1.0, point[1] - center[1]);
233 // get semi axes radii
234 const double &a = radii[0];
235 const double &b = radii[1];
236 // initial guess (t = angle from x-axis)
237 double t = numbers::PI_4;
238 double x = a * std::cos(t);
239 double y = b * std::sin(t);
240
241 unsigned int iter = 0;
242 double delta_t;
243 do
244 {
245 // compute the ellipse evolute (center of curvature) for the current t
246 const double ex =
247 (a * a - b * b) * Utilities::fixed_power<3>(std::cos(t)) / a;
248 const double ey =
249 (b * b - a * a) * Utilities::fixed_power<3>(std::sin(t)) / b;
250 // compute distances from current point on ellipse to its evolute
251 const double rx = x - ex;
252 const double ry = y - ey;
253 // compute distances from point to the current evolute
254 const double qx = px - ex;
255 const double qy = py - ey;
256 // compute the curvature radius at the current point on the ellipse
257 const double r = std::hypot(rx, ry);
258 // compute the distance from evolute to the point
259 const double q = std::hypot(qx, qy);
260 // compute step size on ellipse
261 const double delta_c = r * std::asin((rx * qy - ry * qx) / (r * q));
262 // compute approximate angle step
263 delta_t = delta_c / std::sqrt(a * a + b * b - x * x - y * y);
264 t += delta_t;
265 // make sure the angle stays in first quadrant
266 t = std::clamp(t, 0.0, numbers::PI_2);
267 x = a * std::cos(t);
268 y = b * std::sin(t);
269 ++iter;
270 }
271 while (std::abs(delta_t) > tolerance && iter < max_iter);
272 AssertIndexRange(iter, max_iter);
273
276
277 return center + Point<dim>(sign_px * x, sign_py * y);
278 }
279
280
281
282 template <int dim>
290
291
292
293 template <>
296 const Point<2> &point) const
297 {
298 const auto &a = radii[0];
299 const auto &b = radii[1];
300 const auto &x = point[0];
301 const auto &y = point[1];
302 return Tensor<1, 2, double>({b * x / a, a * y / b});
303 }
304
305
306
307 template <int dim>
308 double
310 {
312 return 0;
313 }
314
315
316
317 template <>
318 double
320 {
321 // point corresponds to center
322 if (point.distance(center) < tolerance)
323 return *std::min_element(radii.begin(), radii.end()) * -1.;
324
325 const Point<2> &closest_point = compute_closest_point_ellipse(point);
326
327 const double distance =
328 std::hypot(closest_point[0] - point[0], closest_point[1] - point[1]);
329
330 return evaluate_ellipsoid(point) < 0.0 ? -distance : distance;
331 }
332
333
334
335 template <int dim>
337 const Point<dim> &top_right)
338 : bounding_box({bottom_left, top_right})
339 {}
340
341
342
343 template <int dim>
345 : bounding_box(bounding_box)
346 {}
347
348
349
350 template <int dim>
351 double
353 const unsigned int component) const
354 {
355 AssertDimension(component, 0);
356 (void)component;
357
358 return bounding_box.signed_distance(p);
359 }
360
361
362
363 template <int dim>
365 const double radius,
366 const double notch_width,
367 const double notch_height)
368 : sphere(center, radius)
369 , notch(// bottom left
370 (dim == 1) ?
371 Point<dim>(center[0] - 0.5 * notch_width) :
372 (dim == 2) ?
373 Point<dim>(center[0] - 0.5 * notch_width,
374 std::numeric_limits<double>::lowest()) : /* notch is open in negative y-direction*/
375 Point<dim>(center[0] - 0.5 * notch_width,
376 std::numeric_limits<
377 double>::lowest() /* notch is open in negative y-direction*/,
378 std::numeric_limits<double>::lowest()), /* notch is open in negative z-direction*/
379 // top right
380 (dim == 1) ?
381 Point<dim>(center[0] + 0.5 * notch_width) :
382 (dim == 2) ?
383 Point<dim>(center[0] + 0.5 * notch_width,
384 center[1] + notch_height - radius) :
385 Point<dim>(center[0] + 0.5 * notch_width,
386 std::numeric_limits<
387 double>::max() /* notch is open in y-direction*/,
388 center[2] + notch_height - radius))
389 {
390 Assert(
391 notch_width <= 2 * radius,
393 "The width of the notch must be less than the circle diameter."));
394 Assert(
395 notch_height <= 2 * radius,
397 "The height of the notch must be less than the circle diameter."));
398 }
399
400
401
402 template <int dim>
403 double
405 const unsigned int component) const
406 {
407 (void)component;
408 AssertDimension(component, 0);
409
410 // calculate the set difference between the level set functions of the
411 // sphere and the notch
412 return std::max(sphere.value(p), -notch.value(p));
413 }
414
415
416 template <int dim>
418 const double radius,
419 const Tensor<1, dim> &axis_direction,
420 const Point<dim> &axis_point)
421 : Function<dim>()
422 , x0(axis_point)
423 , axis(axis_direction / axis_direction.norm())
424 , R(radius)
425 {
426 AssertThrow(radius > 0.0,
427 ExcMessage("Cylinder radius must be positive."));
428
429 const double norm = axis.norm();
430
431 AssertThrow(std::abs(norm - 1.0) < 1e-12,
432 ExcMessage("Axis vector must be normalized (||axis|| = 1)."));
433 }
434
435
436 template <int dim>
437 double
439 const unsigned int) const
440 {
441 const Tensor<1, dim> q = point - x0;
442 const double s = q * axis;
443 const Tensor<1, dim> q_perp = q - s * axis;
444
445 return q_perp.norm() - R;
446 }
447
448
449 template <int dim>
452 const unsigned int) const
453 {
454 const Tensor<1, dim> q = point - x0;
455 const double s = q * axis;
456 const Tensor<1, dim> q_perp = q - s * axis;
457
458 const double r = q_perp.norm();
459
460 return q_perp / r;
461 }
462
463 } // namespace SignedDistance
464} // namespace Functions
465
466#include "base/function_signed_distance.inst"
467
*  const Number radius
double value(const Point< dim > &point, const unsigned int component=0) const override
Tensor< 1, dim > gradient(const Point< dim > &, const unsigned int component=0) const override
double compute_signed_distance_ellipse(const Point< dim > &point) const
Point< dim > compute_closest_point_ellipse(const Point< dim > &point) const
Ellipsoid(const Point< dim > &center, const std::array< double, dim > &radii, const double tolerance=1e-14, const unsigned int max_iter=10)
double evaluate_ellipsoid(const Point< dim > &point) const
Tensor< 1, dim, double > compute_analyical_normal_vector_on_ellipse(const Point< dim > &point) const
Tensor< 1, dim > gradient(const Point< dim > &point, const unsigned int component=0) const override
double value(const Point< dim > &point, const unsigned int component=0) const override
InfiniteCylinder(const double radius, const Tensor< 1, dim > &axis_direction=[] { Tensor< 1, dim > a;a[dim - 1]=1.0;return a;}(), const Point< dim > &axis_point=Point< dim >())
Plane(const Point< dim > &point, const Tensor< 1, dim > &normal)
double value(const Point< dim > &point, const unsigned int component=0) const override
SymmetricTensor< 2, dim > hessian(const Point< dim > &, const unsigned int component=0) const override
Tensor< 1, dim > gradient(const Point< dim > &, const unsigned int component=0) const override
double value(const Point< dim > &p, const unsigned int component=0) const override
Rectangle(const Point< dim > &bottom_left, const Point< dim > &top_right)
Sphere(const Point< dim > &center=Point< dim >(), const double radius=1)
SymmetricTensor< 2, dim > hessian(const Point< dim > &point, const unsigned int component=0) const override
double value(const Point< dim > &point, const unsigned int component=0) const override
Tensor< 1, dim > gradient(const Point< dim > &point, const unsigned int component=0) const override
ZalesakDisk(const Point< dim > &center, const double radius, const double notch_width, const double notch_height)
double value(const Point< dim > &p, const unsigned int component=0) const override
Definition point.h:111
numbers::NumberTraits< Number >::real_type distance(const Point< dim, Number > &p) const
numbers::NumberTraits< Number >::real_type norm() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertIsFinite(number)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
constexpr double PI_2
Definition numbers.h:245
constexpr double PI_4
Definition numbers.h:250
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > asin(const ::VectorizedArray< Number, width > &x)
constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)
constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)