NurbsSurface Class Reference

#include <NurbsSurface.h>

Public Member Functions

 NurbsSurface ()
 
 NurbsSurface (const std::vector< double > &knotsU, const std::vector< double > &knotsV, const std::vector< std::vector< Vec3D >> &controlPoints, const std::vector< std::vector< double >> &weights)
 
 NurbsSurface (const std::vector< std::vector< Vec3D >> &controlPoints, const std::vector< std::vector< Mdouble >> &weights, unsigned int degreeU, unsigned int degreeV, bool clampedAtStartU=true, bool clampedAtEndU=true, bool clampedAtStartV=true, bool clampedAtEndV=true)
 Create a NurbsSurface. This will create uniform knot vectors in u and v (clamped by default). More...
 
Vec3D evaluate (double u, double v) const
 
void evaluateDerivatives (double u, double v, std::array< std::array< Vec3D, 3 >, 3 > &S) const
 
bool getDistance (Vec3D P, double radius, double &distance, Vec3D &normal) const
 
void set (const std::vector< double > &knotsU, const std::vector< double > &knotsV, const std::vector< std::vector< Vec3D >> &controlPoints, const std::vector< std::vector< double >> &weights)
 
void setClosedInU (bool closedInU)
 
void setClosedInV (bool closedInV)
 
void flipOrientation ()
 
void closestPoint (Vec3D position, double &u, double &v) const
 
void splitSurface (int spanU, int spanV)
 
double getLowerBoundU () const
 
double getUpperBoundU () const
 
double getLowerBoundV () const
 
double getUpperBoundV () const
 
const std::vector< std::vector< Vec3D > > & getControlPoints () const
 
const std::vector< std::vector< Mdouble > > & getWeights () const
 
const std::vector< Mdouble > & getKnotsU () const
 
const std::vector< Mdouble > & getKnotsV () const
 
void makePeriodicContinuousInU ()
 This will make the surface repeat itself and ensure continuity over periodic boundaries. More...
 
void makePeriodicContinuousInV ()
 This will make the surface repeat itself and ensure continuity over periodic boundaries. More...
 
void makeClosedInU ()
 This will make the surface close around on itself and ensure continuity. More...
 
void makeClosedInV ()
 This will make the surface close around on itself and ensure continuity. More...
 
void unclampKnots (bool inU, bool atStart)
 Unclamps the knot vector by changing the control points, weights and knots. More...
 
void moveControlPoint (unsigned int indexU, unsigned int indexV, Vec3D dP, bool includingClosedOrPeriodic)
 

Private Member Functions

void wrapAroundInU (unsigned int numStartToEnd, unsigned int numEndToStart, bool forceBothEndsUniform=false)
 Copies control points from the start and adds to the end and vice versa. The first and last control points are ignored, as they are used the indicate the distance to be shifted by. More...
 
void wrapAroundInV (unsigned int numStartToEnd, unsigned int numEndToStart, bool forceBothEndsUniform=false)
 Copies control points from the start and adds to the end and vice versa. The first and last control points are ignored, as they are used the indicate the distance to be shifted by. More...
 

Private Attributes

std::vector< MdoubleknotsU_
 mu knots More...
 
std::vector< MdoubleknotsV_
 mv knots More...
 
std::vector< std::vector< Vec3D > > controlPoints_
 nu x nv control points More...
 
std::vector< std::vector< Mdouble > > weights_
 nu x nv weights More...
 
unsigned int degreeU_
 degree pu = mu-nu-1, pv = mv-nv-1 More...
 
unsigned int degreeV_
 
bool closedInU_
 make it a periodic system More...
 
bool closedInV_
 
bool periodicInU_
 
bool periodicInV_
 
std::vector< Vec3DstartingPoints_
 
std::vector< MdoublestartingKnotsU_
 
std::vector< MdoublestartingKnotsV_
 

Friends

std::ostream & operator<< (std::ostream &os, const NurbsSurface &a)
 Adds elements to an output stream. More...
 
std::istream & operator>> (std::istream &is, NurbsSurface &a)
 Adds elements to an input stream. More...
 

Constructor & Destructor Documentation

◆ NurbsSurface() [1/3]

NurbsSurface::NurbsSurface ( )

Create a NurbsSurface

34  {
35  std::vector<double> knotsU = {0,0,1,1};
36  std::vector<double> knotsV = {0,0,1,1};
37  std::vector<std::vector<Vec3D>> controlPoints = {{{0,0,0},{0,1,0}},{{1,0,0},{1,1,0}}} ;
38  std::vector<std::vector<Mdouble>> weights = {{1,1},{1,1}};
39  set(knotsU,knotsV,controlPoints,weights);
40  //todo think of good default
41 }
void set(const std::vector< double > &knotsU, const std::vector< double > &knotsV, const std::vector< std::vector< Vec3D >> &controlPoints, const std::vector< std::vector< double >> &weights)
Definition: NurbsSurface.cc:60

◆ NurbsSurface() [2/3]

NurbsSurface::NurbsSurface ( const std::vector< double > &  knotsU,
const std::vector< double > &  knotsV,
const std::vector< std::vector< Vec3D >> &  controlPoints,
const std::vector< std::vector< double >> &  weights 
)

Create a NurbsSurface

Parameters
knotsUKnot vector in u-direction
knotsVKnot vector in v-direction
controlPoints2D vector of control points
weights2D vector of weights
45 {
46  set(knotsU,knotsV,controlPoints,weights);
47 }

◆ NurbsSurface() [3/3]

NurbsSurface::NurbsSurface ( const std::vector< std::vector< Vec3D >> &  controlPoints,
const std::vector< std::vector< Mdouble >> &  weights,
unsigned int  degreeU,
unsigned int  degreeV,
bool  clampedAtStartU = true,
bool  clampedAtEndU = true,
bool  clampedAtStartV = true,
bool  clampedAtEndV = true 
)

Create a NurbsSurface. This will create uniform knot vectors in u and v (clamped by default).

