LevelSetUtils-Impl.hpp
Go to the documentation of this file.
1 // This code is based on Jet framework.
2 // Copyright (c) 2018 Doyub Kim
3 // CubbyFlow is voxel-based fluid simulation engine for computer games.
4 // Copyright (c) 2020 CubbyFlow Team
5 // Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6 // AI Part: Dongheon Cho, Minseo Kim
7 // We are making my contributions/submissions to this project solely in our
8 // personal capacity and are not conveying any rights to any intellectual
9 // property of any third parties.
10 
11 // Function fractionInside is originally from Christopher Batty's code:
12 //
13 // http://www.cs.ubc.ca/labs/imager/tr/2007/Batty_VariationalFluids/
14 //
15 // and
16 //
17 // https://github.com/christopherbatty/Fluid3D
18 
19 #ifndef CUBBYFLOW_LEVEL_SET_UTILS_IMPL_HPP
20 #define CUBBYFLOW_LEVEL_SET_UTILS_IMPL_HPP
21 
22 #include <Core/Utils/Constants.hpp>
23 
24 #include <cmath>
25 
26 namespace CubbyFlow
27 {
28 template <typename T>
29 bool IsInsideSDF(T phi)
30 {
31  return phi < 0;
32 }
33 
34 template <typename T>
36 {
37  if (phi > 1.5)
38  {
39  return 1;
40  }
41 
42  if (phi < -1.5)
43  {
44  return 0;
45  }
46 
47  return 0.5 + phi / 3.0 +
48  0.5 * (1 / PI<T>()) * std::sin(PI<T>() * phi / 1.5);
49 }
50 
51 template <typename T>
53 {
54  if (std::fabs(phi) > 1.5)
55  {
56  return 0;
57  }
58 
59  return 1.0 / 3.0 + 1.0 / 3.0 * std::cos(PI<T>() * phi / 1.5);
60 }
61 
62 template <typename T>
63 T FractionInsideSDF(T phi0, T phi1)
64 {
65  if (IsInsideSDF(phi0) && IsInsideSDF(phi1))
66  {
67  return 1;
68  }
69 
70  if (IsInsideSDF(phi0) && !IsInsideSDF(phi1))
71  {
72  return phi0 / (phi0 - phi1);
73  }
74 
75  if (!IsInsideSDF(phi0) && IsInsideSDF(phi1))
76  {
77  return phi1 / (phi1 - phi0);
78  }
79 
80  return 0;
81 }
82 
83 template <typename T>
84 void CycleArray(T* arr, int size)
85 {
86  T t = arr[0];
87 
88  for (int i = 0; i < size - 1; ++i)
89  {
90  arr[i] = arr[i + 1];
91  }
92 
93  arr[size - 1] = t;
94 }
95 
96 template <typename T>
97 T FractionInside(T phiBottomLeft, T phiBottomRight, T phiTopLeft, T phiTopRight)
98 {
99  const int insideCount =
100  (phiBottomLeft < 0 ? 1 : 0) + (phiTopLeft < 0 ? 1 : 0) +
101  (phiBottomRight < 0 ? 1 : 0) + (phiTopRight < 0 ? 1 : 0);
102  T list[] = { phiBottomLeft, phiBottomRight, phiTopRight, phiTopLeft };
103 
104  if (insideCount == 4)
105  {
106  return 1;
107  }
108 
109  if (insideCount == 3)
110  {
111  // Rotate until the positive value is in the first position
112  while (list[0] < 0)
113  {
114  CycleArray(list, 4);
115  }
116 
117  // Work out the area of the exterior triangle
118  T side0 = 1 - FractionInsideSDF(list[0], list[3]);
119  T side1 = 1 - FractionInsideSDF(list[0], list[1]);
120 
121  return 1 - 0.5f * side0 * side1;
122  }
123 
124  if (insideCount == 2)
125  {
126  // Rotate until a negative value is in the first position, and the next
127  // negative is in either slot 1 or 2.
128  while (list[0] >= 0 || !(list[1] < 0 || list[2] < 0))
129  {
130  CycleArray(list, 4);
131  }
132 
133  // The matching signs are adjacent
134  if (list[1] < 0)
135  {
136  T side_left = FractionInsideSDF(list[0], list[3]);
137  T side_right = FractionInsideSDF(list[1], list[2]);
138 
139  return 0.5f * (side_left + side_right);
140  }
141 
142  // Matching signs are diagonally opposite
143  // determine the center point's sign to disambiguate this case
144  T middle_point = 0.25f * (list[0] + list[1] + list[2] + list[3]);
145  if (middle_point < 0)
146  {
147  T area = 0;
148 
149  // First triangle (top left)
150  T side1 = 1 - FractionInsideSDF(list[0], list[3]);
151  T side3 = 1 - FractionInsideSDF(list[2], list[3]);
152 
153  area += 0.5f * side1 * side3;
154 
155  // Second triangle (top right)
156  T side2 = 1 - FractionInsideSDF(list[2], list[1]);
157  T side0 = 1 - FractionInsideSDF(list[0], list[1]);
158 
159  area += 0.5f * side0 * side2;
160 
161  return 1 - area;
162  }
163 
164  T area = 0;
165 
166  // First triangle (bottom left)
167  T side0 = FractionInsideSDF(list[0], list[1]);
168  T side1 = FractionInsideSDF(list[0], list[3]);
169  area += 0.5f * side0 * side1;
170 
171  // Second triangle (top right)
172  T side2 = FractionInsideSDF(list[2], list[1]);
173  T side3 = FractionInsideSDF(list[2], list[3]);
174  area += 0.5f * side2 * side3;
175  return area;
176  }
177 
178  if (insideCount == 1)
179  {
180  // Rotate until the negative value is in the first position
181  while (list[0] >= 0)
182  {
183  CycleArray(list, 4);
184  }
185 
186  // Work out the area of the interior triangle, and subtract from 1.
187  T side0 = FractionInsideSDF(list[0], list[3]);
188  T side1 = FractionInsideSDF(list[0], list[1]);
189 
190  return 0.5f * side0 * side1;
191  }
192 
193  return 0;
194 }
195 
196 template <typename T>
197 T DistanceToZeroLevelSet(T phi0, T phi1)
198 {
199  if (std::fabs(phi0) + std::fabs(phi1) >
200  std::numeric_limits<double>::epsilon())
201  {
202  return std::fabs(phi0) / (std::fabs(phi0) + std::fabs(phi1));
203  }
204 
205  return static_cast<T>(0.5);
206 }
207 } // namespace CubbyFlow
208 
209 #endif
T FractionInside(T phiBottomLeft, T phiBottomRight, T phiTopLeft, T phiTopRight)
Returns the fraction occupied by the implicit surface.
Definition: LevelSetUtils-Impl.hpp:97
bool IsInsideSDF(T phi)
Returns true if phi is inside the implicit surface (< 0).
Definition: LevelSetUtils-Impl.hpp:29
T FractionInsideSDF(T phi0, T phi1)
Returns the fraction occupied by the implicit surface.
Definition: LevelSetUtils-Impl.hpp:63
void CycleArray(T *arr, int size)
Definition: LevelSetUtils-Impl.hpp:84
T SmearedHeavisideSDF(T phi)
Returns smeared Heaviside function.
Definition: LevelSetUtils-Impl.hpp:35
Definition: pybind11Utils.hpp:20
T DistanceToZeroLevelSet(T phi0, T phi1)
Definition: LevelSetUtils-Impl.hpp:197
T SmearedDeltaSDF(T phi)
Returns smeared delta function.
Definition: LevelSetUtils-Impl.hpp:52