ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Cloth.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "Cloth.h"
9 
10 // CV_DB_LIB
11 #include <ecvMesh.h>
12 #include <ecvPointCloud.h>
13 
14 // system
15 #include <assert.h>
16 
17 #include <cmath>
18 #include <fstream>
19 #include <iomanip>
20 #include <iostream>
21 #include <queue>
22 #include <sstream>
23 
24 Cloth::Cloth(const Vec3& _origin_pos,
25  int _num_particles_width,
26  int _num_particles_height,
27  double _step_x,
28  double _step_y,
29  double _smoothThreshold,
30  double _heightThreshold,
31  int rigidness,
32  double time_step)
33  : constraint_iterations(rigidness),
34  time_step(time_step),
35  smoothThreshold(_smoothThreshold),
36  heightThreshold(_heightThreshold),
37  num_particles_width(_num_particles_width),
38  num_particles_height(_num_particles_height),
39  origin_pos(_origin_pos),
40  step_x(_step_x),
41  step_y(_step_y) {
42  particles.resize(
44  num_particles_height); // I am essentially using this
45  // vector as an array with room for
46  // num_particles_width*num_particles_height
47  // particles
48 
49  double time_step2 = time_step * time_step;
50 
51  // creating particles in a grid
52  for (int i = 0; i < num_particles_width; i++) {
53  for (int j = 0; j < num_particles_height; j++) {
54  Vec3 pos(origin_pos.x + i * step_x, origin_pos.y,
55  origin_pos.z + j * step_y);
56 
57  particles[j * num_particles_width + i] = Particle(
58  pos,
59  time_step2); // insert particle in column i at j'th row
60  particles[j * num_particles_width + i].pos_x = i;
61  particles[j * num_particles_width + i].pos_y = j;
62  }
63  }
64 
65  // Connecting immediate neighbor particles with constraints (distance 1 and
66  // sqrt(2) in the grid)
67  for (int x = 0; x < num_particles_width; x++) {
68  for (int y = 0; y < num_particles_height; y++) {
69  if (x < num_particles_width - 1) {
70  addConstraint(&getParticle(x, y), &getParticle(x + 1, y));
71  }
72 
73  if (y < num_particles_height - 1) {
74  addConstraint(&getParticle(x, y), &getParticle(x, y + 1));
75  }
76 
77  if (x < num_particles_width - 1 && y < num_particles_height - 1) {
78  addConstraint(&getParticle(x, y), &getParticle(x + 1, y + 1));
79  addConstraint(&getParticle(x + 1, y), &getParticle(x, y + 1));
80  }
81  }
82  }
83 
84  // Connecting secondary neighbors with constraints (distance 2 and sqrt(4)
85  // in the grid)
86  for (int x = 0; x < num_particles_width; x++) {
87  for (int y = 0; y < num_particles_height; y++) {
88  if (x < num_particles_width - 2) {
89  addConstraint(&getParticle(x, y), &getParticle(x + 2, y));
90  }
91 
92  if (y < num_particles_height - 2) {
93  addConstraint(&getParticle(x, y), &getParticle(x, y + 2));
94  }
95 
96  if (x < num_particles_width - 2 && y < num_particles_height - 2) {
97  addConstraint(&getParticle(x, y), &getParticle(x + 2, y + 2));
98  addConstraint(&getParticle(x + 2, y), &getParticle(x, y + 2));
99  }
100  }
101  }
102 }
103 
105  ccPointCloud* vertices = new ccPointCloud("vertices");
106  ccMesh* mesh = new ccMesh(vertices);
107  mesh->addChild(vertices);
108  vertices->setEnabled(false);
109  unsigned vertCount = static_cast<unsigned>(getSize());
110  unsigned triCount = static_cast<unsigned>((num_particles_height - 1) *
111  (num_particles_width - 1) * 2);
112  if (!vertices->reserve(vertCount) || !mesh->reserve(triCount)) {
113  // not enough memory to generate the cloth mesh
114  delete mesh;
115  mesh = 0;
116  return 0;
117  }
118 
119  // copy the vertices (particles)
120  for (int i = 0; i < getSize(); ++i) {
121  const Particle& particle = particles[i];
122  vertices->addPoint(
123  CCVector3(static_cast<PointCoordinateType>(particle.pos.x),
124  static_cast<PointCoordinateType>(particle.pos.z),
125  static_cast<PointCoordinateType>(-particle.pos.y)));
126  }
127 
128  // and create the triangles
129  for (int x = 0; x < num_particles_width - 1; ++x) {
130  for (int y = 0; y < num_particles_height - 1; ++y) {
131  // A ---------- B
132  // | |
133  // D ---------- C
134  int iA = y * num_particles_width + x;
135  int iD = iA + num_particles_width;
136  int iB = iA + 1;
137  int iC = iD + 1;
138  mesh->addTriangle(iA, iB, iD);
139  mesh->addTriangle(iD, iB, iC);
140  }
141  }
142 
143  return mesh;
144 }
145 
146 double Cloth::timeStep() {
147  int particleCount = static_cast<int>(particles.size());
148 
149 #pragma omp parallel for
150  for (int i = 0; i < particleCount; i++) {
151  particles[i].timeStep();
152  }
153  /*
154  Instead of interating over all the constraints several times, we
155  compute the overall displacement of a particle accroding to the rigidness
156  */
157 
158 #pragma omp parallel for
159  for (int j = 0; j < particleCount; j++) {
160  particles[j].satisfyConstraintSelf(constraint_iterations);
161  }
162 
163  // for (int i = 0; i<constraint_iterations; i++) // iterate over all
164  // constraints several times
165  // {
166  // #pragma omp parallel for
167  // for (int j = 0; j < constraints.size(); j++)
168  // {
169  // constraints[j].satisfyConstraint();
170  // }
171  // }
172 
173  double maxDiff = 0;
174  // #pragma omp parallel for //see https://github.com/CLOUDVIEWER
175  // /CLOUDVIEWER
177  for (int i = 0; i < particleCount; i++) {
178  if (particles[i].isMovable()) {
179  double diff = std::abs(particles[i].old_pos.y - particles[i].pos.y);
180  if (diff > maxDiff) maxDiff = diff;
181  }
182  }
183 
184  return maxDiff;
185 }
186 
187 void Cloth::addForce(const Vec3& direction) {
188  int particleCount = static_cast<int>(particles.size());
189 
190  // add the forces to each particle
191 #pragma omp parallel for
192  for (int i = 0; i < particleCount; i++) {
193  particles[i].addForce(direction);
194  }
195 }
196 
197 // testing the collision
199  assert(particles.size() == heightvals.size());
200 
201  int particleCount = static_cast<int>(particles.size());
202 #pragma omp parallel for
203  for (int i = 0; i < particleCount; i++) {
204  Particle& particle = particles[i];
205  if (particle.pos.y <
206  heightvals[i]) // if the particle is inside the ball
207  {
208  particle.offsetPos(Vec3(0, heightvals[i] - particle.pos.y, 0));
209  particle.makeUnmovable();
210  }
211  }
212 }
213 
215  for (int x = 0; x < num_particles_width; x++) {
216  for (int y = 0; y < num_particles_height; y++) {
217  Particle& ptc = getParticle(x, y);
218  if (ptc.isMovable() && !ptc.isVisited) {
219  std::queue<int> que;
220  std::vector<XY> connected; // store the connected component
221  std::vector<std::vector<int>> neibors;
222  int sum = 1;
223  int index = y * num_particles_width + x;
224  // visit the init node
225  connected.push_back(XY(x, y));
226  particles[index].isVisited = true;
227  // enqueue the init node
228  que.push(index);
229  while (!que.empty()) {
230  Particle& ptc_f = particles[que.front()];
231  que.pop();
232  int cur_x = ptc_f.pos_x;
233  int cur_y = ptc_f.pos_y;
234  std::vector<int> neighbor;
235 
236  if (cur_x > 0) {
237  Particle& ptc_left = getParticle(cur_x - 1, cur_y);
238  if (ptc_left.isMovable()) {
239  if (!ptc_left.isVisited) {
240  sum++;
241  ptc_left.isVisited = true;
242  connected.push_back(XY(cur_x - 1, cur_y));
243  que.push(num_particles_width * cur_y + cur_x -
244  1);
245  neighbor.push_back(sum - 1);
246  ptc_left.c_pos = sum - 1;
247  } else {
248  neighbor.push_back(ptc_left.c_pos);
249  }
250  }
251  }
252 
253  if (cur_x < num_particles_width - 1) {
254  Particle& ptc_right = getParticle(cur_x + 1, cur_y);
255  if (ptc_right.isMovable()) {
256  if (!ptc_right.isVisited) {
257  sum++;
258  ptc_right.isVisited = true;
259  connected.push_back(XY(cur_x + 1, cur_y));
260  que.push(num_particles_width * cur_y + cur_x +
261  1);
262  neighbor.push_back(sum - 1);
263  ptc_right.c_pos = sum - 1;
264  } else {
265  neighbor.push_back(ptc_right.c_pos);
266  }
267  }
268  }
269 
270  if (cur_y > 0) {
271  Particle& ptc_bottom = getParticle(cur_x, cur_y - 1);
272  if (ptc_bottom.isMovable()) {
273  if (!ptc_bottom.isVisited) {
274  sum++;
275  ptc_bottom.isVisited = true;
276  connected.push_back(XY(cur_x, cur_y - 1));
277  que.push(num_particles_width * (cur_y - 1) +
278  cur_x);
279  neighbor.push_back(sum - 1);
280  ptc_bottom.c_pos = sum - 1;
281  } else {
282  neighbor.push_back(ptc_bottom.c_pos);
283  }
284  }
285  }
286 
287  if (cur_y < num_particles_height - 1) {
288  Particle& ptc_top = getParticle(cur_x, cur_y + 1);
289  if (ptc_top.isMovable()) {
290  if (!ptc_top.isVisited) {
291  sum++;
292  ptc_top.isVisited = true;
293  connected.push_back(XY(cur_x, cur_y + 1));
294  que.push(num_particles_width * (cur_y + 1) +
295  cur_x);
296  neighbor.push_back(sum - 1);
297  ptc_top.c_pos = sum - 1;
298  } else {
299  neighbor.push_back(ptc_top.c_pos);
300  }
301  }
302  }
303  neibors.push_back(neighbor);
304  }
305 
306  // Slope postprocessing
307  if (sum > 100) {
308  std::vector<int> edgePoints;
309  findUnmovablePoint(connected, heightvals, edgePoints);
310  handle_slop_connected(edgePoints, connected, neibors,
311  heightvals);
312  }
313  }
314  }
315  }
316 }
317 
318 void Cloth::findUnmovablePoint(const std::vector<XY>& connected,
319  const std::vector<double>& heightvals,
320  std::vector<int>& edgePoints) {
321  for (size_t i = 0; i < connected.size(); i++) {
322  int x = connected[i].x;
323  int y = connected[i].y;
324  int index = y * num_particles_width + x;
325  Particle& ptc = getParticle(x, y);
326  if (x > 0) {
327  const Particle& ptc_x = getParticle(x - 1, y);
328  if (!ptc_x.isMovable()) {
329  int index_ref = y * num_particles_width + x - 1;
330  if (std::abs(heightvals[index] - heightvals[index_ref]) <
331  smoothThreshold &&
332  ptc.pos.y - heightvals[index] < heightThreshold) {
333  Vec3 offsetVec(0, heightvals[index] - ptc.pos.y, 0);
334  particles[index].offsetPos(offsetVec);
335  ptc.makeUnmovable();
336  edgePoints.push_back(static_cast<int>(i));
337  continue;
338  }
339  }
340  }
341 
342  if (x < num_particles_width - 1) {
343  const Particle& ptc_x = getParticle(x + 1, y);
344  if (!ptc_x.isMovable()) {
345  int index_ref = y * num_particles_width + x + 1;
346  if (std::abs(heightvals[index] - heightvals[index_ref]) <
347  smoothThreshold &&
348  ptc.pos.y - heightvals[index] < heightThreshold) {
349  Vec3 offsetVec(0, heightvals[index] - ptc.pos.y, 0);
350  particles[index].offsetPos(offsetVec);
351  ptc.makeUnmovable();
352  edgePoints.push_back(static_cast<int>(i));
353  continue;
354  }
355  }
356  }
357 
358  if (y > 0) {
359  const Particle& ptc_y = getParticle(x, y - 1);
360  if (!ptc_y.isMovable()) {
361  int index_ref = (y - 1) * num_particles_width + x;
362  if (std::abs(heightvals[index] - heightvals[index_ref]) <
363  smoothThreshold &&
364  ptc.pos.y - heightvals[index] < heightThreshold) {
365  Vec3 offsetVec(0, heightvals[index] - ptc.pos.y, 0);
366  particles[index].offsetPos(offsetVec);
367  ptc.makeUnmovable();
368  edgePoints.push_back(static_cast<int>(i));
369  continue;
370  }
371  }
372  }
373 
374  if (y < num_particles_height - 1) {
375  const Particle& ptc_y = getParticle(x, y + 1);
376  if (!ptc_y.isMovable()) {
377  int index_ref = (y + 1) * num_particles_width + x;
378  if (std::abs(heightvals[index] - heightvals[index_ref]) <
379  smoothThreshold &&
380  ptc.pos.y - heightvals[index] < heightThreshold) {
381  Vec3 offsetVec(0, heightvals[index] - ptc.pos.y, 0);
382  particles[index].offsetPos(offsetVec);
383  ptc.makeUnmovable();
384  edgePoints.push_back(static_cast<int>(i));
385  continue;
386  }
387  }
388  }
389  }
390 }
391 
392 // implementing postprocessing to every group of movable points
393 void Cloth::handle_slop_connected(const std::vector<int>& edgePoints,
394  const std::vector<XY>& connected,
395  const std::vector<std::vector<int>>& neibors,
396  const std::vector<double>& heightvals) {
397  std::vector<bool> visited(connected.size(), false);
398 
399  std::queue<int> que;
400  for (size_t i = 0; i < edgePoints.size(); i++) {
401  que.push(edgePoints[i]);
402  visited[edgePoints[i]] = true;
403  }
404 
405  while (!que.empty()) {
406  int index = que.front();
407  que.pop();
408  // ÅжÏÖܱߵãÊÇ·ñÐèÒª´¦Àí
409  int index_center =
410  connected[index].y * num_particles_width + connected[index].x;
411  for (size_t i = 0; i < neibors[index].size(); i++) {
412  int index_neibor =
413  connected[neibors[index][i]].y * num_particles_width +
414  connected[neibors[index][i]].x;
415  if (std::abs(heightvals[index_center] - heightvals[index_neibor]) <
416  smoothThreshold &&
417  fabs(particles[index_neibor].pos.y - heightvals[index_neibor]) <
418  heightThreshold) {
419  Vec3 offsetVec(0,
420  heightvals[index_neibor] -
421  particles[index_neibor].pos.y,
422  0);
423  particles[index_neibor].offsetPos(offsetVec);
424  particles[index_neibor].makeUnmovable();
425  if (visited[neibors[index][i]] == false) {
426  que.push(neibors[index][i]);
427  visited[neibors[index][i]] = true;
428  }
429  }
430  }
431  }
432 }
433 
434 void Cloth::saveToFile(std::string path) {
435  std::string filepath = "cloth_nodes.txt";
436  if (path == "") {
437  filepath = "cloth_nodes.txt";
438  } else {
439  filepath = path;
440  }
441  std::ofstream f1(filepath);
442  if (!f1) return;
443  // clang-format off
444  for (size_t i = 0; i < particles.size(); i++) {
445  // if (!particles[i].isMovable())
446  f1 << std::fixed << std::setprecision(8) << particles[i].pos.x << " "
447  << particles[i].pos.z << " " << -particles[i].pos.y << std::endl;
448  }
449  // clang-format on
450  f1.close();
451 }
452 
453 void Cloth::saveMovableToFile(std::string path) {
454  std::string filepath = "cloth_movable.txt";
455  if (path == "") {
456  filepath = "cloth_movable.txt";
457  } else {
458  filepath = path;
459  }
460  std::ofstream f1(filepath);
461  if (!f1) return;
462  for (size_t i = 0; i < particles.size(); i++) {
463  if (particles[i].isMovable())
464  f1 << std::fixed << std::setprecision(8) << particles[i].pos.x
465  << " " << particles[i].pos.z << " " << -particles[i].pos.y
466  << std::endl;
467  }
468  f1.close();
469 }
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
void saveToFile(std::string path="")
Definition: Cloth.cpp:434
int getSize() const
Definition: Cloth.h:93
int num_particles_width
Definition: Cloth.h:88
void movableFilter()
Definition: Cloth.cpp:214
void addForce(const Vec3 &direction)
Definition: Cloth.cpp:187
double step_y
Definition: Cloth.h:91
void findUnmovablePoint(const std::vector< XY > &connected, const std::vector< double > &heightvals, std::vector< int > &edgePoints)
Definition: Cloth.cpp:318
void saveMovableToFile(std::string path="")
Definition: Cloth.cpp:453
int num_particles_height
Definition: Cloth.h:89
Cloth(const Vec3 &origin_pos, int num_particles_width, int num_particles_height, double step_x, double step_y, double smoothThreshold, double heightThreshold, int rigidness, double time_step)
Definition: Cloth.cpp:24
void terrainCollision()
Definition: Cloth.cpp:198
Vec3 origin_pos
Definition: Cloth.h:90
double timeStep()
Definition: Cloth.cpp:146
void handle_slop_connected(const std::vector< int > &edgePoints, const std::vector< XY > &connected, const std::vector< std::vector< int >> &neighbors, const std::vector< double > &heightvals)
Definition: Cloth.cpp:393
Particle & getParticle(int x, int y)
Definition: Cloth.h:77
ccMesh * toMesh() const
Converts the cloth to a CC mesh structure.
Definition: Cloth.cpp:104
double step_x
Definition: Cloth.h:91
int pos_x
Definition: Particle.h:35
int pos_y
Definition: Particle.h:36
void makeUnmovable()
Definition: Particle.h:98
bool isMovable() const
Definition: Particle.h:88
bool isVisited
Definition: Particle.h:33
int c_pos
Definition: Particle.h:37
void offsetPos(const Vec3 v)
Definition: Particle.h:94
Vec3 pos
Definition: Particle.h:38
Definition: Vec3.h:14
double z
Definition: Vec3.h:18
double y
Definition: Vec3.h:18
double x
Definition: Vec3.h:18
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Triangular mesh.
Definition: ecvMesh.h:35
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
static const std::string path
Definition: PointCloud.cpp:59