Parameters
controlPoints2D vector of control points
weights2D vector of weights
degreeUDegree in u
degreeVDegree in v
clampedAtStartUClamp knots in u at start
clampedAtEndUClamp knots in u at end
clampedAtStartVClamp knots in v at start
clampedAtEndVClamp knots in v at end
53 {
54  std::vector<Mdouble> knotsU = createUniformKnotVector(controlPoints.size(), degreeU, clampedAtStartU, clampedAtEndU);
55  std::vector<Mdouble> knotsV = createUniformKnotVector(controlPoints[0].size(), degreeV, clampedAtStartV, clampedAtEndV);
56 
57  set(knotsU, knotsV, controlPoints, weights);
58 }
std::vector< Mdouble > createUniformKnotVector(unsigned int numberOfControlPoints, unsigned int degree, bool clampedAtStart, bool clampedAtEnd)
Creates a uniform (clamped) knot vector.
Definition: NurbsUtils.cc:254

References NurbsUtils::createUniformKnotVector().

Member Function Documentation

◆ closestPoint()

void NurbsSurface::closestPoint ( Vec3D  position,
double u,
double v 
) const

◆ evaluate()

Vec3D NurbsSurface::evaluate ( double  u,
double  v 
) const

Evaluate point on a NURBS surface

Parameters
uParameter to evaluate the surface at
vParameter to evaluate the surface at
Returns
Resulting point on the surface at (u, v)
170  {
171 
172  Vec3D point = {0,0,0};
173  double temp = 0;
174 
175  // Find span and non-zero basis functions
176  int spanU = findSpan(degreeU_, knotsU_, u);
177  int spanV = findSpan(degreeV_, knotsV_, v);
178  std::vector<double> Nu, Nv;
179  bsplineBasis(degreeU_, spanU, knotsU_, u, Nu);
180  bsplineBasis(degreeV_, spanV, knotsV_, v, Nv);
181  // make linear combination
182  for (int l = 0; l <= degreeV_; ++l)
183  {
184  for (int k = 0; k <= degreeU_; ++k)
185  {
186  double weight = Nv[l] * Nu[k] * weights_[spanU - degreeU_ + k][spanV - degreeV_ + l];
187  point += weight * controlPoints_[spanU - degreeU_ + k][spanV - degreeV_ + l];
188  temp += weight;
189  }
190  }
191  point /= temp;
192  return point;
193 }
unsigned int degreeU_
degree pu = mu-nu-1, pv = mv-nv-1
Definition: NurbsSurface.h:206
unsigned int degreeV_
Definition: NurbsSurface.h:206
std::vector< std::vector< Vec3D > > controlPoints_
nu x nv control points
Definition: NurbsSurface.h:202
std::vector< Mdouble > knotsU_
mu knots
Definition: NurbsSurface.h:198
std::vector< Mdouble > knotsV_
mv knots
Definition: NurbsSurface.h:200
std::vector< std::vector< Mdouble > > weights_
nu x nv weights
Definition: NurbsSurface.h:204
Definition: Vector.h:51
double Nu
Poisson's ratio.
Definition: TwenteMeshGluing.cpp:67
int findSpan(int degree, const std::vector< double > &knots, double u)
Definition: NurbsUtils.cc:43
void bsplineBasis(int deg, int span, const std::vector< double > &knots, double u, std::vector< double > &N)
Definition: NurbsUtils.cc:129

References NurbsUtils::bsplineBasis(), NurbsUtils::findSpan(), and Global_Physical_Variables::Nu.

Referenced by main(), and NurbsWall::writeVTK().

◆ evaluateDerivatives()

void NurbsSurface::evaluateDerivatives ( double  u,
double  v,
std::array< std::array< Vec3D, 3 >, 3 > &  S 
) const

Evaluate derivatives of a NURBS curve

Parameters
[in]uParameter to evaluate derivatives at
[in]vParameter to evaluate derivatives at
[out]SContains position, first order derivatives and second order derivatives
330 {
331  std::array<std::array<Vec3D,3>,3> A;
332  std::array<std::array<double,3>,3> w;
333  // Find span and non-zero basis functions
334  int spanU = findSpan(degreeU_, knotsU_, u);
335  int spanV = findSpan(degreeV_, knotsV_, v);
336  std::vector<std::vector<double>> Nu, Nv;
337  bsplineDerBasis(degreeU_, spanU, knotsU_, u, 2, Nu);
338  bsplineDerBasis(degreeV_, spanV, knotsV_, v, 2, Nv);
339  // make linear combination (eq 4.21 in Nurbs book)
340  for (int i=0; i<3; ++i) {
341  for (int j = 0; j < 3; ++j) {
342  A[i][j].setZero();
343  w[i][j] = 0;
344  for (int k = 0; k <= degreeU_; ++k) {
345  for (int l = 0; l <= degreeV_; ++l) {
346  double weight = Nu[i][k] * Nv[j][l] * weights_[spanU - degreeU_ + k][spanV - degreeV_ + l];
347  A[i][j] += weight * controlPoints_[spanU - degreeU_ + k][spanV - degreeV_ + l];
348  w[i][j] += weight;
349  }
350  }
351  }
352  }
353  S[0][0] = A[0][0]/w[0][0];
354  S[1][0] = (A[1][0]-w[1][0]*S[0][0])/w[0][0];
355  S[0][1] = (A[0][1]-w[0][1]*S[0][0])/w[0][0];
356  S[1][1] = (A[1][1]-w[1][1]*S[0][0]-w[1][0]*S[0][1]-w[0][1]*S[1][0])/w[0][0];
357  S[2][0] = (A[2][0]-2*w[1][0]*S[1][0]-w[2][0]*S[0][0])/w[0][0];
358  S[0][2] = (A[0][2]-2*w[0][1]*S[0][1]-w[0][2]*S[0][0])/w[0][0];
359  // See equations around eq 4.21 in Nurbs book
360  // [0][0] = e.g. S[0][0] = S w[0][0] = w
361  // [1][0] = u e.g. S[1][0] = S_u w[1][0] = w_u d/du
362  // [0][1] = v e.g. S[0][1] = S_v w[0][1] = w_v d/dv
363  // [1][1] = uv e.g. S[1][1] = S_uv w[1][1] = w_uv d^2/dudv
364  // [2][0] = uu e.g. S[2][0] = S_uu w[2][0] = w_uu d^2/du^2
365  // [0][2] = vv e.g. S[0][2] = S_vv w[0][2] = w_vv d^2/dv^2
366 }
@ A
Definition: StatisticsVector.h:42
void bsplineDerBasis(int deg, int span, const std::vector< double > &knots, double u, int nDers, std::vector< std::vector< double >> &ders)
Definition: NurbsUtils.cc:155
const std::complex< Mdouble > i
Definition: ExtendedMath.h:51

