• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "animation/rs_particle_noise_field.h"
17 
18 namespace OHOS {
19 namespace Rosen {
20 constexpr float EPSILON = 1e-3;
21 constexpr float HALF = 0.5f;
22 constexpr float FEATHERMAX = 100.f;
23 constexpr int SQUARE_NUM = 2;
IsPointInField(const Vector2f & point,const ShapeType & fieldShape,const Vector2f & fieldCenter,float width,float height)24 bool ParticleNoiseField::IsPointInField(
25     const Vector2f& point, const ShapeType& fieldShape, const Vector2f& fieldCenter, float width, float height)
26 {
27     if (fieldShape == ShapeType::RECT) {
28         return ((point.x_ > fieldCenter_.x_ - width * HALF) && (point.x_ < fieldCenter_.x_ + width * HALF) &&
29                 (point.y_ >= fieldCenter_.y_ - height * HALF) && (point.y_ < fieldCenter_.y_ + height * HALF));
30     } else {
31         double normX = (point.x_ - fieldCenter_.x_) * (point.x_ - fieldCenter_.x_);
32         double normY = (point.y_ - fieldCenter_.y_) * (point.y_ - fieldCenter_.y_);
33         return ROSEN_EQ(width, 0.f) || ROSEN_EQ(height, 0.f) ? false :
34                (normX / (width * HALF * width * HALF) + normY / (height * HALF * height * HALF) <= 1.0);
35     }
36     return false;
37 }
38 
CalculateDistanceToRectangleEdge(const Vector2f & position,const Vector2f & direction,const Vector2f & center,const Vector2f & size)39 float ParticleNoiseField::CalculateDistanceToRectangleEdge(
40     const Vector2f& position, const Vector2f& direction, const Vector2f& center, const Vector2f& size)
41 {
42     if (ROSEN_EQ(direction.x_, 0.f) && ROSEN_EQ(direction.y_, 0.f)) {
43         return 0.0f;
44     }
45     // Calculates the four boundaries of a rectangle.
46     float left = center.x_ - size.x_ * HALF;
47     float right = center.x_ + size.x_ * HALF;
48     float top = center.y_ - size.y_ * HALF;
49     float bottom = center.y_ + size.y_ * HALF;
50     // Calculate the time required for reaching each boundary.
51     float tLeft = ROSEN_EQ(direction.x_, 0.f) ? -1.f : (left - position.x_) / direction.x_;
52     float tRight = ROSEN_EQ(direction.x_, 0.f) ? -1.f : (right - position.x_) / direction.x_;
53     float tTop = ROSEN_EQ(direction.y_, 0.f) ? -1.f : (top - position.y_) / direction.y_;
54     float tBottom = ROSEN_EQ(direction.y_, 0.f) ? -1.f : (bottom - position.y_) / direction.y_;
55 
56     // Particles advance to the boundary only if t is a positive number.
57     std::vector<float> times;
58     if (tLeft > 0.f) {
59         times.push_back(tLeft);
60     }
61     if (tRight > 0.f) {
62         times.push_back(tRight);
63     }
64     if (tTop > 0.f) {
65         times.push_back(tTop);
66     }
67     if (tBottom > 0.f) {
68         times.push_back(tBottom);
69     }
70 
71     if (times.empty()) {
72         return 0.f;
73     }
74     // The smallest value of t, which is the first time the particle will reach the boundary.
75     float tEdge = *std::min_element(times.begin(), times.end());
76 
77     // Calculates the distance to the border.
78     float distance = std::sqrt(std::pow(tEdge * direction.x_, 2) + std::pow(tEdge * direction.y_, 2));
79     return distance;
80 }
81 
CalculateDistanceToEllipseEdge(const Vector2f & direction,const Vector2f & center,const Vector2f & axes)82 float ParticleNoiseField::CalculateDistanceToEllipseEdge(const Vector2f& direction,
83     const Vector2f& center, const Vector2f& axes)
84 {
85     if (ROSEN_EQ(direction.x_, 0.f) && ROSEN_EQ(direction.y_, 0.f)) {
86         return 0.0f;
87     }
88 
89     float a = axes.x_ / 2;
90     float b = axes.y_ / 2;
91 
92     float t = std::atan2(direction.y_, direction.x_);
93     float x = a * std::cos(t);
94     float y = b * std::sin(t);
95 
96     return std::sqrt(std::pow(x, SQUARE_NUM) + std::pow(y, SQUARE_NUM));
97 }
98 
CalculateFeatherEffect(float distanceToEdge,float featherWidth)99 float ParticleNoiseField::CalculateFeatherEffect(float distanceToEdge, float featherWidth)
100 {
101     float normalizedDistance = 1.0f;
102     if (featherWidth > 0.f && !ROSEN_EQ(featherWidth, 0.f) && distanceToEdge >= 0) {
103         normalizedDistance = distanceToEdge / featherWidth;
104     }
105     if (normalizedDistance >= 1.0f - std::numeric_limits<float>::epsilon()) {
106         return 1.0f;
107     }
108     return normalizedDistance;
109 }
110 
ApplyField(const Vector2f & position,float deltaTime)111 Vector2f ParticleNoiseField::ApplyField(const Vector2f& position, float deltaTime)
112 {
113     if (fieldShape_ == ShapeType::CIRCLE) {
114         fieldSize_.x_ = std::min(fieldSize_.x_, fieldSize_.y_);
115         fieldSize_.y_ = fieldSize_.x_;
116     }
117     if (IsPointInField(position, fieldShape_, fieldCenter_, fieldSize_.x_, fieldSize_.y_) && fieldStrength_ != 0) {
118         Vector2f direction = position - fieldCenter_;
119         float distance = direction.GetLength();
120         float forceMagnitude = static_cast<float>(fieldStrength_);
121         float featherWidth = fieldSize_.x_ * (fieldFeather_ / FEATHERMAX);
122         float edgeDistance = 0.f;
123         if (fieldShape_ == ShapeType::CIRCLE) {
124             edgeDistance = fieldSize_.x_;
125         } else if (fieldShape_ == ShapeType::ELLIPSE) {
126             edgeDistance = CalculateDistanceToEllipseEdge(direction, fieldCenter_, fieldSize_);
127         } else {
128             edgeDistance = CalculateDistanceToRectangleEdge(position, direction, fieldCenter_, fieldSize_);
129         }
130 
131         if (fieldStrength_ < 0 && !ROSEN_EQ(deltaTime, 0.f)) {
132             forceMagnitude = std::max(forceMagnitude, -1.f * distance / deltaTime);
133         } else if (fieldStrength_ > 0 && !ROSEN_EQ(deltaTime, 0.f)) {
134             forceMagnitude = std::min(forceMagnitude, edgeDistance / deltaTime);
135         }
136         float featherEffect = CalculateFeatherEffect(edgeDistance, featherWidth);
137         forceMagnitude *= featherEffect;
138         Vector2f force = direction.Normalized() * forceMagnitude;
139         return force;
140     }
141     return Vector2f { 0.f, 0.f };
142 }
143 
ApplyCurlNoise(const Vector2f & position)144 Vector2f ParticleNoiseField::ApplyCurlNoise(const Vector2f& position)
145 {
146     if (IsPointInField(position, fieldShape_, fieldCenter_, fieldSize_.x_, fieldSize_.y_)) {
147         PerlinNoise2D noise(noiseScale_, noiseFrequency_, noiseAmplitude_);
148         return noise.Curl(position.x_, position.y_) * noiseScale_;
149     }
150     return Vector2f { 0.f, 0.f };
151 }
152 
Dump(std::string & out) const153 void ParticleNoiseFields::Dump(std::string& out) const
154 {
155     out += '[';
156     bool found = false;
157     for (auto& field : fields_) {
158         if (field != nullptr) {
159             found = true;
160             out += "field[fieldStrength:" + std::to_string(field->fieldStrength_);
161             out += " fieldShape:"  + std::to_string(static_cast<int>(field->fieldShape_));
162             out += " fieldSize[x:" + std::to_string(field->fieldSize_.x_) + " y:";
163             out += std::to_string(field->fieldSize_.y_) + "]";
164             out += " fieldCenter[x:" + std::to_string(field->fieldCenter_.x_) + " y:";
165             out += std::to_string(field->fieldCenter_.y_) + "] fieldFeather:" + std::to_string(field->fieldFeather_);
166             out += " noiseScale:" + std::to_string(field->noiseScale_);
167             out += " noiseFrequency:" + std::to_string(field->noiseFrequency_);
168             out += " noiseAmplitude:" + std::to_string(field->noiseAmplitude_) + "] ";
169         }
170     }
171     if (found) {
172         out.pop_back();
173     }
174     out += ']';
175 }
176 
Fade(float t)177 float PerlinNoise2D::Fade(float t)
178 {
179     // Fade function as defined by Ken Perlin: 6t^5 - 15t^4 + 10t^3
180     return t * t * t * (t * (t * 6 - 15) + 10);
181 }
182 
Lerp(float t,float a,float b)183 float PerlinNoise2D::Lerp(float t, float a, float b)
184 {
185     // Linear interpolate between a and b
186     return a + t * (b - a);
187 }
188 
Grad(int hash,float x,float y)189 float PerlinNoise2D::Grad(int hash, float x, float y)
190 {
191     // Convert low 4 bits of hash code into 12 gradient directions.
192     // 15 mins use a bitwise AND operation (&) to get the lowest 4 bits of the hash value.
193     uint32_t h = static_cast<uint32_t>(hash) & 15;
194     // The value of h determines whether the first component of the gradient vector is x or y.
195     // If the value of h is less than 8, u is assigned the value x, otherwise y
196     double u = h < 8 ? x : y;
197     // Selects the second component of the gradient vector.
198     // If h is less than 4, v is assigned the value y; If h is equal to 12 or 14, v is assigned to x; Otherwise, v is 0.
199     // This allocation ensures diversity in the gradient vectors and helps to produce a more natural noise texture.
200     double v = h < 4 ? y : h == 12 || h == 14 ? x : 0;
201     return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); // h & 2 check the h's second least bit
202 }
203 
PerlinNoise2D(float noiseScale,float noiseFrequency,float noiseAmplitude)204 PerlinNoise2D::PerlinNoise2D(float noiseScale, float noiseFrequency, float noiseAmplitude)
205 {
206     noiseScale_ = noiseScale;
207     noiseFrequency_ = noiseFrequency;
208     noiseAmplitude_ = noiseAmplitude;
209     // Initialize permutation vector with values from 0 to 255
210     p.resize(256); // 256 is the vector size
211     std::iota(p.begin(), p.end(), 0);
212 
213     // Shuffle using a default random engine
214     std::default_random_engine engine;
215     std::shuffle(p.begin(), p.end(), engine);
216 
217     // Duplicate the permutation vector
218     p.insert(p.end(), p.begin(), p.end());
219 }
220 
Noise(float x,float y)221 float PerlinNoise2D::Noise(float x, float y)
222 {
223     x *= noiseFrequency_;
224     y *= noiseFrequency_;
225     // Find the unit square that contains the point, 255 is vector max index
226     uint32_t X = static_cast<uint32_t>(static_cast<int>(floor(x))) & 255;
227     uint32_t Y = static_cast<uint32_t>(static_cast<int>(floor(y))) & 255;
228 
229     // Find relative x, y of point in square
230     x -= floor(x);
231     y -= floor(y);
232 
233     // Compute fade curves for each of x, y
234     float u = Fade(x);
235     float v = Fade(y);
236 
237     // Hash coordinates of the 4 square corners
238     int A = p[X] + static_cast<int>(Y);
239     int AA = p[A];
240     int AB = p[A + 1];
241     int B = p[X + 1] + static_cast<int>(Y);
242     int BA = p[B];
243     int BB = p[B + 1];
244 
245     // And add blended results from the 4 corners of the square
246     float res = Lerp(v, Lerp(u, Grad(p[AA], x, y), Grad(p[BA], x - 1, y)),
247         Lerp(u, Grad(p[AB], x, y - 1), Grad(p[BB], x - 1, y - 1)));
248 
249     return noiseAmplitude_ * (res + 1.f) / 2.f; // Normalize the result
250 }
251 
252 // In the two-dimensional mode, curl actually returns a vector instead of a scalar.
Curl(float x,float y)253 Vector2f PerlinNoise2D::Curl(float x, float y)
254 {
255     // Calculate the partial derivative of the noise field.
256     float noise_dx = Noise(x + EPSILON, y) - Noise(x - EPSILON, y);
257     float noise_dy = Noise(x, y + EPSILON) - Noise(x, y - EPSILON);
258 
259     // The result of the two-dimensional curl is the vector obtained by rotating the gradient by 90 degrees.
260     // Assume that Curl has a value only in the Z component (rotation in the two-dimensional plane).
261     float curlx = -noise_dy / (2 * EPSILON);
262     float curly = noise_dx / (2 * EPSILON);
263 
264     return Vector2f(curlx, curly);
265 }
266 } // namespace Rosen
267 } // namespace OHOS