References A, NurbsUtils::bsplineDerBasis(), NurbsUtils::findSpan(), constants::i, and Global_Physical_Variables::Nu.

◆ flipOrientation()

void NurbsSurface::flipOrientation ( )
151 {
152  for (auto& cp : controlPoints_) {
153  std::reverse(cp.begin(), cp.end());
154  }
155  for (auto& w : weights_) {
156  std::reverse(w.begin(), w.end());
157  }
158  std::reverse(knotsV_.begin(), knotsV_.end());
159  double maxK = knotsV_[0];
160  for (auto& k : knotsV_) {
161  k = maxK-k;
162  }
163  // \todo Fix logger messages
164  //logger(INFO,"Created Nurbs surface.");
165  //logger(INFO," %x% knots",knotsU.size(), knotsV.size());
166  //logger(INFO," %x% control points",controlPoints.size(), controlPoints[0].size());
167  //logger(INFO," %x% degrees",degreeU_,degreeV_);
168 }

◆ getControlPoints()

◆ getDistance()

bool NurbsSurface::getDistance ( Vec3D  P,
double  radius,
double distance,
Vec3D normal 
) const

Find projection onto surface, return distance (and contactPoint)

Todo:
here we should use the convex hull argument to rule out certain contactse quickly
198  {
199  //JWB Known reasons why convergence might fail (or worse it doesn't fail but finds a wrong point):
200  // 1. Multiple same value knots (together with a low degree).
201  // 2. Multiple control points at the exact same position.
202  // 3. Really high value weights, or big difference in weights.
203  // 4. Too little starting points to start off iteration.
204  // 5. In general possibly a too low degree.
205  // In some cases there is a clear discontinuity, which then is the obvious reason for failure.
206  // In other cases this is not really clear, so the reason isn't quite clear.
207  // One last reason the iteration might fail is when it actually got quite close to the closest point, but the
208  // tolerance is set too tight. This failure does not cause huge problems though, as when the iteration fails the
209  // distance is calculated anyway. The tolerance value is pretty much picked from thin air, so improving it is fine.
210 
211  // Find the closest starting point
212  double u, v, minDist2 = constants::inf;
213  for (int i = 0; i < startingPoints_.size(); i++) {
214  const double dist2 = Vec3D::getLengthSquared(startingPoints_[i] - P);
215  if (dist2 < minDist2) {
216  u = startingKnotsU_[i];
217  v = startingKnotsV_[i];
218  minDist2 = dist2;
219  }
220  }
222  //Now do Newton-Raphson: Find closest point C(u) to P, see (6.6) in Nurbs book
223  // define f(u)=C'(u).(C(u)-P), find f(u)=0
224  // iterate u <- u-f(u)/f'(u)
225  //in 2D:
226  // define r(u,v) = S(u,v)-P
227  // f = r. dSdu, g = r. dSdv
228  // iterate u <- u + du, v <- v + dv
229  // J.[du;dv] = k
230  // J=[fu fv;gu gv], k=-[f;g]
231  std::array<std::array<Vec3D,3>,3> S;
232  //JWB The tolerance originally was pretty much grabbed from thin air. Multiplying it by 100 allowed for additional
233  // convergence (of the third convergence criterion: parameters fixed) for a bunch of positions.
234  const double tol = 2*std::numeric_limits<double>::epsilon() * 100;
235  const double tolSquared = mathsFunc::square<double>(tol);
236  Vec3D r;
237  double r1;
238  double previousU, previousV;
239  for (int i = 0; i < 15; ++i) {
240  evaluateDerivatives(u,v,S);
241  r = S[0][0] - P;
242  r1 = r.getLengthSquared();
243  if (r1 < tolSquared) /*first convergence criterion: contact point on surface*/ {
244  distance = 0;
245  normal = r;
246  return true;
247  }
248  const double r2 = mathsFunc::square<double>(Vec3D::dot(r, S[1][0]))/(r1*S[1][0].getLengthSquared());
249  const double r3 = mathsFunc::square<double>(Vec3D::dot(r, S[0][1]))/(r1*S[0][1].getLengthSquared());
250  if (std::fabs(r2) < tolSquared && std::fabs(r3) < tolSquared) /*second convergence criterion: zero cosine*/ {
251  //you should exit the function here
252  if (r1 < radius * radius) {
253  logger(VERBOSE,"contact found at % after % iterations", S[0][0], i);
254  distance = sqrt(r1);
255  normal = r / distance;
256  return true;
257  } else {
258  logger(VERBOSE,"contact found, but too distant");
259  return false;
260  }
261  }
262  const double f = Vec3D::dot(r, S[1][0]);
263  const double g = Vec3D::dot(r, S[0][1]);
264  const double a = S[1][0].getLengthSquared() + Vec3D::dot(r, S[2][0]);
265  const double b = Vec3D::dot(S[1][0], S[0][1]) + Vec3D::dot(r, S[1][1]);
266  const double d = S[0][1].getLengthSquared() + Vec3D::dot(r, S[0][2]);
267  const double det = a * d - b * b;
268  const double du = (b * g - d * f) / det;
269  const double dv = (b * f - a * g) / det;
270 
271  previousU = u;
272  previousV = v;
273  u += du;
274  v += dv;
275 
276  // Keeping within bounds
277  Mdouble lbU = getLowerBoundU();
278  Mdouble ubU = getUpperBoundU();
279  Mdouble lbV = getLowerBoundV();
280  Mdouble ubV = getUpperBoundV();
281  if (u < lbU) {
282  u = closedInU_ ? ubU - fmod(lbU - u, ubU - lbU) : lbU;
283  }
284  else if (u > ubU) {
285  u = closedInU_ ? lbU + fmod(u - ubU, ubU - lbU) : ubU;
286  }
287  if (v < lbV) {
288  v = closedInV_ ? ubV - fmod(lbV - v, ubV - lbV) : lbV;
289  }
290  else if (v > ubV) {
291  v = closedInV_ ? lbV + fmod(v - ubV, ubV - lbV) : ubV;
292  }
293 
294  const double r4 = ((u - previousU) * S[1][0] + (v - previousV) * S[0][1]).getLengthSquared();
295  if (r4 < tolSquared) /*third convergence criterion: parameters fixed*/ {
296  if (r1 < radius * radius) {
297  logger(VERBOSE,"parameters fixed, contact at % after % iterations", S[0][0], i);
298  distance = sqrt(r1);
299  normal = r / distance;
300  return true;
301  } else {
302  logger(VERBOSE,"parameters fixed, but too distant");
303  return false;
304  }
305  }
306  }
307  /* iteration fails */
308  //JWB See comments at the start of this method for possible reasons for failure and how it might be solved.
309  // For now a warning is given and whatever was found so far is returned (which is basically rubbish).
310  logger(WARN,"P=%: Number of allowed iterations exceeded; this should never be reached", P);
311  if (r1 < radius * radius) {
312  logger(VERBOSE,"contact found at %", S[0][0]);
313  distance = sqrt(r1);
314  normal = r / distance;
315  return true;
316  } else {
317  logger(VERBOSE,"contact found, but too distant");
318  return false;
319  }
320 }
Logger< MERCURYDPM_LOGLEVEL > logger("MercuryKernel")
Definition of different loggers with certain modules. A user can define its own custom logger here.
@ WARN
@ VERBOSE
std::vector< Mdouble > startingKnotsU_
Definition: NurbsSurface.h:214
void evaluateDerivatives(double u, double v, std::array< std::array< Vec3D, 3 >, 3 > &S) const
Definition: NurbsSurface.cc:329
bool closedInU_
make it a periodic system
Definition: NurbsSurface.h:208
std::vector< Vec3D > startingPoints_
Definition: NurbsSurface.h:213
double getLowerBoundV() const
Definition: NurbsSurface.h:131
double getUpperBoundU() const
Definition: NurbsSurface.h:125
bool closedInV_
Definition: NurbsSurface.h:208
double getLowerBoundU() const
Definition: NurbsSurface.h:119
double getUpperBoundV() const
Definition: NurbsSurface.h:137
std::vector< Mdouble > startingKnotsV_
Definition: NurbsSurface.h:215
static Mdouble getLengthSquared(const Vec3D &a)
Calculates the squared length of a Vec3D: .
Definition: Vector.h:332
Mdouble getLengthSquared() const
Calculates the squared length of this Vec3D: .
Definition: Vector.cc:184
static Mdouble dot(const Vec3D &a, const Vec3D &b)
Calculates the dot product of two Vec3D: .
Definition: Vector.cc:76
double P
Uniform pressure.
Definition: TwenteMeshGluing.cpp:73
const Mdouble inf
Definition: GeneralDefine.h:44

References Vec3D::dot(), Vec3D::getLengthSquared(), constants::i, constants::inf, logger, Global_Physical_Variables::P, VERBOSE, and WARN.

Referenced by NurbsWall::getDistanceAndNormal().

◆ getKnotsU()

const std::vector<Mdouble>& NurbsSurface::getKnotsU ( ) const
inline

◆ getKnotsV()

const std::vector<Mdouble>& NurbsSurface::getKnotsV ( ) const
inline

◆ getLowerBoundU()

double NurbsSurface::getLowerBoundU ( ) const
inline
Returns
Lowest allowed value for u
119  {
120  return knotsU_[degreeU_];
121  }

References degreeU_, and knotsU_.

Referenced by NurbsWall::writeVTK().

◆ getLowerBoundV()

double NurbsSurface::getLowerBoundV ( ) const
inline
Returns
Lowest allowed value for v
131  {
132  return knotsV_[degreeV_];
133  }

References degreeV_, and knotsV_.

Referenced by NurbsWall::writeVTK().

◆ getUpperBoundU()

double NurbsSurface::getUpperBoundU ( ) const
inline
Returns
Highest allowed value for u
125  {
126  return knotsU_[knotsU_.size() - degreeU_ - 1]; // same as knotsU_[controlPoints.size()];
127  }

References degreeU_, and knotsU_.

Referenced by NurbsWall::writeVTK().

◆ getUpperBoundV()

double NurbsSurface::getUpperBoundV ( ) const
inline
Returns
Highest allowed value for v
137  {
138  return knotsV_[knotsV_.size() - degreeV_ - 1]; // same as knotsV_[controlPoints[0].size()];
139  }

References degreeV_, and knotsV_.

Referenced by NurbsWall::writeVTK().

◆ getWeights()

const std::vector<std::vector<Mdouble> >& NurbsSurface::getWeights ( ) const
inline

◆ makeClosedInU()

void NurbsSurface::makeClosedInU ( )

This will make the surface close around on itself and ensure continuity.

The first and last control point are assumed to already be in the same position, then degree-1 control points are copied from the start to the end. Resulting in degree amount of control points are overlapping, which ensures continuity. When both ends of the original knot vector weren't uniform, this will change the shape of the surface a bit, however the inner most surface remains intact.

472 {
473  // Add degree-1 amount of control points to the back.
474  wrapAroundInU(degreeU_ - 1, 0, true);
475  setClosedInU(true);
476 }
void setClosedInU(bool closedInU)
Definition: NurbsSurface.cc:142
void wrapAroundInU(unsigned int numStartToEnd, unsigned int numEndToStart, bool forceBothEndsUniform=false)
Copies control points from the start and adds to the end and vice versa. The first and last control p...
Definition: NurbsSurface.cc:485

◆ makeClosedInV()

void NurbsSurface::makeClosedInV ( )

This will make the surface close around on itself and ensure continuity.

The first and last control point are assumed to already be in the same position, then degree-1 control points are copied from the start to the end. Resulting in degree amount of control points are overlapping, which ensures continuity. When both ends of the original knot vector weren't uniform, this will change the shape of the surface a bit, however the inner most surface remains intact.

479 {
480  // Add degree-1 amount of control points to the back.
481  wrapAroundInV(degreeV_ - 1, 0, true);
482  setClosedInV(true);
483 }
void setClosedInV(bool closedInV)
Definition: NurbsSurface.cc:146
void wrapAroundInV(unsigned int numStartToEnd, unsigned int numEndToStart, bool forceBothEndsUniform=false)
Copies control points from the start and adds to the end and vice versa. The first and last control p...
Definition: NurbsSurface.cc:538

◆ makePeriodicContinuousInU()

void NurbsSurface::makePeriodicContinuousInU ( )

This will make the surface repeat itself and ensure continuity over periodic boundaries.

The first and last control point are assumed to be close to or exactly on the periodic boundaries. Degree-1 amount of control points are copied from both sides to the other. On both sides this results in degree amount of control points "overlapping", which ensures continuity. When both ends of the original knot vector weren't uniform, this will change the shape of the surface a bit, however the inner most surface remains intact.

458 {
459  // Add degree-1 amount of control points to the front and back.
460  wrapAroundInU(degreeU_ - 1, degreeU_ - 1);
461  periodicInU_ = true;
462 }
bool periodicInU_
Definition: NurbsSurface.h:210

Referenced by WearableNurbsWall::set().

◆ makePeriodicContinuousInV()

void NurbsSurface::makePeriodicContinuousInV ( )

This will make the surface repeat itself and ensure continuity over periodic boundaries.

The first and last control point are assumed to be close to or exactly on the periodic boundaries. Degree-1 amount of control points are copied from both sides to the other. On both sides this results in degree amount of control points "overlapping", which ensures continuity. When both ends of the original knot vector weren't uniform, this will change the shape of the surface a bit, however the inner most surface remains intact.

465 {
466  // Add degree-1 amount of control points to the front and back.
467  wrapAroundInV(degreeV_ - 1, degreeV_ - 1);
468  periodicInV_ = true;
469 }
bool periodicInV_
Definition: NurbsSurface.h:210

Referenced by WearableNurbsWall::set().

◆ moveControlPoint()

void NurbsSurface::moveControlPoint ( unsigned int  indexU,
unsigned int  indexV,
Vec3D  dP,
bool  includingClosedOrPeriodic 
)
659 {
660  // When the surface is closed or periodic, there might be other control points which should also move.
661  // To that end, store all the indices in u and v in two vectors, starting with the current one.
662  // Then later each combination of indices of u and v is moved.
663  std::vector<unsigned int> moveIndexU, moveIndexV;
664  moveIndexU.push_back(indexU);
665  moveIndexV.push_back(indexV);
666 
667  if (includingClosedOrPeriodic)
668  {
669  // Assuming either closed or periodic in u/v, not both.
670  unsigned int lowestIndexU, lowestIndexV, shiftU, shiftV;
671 
672  // For closed, degree-1 control points were added to the end
673  if (closedInU_)
674  {
675  lowestIndexU = degreeU_ - 1;
676  shiftU = controlPoints_.size() - degreeU_;
677  }
678  if (closedInV_)
679  {
680  lowestIndexV = degreeV_ - 1;
681  shiftV = controlPoints_[0].size() - degreeV_;
682  }
683 
684  // For periodic, degree-1 control points were added in front and to the end
685  if (periodicInU_)
686  {
687  lowestIndexU = 2 * (degreeU_ - 1);
688  shiftU = controlPoints_.size() - 2 * degreeU_ + 1;
689  }
690  if (periodicInV_)
691  {
692  lowestIndexV = 2 * (degreeV_ - 1);
693  shiftV = controlPoints_[0].size() - 2 * degreeV_ + 1;
694  }
695 
696  if (closedInU_ || periodicInU_)
697  {
698  // Note, no else if, because technically it might be possible for e.g. the middle control point to be the degree-1
699  // from the starting index as well as the end index. Meaning that on both sides other control points need moving.
700  if (indexU <= lowestIndexU)
701  moveIndexU.push_back(indexU + shiftU);
702  if (indexU >= shiftU)
703  moveIndexU.push_back(indexU - shiftU);
704  }
705  if (closedInV_ || periodicInV_)
706  {
707  if (indexV <= lowestIndexV)
708  moveIndexV.push_back(indexV + shiftV);
709  if (indexV >= shiftV)
710  moveIndexV.push_back(indexV - shiftV);
711  }
712  }
713 
714  for (unsigned int u : moveIndexU)
715  for (unsigned int v : moveIndexV)
716  controlPoints_[u][v] += dP;
717 }

Referenced by WearableNurbsWall::moveControlPoint(), and WearableNurbsWall::processDebris().

◆ set()

void NurbsSurface::set ( const std::vector< double > &  knotsU,
const std::vector< double > &  knotsV,
const std::vector< std::vector< Vec3D >> &  controlPoints,
const std::vector< std::vector< double >> &  weights 
)

Create a NurbsSurface

Parameters
knotsUKnot vector in u-direction
knotsVKnot vector in v-direction
controlPoints2D vector of control points
weights2D vector of weights
62 {
63  controlPoints_ = controlPoints;
64  weights_ = weights;
65  knotsU_ = knotsU;
66  knotsV_ = knotsV;
67 
68  if ( controlPoints.size()<2 || controlPoints[0].size()<2 ) {
69  logger(ERROR,"At least two control points are necessary");
70  }
71 
72  if (std::any_of(controlPoints.begin(), controlPoints.end(),
73  [controlPoints](auto v) { return v.size() != controlPoints[0].size(); }))
74  {
75  logger(ERROR, "All rows of the control matrix must have the same size");
76  }
77 
78  if (controlPoints.size() != weights.size() ||
79  std::any_of(weights.begin(), weights.end(),
80  [controlPoints](auto v) { return v.size() != controlPoints[0].size(); }))
81  {
82  logger(ERROR, "Number of control points and weights must match");
83  }
84 
85  if (knotsU.size() < 2 || knotsV.size() < 2)
86  {
87  logger(ERROR, "At least two knots are necessary");
88  }
89 
90  if (!isKnotVectorMonotonic(knotsU) || !isKnotVectorMonotonic(knotsV))
91  {
92  logger(ERROR, "Knot vector(s) is not monotonic");
93  }
94 
95  if (knotsU.size() - controlPoints.size() < 2 || knotsV.size() - controlPoints[0].size() - 1 < 1)
96  {
97  logger(ERROR, "Degree has to be at least 1");
98  }
99 
100  // Reset the u/v interval to [0,1]
103 
104  degreeU_ = knotsU.size() - controlPoints.size() - 1;
105  degreeV_ = knotsV.size() - controlPoints[0].size() - 1;
106 
107  logger(INFO,"Created Nurbs surface.");
108  logger(INFO," %x% knots",knotsU.size(), knotsV.size());
109  logger(INFO," %x% control points",controlPoints.size(), controlPoints[0].size());
110  logger(INFO," %x% degrees",degreeU_,degreeV_);
111 
112  //\todo TW
113  closedInU_ = false;
114  closedInV_ = false;
115  periodicInU_ = false;
116  periodicInV_ = false;
117 
118  // This simply evaluates the positions for a bunch of u's and v's and stores them.
119  // As it is now, the u's and v's are simply taken uniform.
120  // The more points the better (sort of). Too little points can cause a possible convergence to a non-global minimum,
121  // or possible no convergence at all.
122  // For smooth surfaces this already helps a lot. For non-smooth surfaces it most likely doesn't suffice.
123  //\todo JWB The convergence still fails from time to time
124  //\todo JWB These aren't updated when the surface changes shape during simulation (moving control points)
125  unsigned nu = knotsU_.size() * 3;
126  unsigned nv = knotsV_.size() * 3;
127  startingPoints_.clear();
128  startingKnotsU_.clear();
129  startingKnotsV_.clear();
130  for (double i = 0; i <= nu; i++) {
131  double u = getLowerBoundU() + (getUpperBoundU() - getLowerBoundU()) * i / nu;
132  for (double j = 0; j <= nv; j++) {
133  double v = getLowerBoundV() + (getUpperBoundV() - getLowerBoundV()) * j / nv;
134  Vec3D p = evaluate(u, v);
135  startingPoints_.push_back(p);
136  startingKnotsU_.push_back(u);
137  startingKnotsV_.push_back(v);
138  }
139  }
140 }
@ INFO
@ ERROR
Vec3D evaluate(double u, double v) const
Definition: NurbsSurface.cc:170
void normalizeKnotVector(std::vector< Mdouble > &knots)
Resets the knot vector to the interval [0, 1].
Definition: NurbsUtils.cc:288
bool isKnotVectorMonotonic(const std::vector< double > &knots)
Definition: NurbsUtils.cc:33

References ERROR, NurbsUtils::evaluate(), constants::i, INFO, NurbsUtils::isKnotVectorMonotonic(), logger, and NurbsUtils::normalizeKnotVector().

◆ setClosedInU()

void NurbsSurface::setClosedInU ( bool  closedInU)
142  {
143  closedInU_ = closedInU;
144 }

◆ setClosedInV()

void NurbsSurface::setClosedInV ( bool  closedInV)
146  {
147  closedInV_ = closedInV;
148 }

◆ splitSurface()

void NurbsSurface::splitSurface ( int  spanU,
int  spanV 
)
inline
102  {
103 
104  }

◆ unclampKnots()

void NurbsSurface::unclampKnots ( bool  inU,
bool  atStart 
)

Unclamps the knot vector by changing the control points, weights and knots.

Parameters
inUWhether to unclamp the knots in u-direction or the knots in v-direction
atStartWhether to unclamp the knots at the start or the end
579 {
580  // Algorithm from Nurbs book A12.1 extended to surfaces
581 
582  std::vector<Mdouble>& knots = inU ? knotsU_ : knotsV_;
583  unsigned int n = inU ? controlPoints_.size() - 1 : controlPoints_[0].size() - 1;
584  unsigned int p = inU ? degreeU_ : degreeV_;
585 
586  if (atStart)
587  {
588  // Unclamp at left end
589  for (int i = 0; i <= p-2; i++)
590  {
591  knots[p - i - 1] = knots[p - i] - (knots[n - i + 1] - knots[n - i]);
592  int k = p - 1;
593  for (int j = i; j >= 0; j--)
594  {
595  Mdouble alpha = (knots[p] - knots[k]) / (knots[p + j + 1] - knots[k]);
596  k--;
597 
598  // Update changed for each control point and weight in other direction
599  // Only difference between inU or not is the indices are swapped: [j][l] -> [l][j]
600  if (inU)
601  {
602  for (int l = 0; l < controlPoints_[0].size(); l++)
603  {
604  controlPoints_[j][l] = (controlPoints_[j][l] - alpha * controlPoints_[j+1][l]) / (1.0 - alpha);
605  weights_[j][l] = (weights_[j][l] - alpha * weights_[j+1][l]) / (1.0 - alpha);
606  }
607  }
608  else
609  {
610  for (int l = 0; l < controlPoints_.size(); l++)
611  {
612  controlPoints_[l][j] = (controlPoints_[l][j] - alpha * controlPoints_[l][j+1]) / (1.0 - alpha);
613  weights_[l][j] = (weights_[l][j] - alpha * weights_[l][j+1]) / (1.0 - alpha);
614  }
615  }
616  }
617  }
618  // Set first knot
619  knots[0] = knots[1] - (knots[n - p + 2] - knots[n - p + 1]);
620  }
621  else
622  {
623  // Unclamp at right end
624  for (int i = 0; i <= p-2; i++)
625  {
626  knots[n + i + 2] = knots[n + i + 1] + (knots[p + i + 1] - knots[p + i]);
627  for (int j = 1; j >= 0; j--)
628  {
629  Mdouble alpha = (knots[n + 1] - knots[n - j]) / (knots[n - j + i + 2] - knots[n - j]);
630 
631  // Update changed for each control point and weight in other direction
632  // Only difference between inU or not is the indices are swapped: [j][l] -> [l][j]
633  if (inU)
634  {
635  for (int l = 0; l < controlPoints_[0].size(); l++)
636  {
637  controlPoints_[n - j][l] = (controlPoints_[n - j][l] - (1.0 - alpha) * controlPoints_[n - j -1][l]) / alpha;
638  weights_[n - j][l] = (weights_[n - j][l] - (1.0 - alpha) * weights_[n - j -1][l]) / alpha;
639  }
640  }
641  else
642  {
643  for (int l = 0; l < controlPoints_.size(); l++)
644  {
645  controlPoints_[l][n - j] = (controlPoints_[l][n - j] - (1.0 - alpha) * controlPoints_[l][n - j -1]) / alpha;
646  weights_[l][n - j] = (weights_[l][n - j] - (1.0 - alpha) * weights_[l][n - j -1]) / alpha;
647  }
648  }
649  }
650  }
651  // Set last knot
652  knots[n + p + 1] = knots[n + p] + (knots[2*p] - knots[2*p - 1]);
653  }
654 
655  normalizeKnotVector(knots);
656 }
const unsigned n
Definition: CG3DPackingUnitTest.cpp:32

References constants::i, n, and NurbsUtils::normalizeKnotVector().

◆ wrapAroundInU()

void NurbsSurface::wrapAroundInU ( unsigned int  numStartToEnd,
unsigned int  numEndToStart,
bool  forceBothEndsUniform = false 
)
private

Copies control points from the start and adds to the end and vice versa. The first and last control points are ignored, as they are used the indicate the distance to be shifted by.

Parameters
numStartToEndAmount to copy from start and add to end
numEndToStartAmount to copy from end and insert before start
forceBothEndsUniformWhen only copying to one side, whether or not the other end should remain untouched
486 {
487  // This method copies the given number of control points from the start to the end and vice versa (not counting the
488  // first and last control point).
489  // The first and last control point are used to know the amount that the control points to be copied have to be
490  // shifted by. In case of closing a surface (circle like shape) the shifted distance is simply 0.
491  // The knot vector is updated in such a way that only the start and end of the shape might be influenced (both ends
492  // should be uniform), however the inner shape remains intact.
493 
494  // A vector of offsets, which is the difference between the first and last row of control points.
495  // These are the values the copied control points need to be shifted by.
496  std::vector<Vec3D> offsets;
497  offsets.reserve(controlPoints_[0].size());
498  for (int j = 0; j < controlPoints_[0].size(); j++)
499  {
500  offsets.push_back(controlPoints_.back()[j] - controlPoints_.front()[j]);
501  }
502 
503  // Temporarily store "ghost" control points and weights to be added in front
504  std::vector<std::vector<Vec3D>> frontControlPoints;
505  std::vector<std::vector<Mdouble>> frontWeights;
506  // Get the numEndToStart amount, in order, ignoring the last one
507  for (unsigned int i = numEndToStart; i > 0; i--)
508  {
509  std::vector<Vec3D> tmpCP = controlPoints_[controlPoints_.size() - 1 - i];
510  for (int j = 0; j < tmpCP.size(); j++)
511  {
512  tmpCP[j] -= offsets[j];
513  }
514  frontControlPoints.push_back(tmpCP);
515  frontWeights.push_back(weights_[weights_.size() - 1 - i]);
516  }
517 
518  // Add "ghost" control points and weights at the back
519  // Get the numStartToEnd amount, in order, ignoring the first one
520  for (int i = 1; i <= numStartToEnd; i++)
521  {
522  std::vector<Vec3D> tmpCP = controlPoints_[i];
523  for (int j = 0; j < tmpCP.size(); j++)
524  {
525  tmpCP[j] += offsets[j];
526  }
527  controlPoints_.push_back(tmpCP);
528  weights_.push_back(weights_[i]);
529  }
530 
531  // Now actually add the "ghost" control points and weights in front
532  controlPoints_.insert(controlPoints_.begin(), frontControlPoints.begin(), frontControlPoints.end());
533  weights_.insert(weights_.begin(), frontWeights.begin(), frontWeights.end());
534 
535  extendKnotVector(knotsU_, degreeU_, numEndToStart, numStartToEnd, forceBothEndsUniform);
536 }
void extendKnotVector(std::vector< Mdouble > &knots, unsigned int degree, unsigned int numStart, unsigned int numEnd, bool forceBothEndsUniform)
Extends the knot vector for when control points have been added at the start or end.
Definition: NurbsUtils.cc:304

References NurbsUtils::extendKnotVector(), and constants::i.

◆ wrapAroundInV()

void NurbsSurface::wrapAroundInV ( unsigned int  numStartToEnd,
unsigned int  numEndToStart,
bool  forceBothEndsUniform = false 
)
private

Copies control points from the start and adds to the end and vice versa. The first and last control points are ignored, as they are used the indicate the distance to be shifted by.

Parameters
numStartToEndAmount to copy from start and add to end
numEndToStartAmount to copy from end and insert before start
forceBothEndsUniformWhen only copying to one side, whether or not the other end should remain untouched
539 {
540  // This method copies the given number of control points from the start to the end and vice versa (not counting the
541  // first and last control point).
542  // The first and last control point are used to know the amount that the control points to be copied have to be
543  // shifted by. In case of closing a surface (circle like shape) the shifted distance is simply 0.
544  // The knot vector is updated in such a way that only the start and end of the shape might be influenced (both ends
545  // should be uniform), however the inner shape remains intact.
546 
547  for (int i = 0; i < controlPoints_.size(); i++)
548  {
549  // Current offset
550  Vec3D offset = controlPoints_[i].back() - controlPoints_[i].front();
551 
552  // Temporarily store "ghost" control points and weights to be added in front
553  std::vector<Vec3D> frontControlPoints;
554  std::vector<Mdouble> frontWeights;
555  // Get the numEndToStart amount, in order, ignoring the last one
556  for (unsigned int j = numEndToStart; j > 0; j--)
557  {
558  frontControlPoints.push_back(controlPoints_[i][controlPoints_[i].size() - 1 - j] - offset);
559  frontWeights.push_back(weights_[i][weights_[i].size() - 1 - j]);
560  }
561 
562  // Add "ghost" control points and weights at the back
563  // Get the numStartToEnd amount, in order, ignoring the first one
564  for (int j = 1; j <= numStartToEnd; j++)
565  {
566  controlPoints_[i].push_back(controlPoints_[i][j] + offset);
567  weights_[i].push_back(weights_[i][j]);
568  }
569 
570  // Now actually add the "ghost" control points and weights in front
571  controlPoints_[i].insert(controlPoints_[i].begin(), frontControlPoints.begin(), frontControlPoints.end());
572  weights_[i].insert(weights_[i].begin(), frontWeights.begin(), frontWeights.end());
573  }
574 
575  extendKnotVector(knotsV_, degreeV_, numEndToStart, numStartToEnd, forceBothEndsUniform);
576 }

References NurbsUtils::extendKnotVector(), and constants::i.

Friends And Related Function Documentation

◆ operator<<

std::ostream& operator<< ( std::ostream &  os,
const NurbsSurface a 
)
friend

Adds elements to an output stream.

Adds all elements of the vector to an output stream. NB: this is a global function and a friend of the Vec3D class!

Parameters
[in]osthe output stream,
[in]aThe vector of interest
Returns
the output stream with vector elements added
377 {
378  os << "mu " << a.knotsU_.size() << ' ';
379  os << "mv " << a.knotsV_.size() << ' ';
380  os << "nu " << a.controlPoints_.size() << ' ';
381  os << "nv " << a.controlPoints_[0].size() << ' ';
382  os << "knotsU ";
383  for (const auto k : a.knotsU_) os << k << ' ';
384  os << "knotsV ";
385  for (const auto k : a.knotsV_) os << k << ' ';
386  os << "controlPoints ";
387  for (const auto& cp0 : a.controlPoints_) for (const auto cp : cp0) os << cp << ' ';
388  os << "weights ";
389  for (const auto& w0 : a.weights_) for (const auto w : w0) os << w << ' ';
390  os << "closedInUV " << a.closedInU_ << ' ' << a.closedInV_;
391  os << " periodicInUV " << a.periodicInU_ << ' ' << a.periodicInV_;
392  return os;
393 }

◆ operator>>

std::istream& operator>> ( std::istream &  is,
NurbsSurface a 
)
friend

Adds elements to an input stream.

Reads all elements of a given vector from an input stream. NB: this is a global function and a friend of the Vec3D class!

Parameters
[in,out]isthe input stream
[in,out]athe vector to be read in
Returns
the input stream from which the vector elements were read
403 {
404  std::string dummy;
405  unsigned mu, mv, nu, nv;
406  is >> dummy >> mu;
407  is >> dummy >> mv;
408  is >> dummy >> nu;
409  is >> dummy >> nv;
410 
411  is >> dummy;
412  std::vector<double> knotsU;
413  knotsU.resize(mu);
414  for (auto& k : knotsU) is >> k;
415 
416  is >> dummy;
417  std::vector<double> knotsV;
418  knotsV.resize(mv);
419  for (auto& k : knotsV) is >> k;
420 
421  is >> dummy;
422  std::vector<std::vector<Vec3D>> controlPoints;
423  controlPoints.resize(nu);
424  for (auto& cp0 : controlPoints) {
425  cp0.resize(nv);
426  for (auto& cp : cp0) is >> cp;
427  }
428 
429  is >> dummy;
430  std::vector<std::vector<double>> weights;
431  weights.resize(nu);
432  for (auto& w0 : weights) {
433  w0.resize(nv);
434  for (auto& w : w0) is >> w;
435  }
436 
437  a.set(knotsU,knotsV,controlPoints,weights);
438 
439  // After setting, since in that method the defaults are set to false.
440  // Also, check if dummy variable exist, for backwards compatibility.
441  is >> dummy;
442  if (dummy == "closedInUV")
443  {
444  is >> a.closedInU_;
445  is >> a.closedInV_;
446  }
447  is >> dummy;
448  if (dummy == "periodicInUV")
449  {
450  is >> a.periodicInU_;
451  is >> a.periodicInV_;
452  }
453 
454  return is;
455 }

Member Data Documentation

◆ closedInU_

bool NurbsSurface::closedInU_
private

make it a periodic system

◆ closedInV_

bool NurbsSurface::closedInV_
private

◆ controlPoints_

std::vector<std::vector<Vec3D> > NurbsSurface::controlPoints_
private

nu x nv control points

Referenced by getControlPoints().

◆ degreeU_

unsigned int NurbsSurface::degreeU_
private

degree pu = mu-nu-1, pv = mv-nv-1

Referenced by getLowerBoundU(), and getUpperBoundU().

◆ degreeV_

unsigned int NurbsSurface::degreeV_
private

Referenced by getLowerBoundV(), and getUpperBoundV().

◆ knotsU_

std::vector<Mdouble> NurbsSurface::knotsU_
private

mu knots

Referenced by getKnotsU(), getLowerBoundU(), and getUpperBoundU().

◆ knotsV_

std::vector<Mdouble> NurbsSurface::knotsV_
private

mv knots

Referenced by getKnotsV(), getLowerBoundV(), and getUpperBoundV().

◆ periodicInU_

bool NurbsSurface::periodicInU_
private

◆ periodicInV_

bool NurbsSurface::periodicInV_
private

◆ startingKnotsU_

std::vector<Mdouble> NurbsSurface::startingKnotsU_
private

◆ startingKnotsV_

std::vector<Mdouble> NurbsSurface::startingKnotsV_
private

◆ startingPoints_

std::vector<Vec3D> NurbsSurface::startingPoints_
private

◆ weights_

std::vector<std::vector<Mdouble> > NurbsSurface::weights_
private

nu x nv weights

Referenced by getWeights().


The documentation for this class was generated from the following files: