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

13  {
14  std::vector<double> knotsU = {0,0,1,1};
15  std::vector<double> knotsV = {0,0,1,1};
16  std::vector<std::vector<Vec3D>> controlPoints = {{{0,0,0},{0,1,0}},{{1,0,0},{1,1,0}}} ;
17  std::vector<std::vector<Mdouble>> weights = {{1,1},{1,1}};
18  set(knotsU,knotsV,controlPoints,weights);
19  //todo think of good default
20 }
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:39
list weights
Definition: calibrate.py:94

References set(), and calibrate::weights.

◆ 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
24 {
25  set(knotsU,knotsV,controlPoints,weights);
26 }

References set(), and calibrate::weights.

◆ 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
32 {
33  std::vector<Mdouble> knotsU = createUniformKnotVector(controlPoints.size(), degreeU, clampedAtStartU, clampedAtEndU);
34  std::vector<Mdouble> knotsV = createUniformKnotVector(controlPoints[0].size(), degreeV, clampedAtStartV, clampedAtEndV);
35 
36  set(knotsU, knotsV, controlPoints, weights);
37 }
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
std::vector< Mdouble > createUniformKnotVector(unsigned int numberOfControlPoints, unsigned int degree, bool clampedAtStart, bool clampedAtEnd)
Creates a uniform (clamped) knot vector.
Definition: NurbsUtils.cc:233

References NurbsUtils::createUniformKnotVector(), set(), size, and calibrate::weights.

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)
149  {
150 
151  Vec3D point = {0,0,0};
152  double temp = 0;
153 
154  // Find span and non-zero basis functions
155  int spanU = findSpan(degreeU_, knotsU_, u);
156  int spanV = findSpan(degreeV_, knotsV_, v);
157  std::vector<double> Nu, Nv;
158  bsplineBasis(degreeU_, spanU, knotsU_, u, Nu);
159  bsplineBasis(degreeV_, spanV, knotsV_, v, Nv);
160  // make linear combination
161  for (int l = 0; l <= degreeV_; ++l)
162  {
163  for (int k = 0; k <= degreeU_; ++k)
164  {
165  double weight = Nv[l] * Nu[k] * weights_[spanU - degreeU_ + k][spanV - degreeV_ + l];
166  point += weight * controlPoints_[spanU - degreeU_ + k][spanV - degreeV_ + l];
167  temp += weight;
168  }
169  }
170  point /= temp;
171  return point;
172 }
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
unsigned int degreeU_
degree pu = mu-nu-1, pv = mv-nv-1
Definition: NurbsSurface.h:185
unsigned int degreeV_
Definition: NurbsSurface.h:185
std::vector< std::vector< Vec3D > > controlPoints_
nu x nv control points
Definition: NurbsSurface.h:181
std::vector< Mdouble > knotsU_
mu knots
Definition: NurbsSurface.h:177
std::vector< Mdouble > knotsV_
mv knots
Definition: NurbsSurface.h:179
std::vector< std::vector< Mdouble > > weights_
nu x nv weights
Definition: NurbsSurface.h:183
Definition: Kernel/Math/Vector.h:30
char char char int int * k
Definition: level2_impl.h:374
double Nu
Definition: ConstraintElementsUnitTest.cpp:40
int findSpan(int degree, const std::vector< double > &knots, double u)
Definition: NurbsUtils.cc:22
void bsplineBasis(int deg, int span, const std::vector< double > &knots, double u, std::vector< double > &N)
Definition: NurbsUtils.cc:108

References NurbsUtils::bsplineBasis(), NurbsUtils::findSpan(), k, Constitutive_Parameters::Nu, and v.

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
309 {
310  std::array<std::array<Vec3D,3>,3> A;
311  std::array<std::array<double,3>,3> w;
312  // Find span and non-zero basis functions
313  int spanU = findSpan(degreeU_, knotsU_, u);
314  int spanV = findSpan(degreeV_, knotsV_, v);
315  std::vector<std::vector<double>> Nu, Nv;
316  bsplineDerBasis(degreeU_, spanU, knotsU_, u, 2, Nu);
317  bsplineDerBasis(degreeV_, spanV, knotsV_, v, 2, Nv);
318  // make linear combination (eq 4.21 in Nurbs book)
319  for (int i=0; i<3; ++i) {
320  for (int j = 0; j < 3; ++j) {
321  A[i][j].setZero();
322  w[i][j] = 0;
323  for (int k = 0; k <= degreeU_; ++k) {
324  for (int l = 0; l <= degreeV_; ++l) {
325  double weight = Nu[i][k] * Nv[j][l] * weights_[spanU - degreeU_ + k][spanV - degreeV_ + l];
326  A[i][j] += weight * controlPoints_[spanU - degreeU_ + k][spanV - degreeV_ + l];
327  w[i][j] += weight;
328  }
329  }
330  }
331  }
332  S[0][0] = A[0][0]/w[0][0];
333  S[1][0] = (A[1][0]-w[1][0]*S[0][0])/w[0][0];
334  S[0][1] = (A[0][1]-w[0][1]*S[0][0])/w[0][0];
335  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];
336  S[2][0] = (A[2][0]-2*w[1][0]*S[1][0]-w[2][0]*S[0][0])/w[0][0];
337  S[0][2] = (A[0][2]-2*w[0][1]*S[0][1]-w[0][2]*S[0][0])/w[0][0];
338  // See equations around eq 4.21 in Nurbs book
339  // [0][0] = e.g. S[0][0] = S w[0][0] = w
340  // [1][0] = u e.g. S[1][0] = S_u w[1][0] = w_u d/du
341  // [0][1] = v e.g. S[0][1] = S_v w[0][1] = w_v d/dv
342  // [1][1] = uv e.g. S[1][1] = S_uv w[1][1] = w_uv d^2/dudv
343  // [2][0] = uu e.g. S[2][0] = S_uu w[2][0] = w_uu d^2/du^2
344  // [0][2] = vv e.g. S[0][2] = S_vv w[0][2] = w_vv d^2/dv^2
345 }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
RowVector3d w
Definition: Matrix_resize_int.cpp:3
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:47
The matrix class, also used for vectors and row-vectors.
Definition: Eigen/Eigen/src/Core/Matrix.h:186
EIGEN_DEVICE_FUNC Derived & setZero(Index size)
Definition: CwiseNullaryOp.h:569
void bsplineDerBasis(int deg, int span, const std::vector< double > &knots, double u, int nDers, std::vector< std::vector< double >> &ders)
Definition: NurbsUtils.cc:134
@ S
Definition: quadtree.h:62
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References NurbsUtils::bsplineDerBasis(), NurbsUtils::findSpan(), i, j, k, Constitutive_Parameters::Nu, oomph::QuadTreeNames::S, Eigen::PlainObjectBase< Derived >::setZero(), v, and w.

◆ flipOrientation()

void NurbsSurface::flipOrientation ( )
130 {
131  for (auto& cp : controlPoints_) {
132  std::reverse(cp.begin(), cp.end());
133  }
134  for (auto& w : weights_) {
135  std::reverse(w.begin(), w.end());
136  }
137  std::reverse(knotsV_.begin(), knotsV_.end());
138  double maxK = knotsV_[0];
139  for (auto& k : knotsV_) {
140  k = maxK-k;
141  }
142  // \todo Fix logger messages
143  //logger(INFO,"Created Nurbs surface.");
144  //logger(INFO," %x% knots",knotsU.size(), knotsV.size());
145  //logger(INFO," %x% control points",controlPoints.size(), controlPoints[0].size());
146  //logger(INFO," %x% degrees",degreeU_,degreeV_);
147 }
void reverse(const MatrixType &m)
Definition: array_reverse.cpp:17

References k, reverse(), and w.

◆ 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
177  {
178  //JWB Known reasons why convergence might fail (or worse it doesn't fail but finds a wrong point):
179  // 1. Multiple same value knots (together with a low degree).
180  // 2. Multiple control points at the exact same position.
181  // 3. Really high value weights, or big difference in weights.
182  // 4. Too little starting points to start off iteration.
183  // 5. In general possibly a too low degree.
184  // In some cases there is a clear discontinuity, which then is the obvious reason for failure.
185  // In other cases this is not really clear, so the reason isn't quite clear.
186  // One last reason the iteration might fail is when it actually got quite close to the closest point, but the
187  // tolerance is set too tight. This failure does not cause huge problems though, as when the iteration fails the
188  // distance is calculated anyway. The tolerance value is pretty much picked from thin air, so improving it is fine.
189 
190  // Find the closest starting point
191  double u, v, minDist2 = constants::inf;
192  for (int i = 0; i < startingPoints_.size(); i++) {
193  const double dist2 = Vec3D::getLengthSquared(startingPoints_[i] - P);
194  if (dist2 < minDist2) {
195  u = startingKnotsU_[i];
196  v = startingKnotsV_[i];
197  minDist2 = dist2;
198  }
199  }
201  //Now do Newton-Raphson: Find closest point C(u) to P, see (6.6) in Nurbs book
202  // define f(u)=C'(u).(C(u)-P), find f(u)=0
203  // iterate u <- u-f(u)/f'(u)
204  //in 2D:
205  // define r(u,v) = S(u,v)-P
206  // f = r. dSdu, g = r. dSdv
207  // iterate u <- u + du, v <- v + dv
208  // J.[du;dv] = k
209  // J=[fu fv;gu gv], k=-[f;g]
210  std::array<std::array<Vec3D,3>,3> S;
211  //JWB The tolerance originally was pretty much grabbed from thin air. Multiplying it by 100 allowed for additional
212  // convergence (of the third convergence criterion: parameters fixed) for a bunch of positions.
213  const double tol = 2*std::numeric_limits<double>::epsilon() * 100;
214  const double tolSquared = mathsFunc::square<double>(tol);
215  Vec3D r;
216  double r1;
217  double previousU, previousV;
218  for (int i = 0; i < 15; ++i) {
220  r = S[0][0] - P;
221  r1 = r.getLengthSquared();
222  if (r1 < tolSquared) /*first convergence criterion: contact point on surface*/ {
223  distance = 0;
224  normal = r;
225  return true;
226  }
227  const double r2 = mathsFunc::square<double>(Vec3D::dot(r, S[1][0]))/(r1*S[1][0].getLengthSquared());
228  const double r3 = mathsFunc::square<double>(Vec3D::dot(r, S[0][1]))/(r1*S[0][1].getLengthSquared());
229  if (std::fabs(r2) < tolSquared && std::fabs(r3) < tolSquared) /*second convergence criterion: zero cosine*/ {
230  //you should exit the function here
231  if (r1 < radius * radius) {
232  logger(VERBOSE,"contact found at % after % iterations", S[0][0], i);
233  distance = sqrt(r1);
234  normal = r / distance;
235  return true;
236  } else {
237  logger(VERBOSE,"contact found, but too distant");
238  return false;
239  }
240  }
241  const double f = Vec3D::dot(r, S[1][0]);
242  const double g = Vec3D::dot(r, S[0][1]);
243  const double a = S[1][0].getLengthSquared() + Vec3D::dot(r, S[2][0]);
244  const double b = Vec3D::dot(S[1][0], S[0][1]) + Vec3D::dot(r, S[1][1]);
245  const double d = S[0][1].getLengthSquared() + Vec3D::dot(r, S[0][2]);
246  const double det = a * d - b * b;
247  const double du = (b * g - d * f) / det;
248  const double dv = (b * f - a * g) / det;
249 
250  previousU = u;
251  previousV = v;
252  u += du;
253  v += dv;
254 
255  // Keeping within bounds
256  Mdouble lbU = getLowerBoundU();
257  Mdouble ubU = getUpperBoundU();
258  Mdouble lbV = getLowerBoundV();
259  Mdouble ubV = getUpperBoundV();
260  if (u < lbU) {
261  u = closedInU_ ? ubU - fmod(lbU - u, ubU - lbU) : lbU;
262  }
263  else if (u > ubU) {
264  u = closedInU_ ? lbU + fmod(u - ubU, ubU - lbU) : ubU;
265  }
266  if (v < lbV) {
267  v = closedInV_ ? ubV - fmod(lbV - v, ubV - lbV) : lbV;
268  }
269  else if (v > ubV) {
270  v = closedInV_ ? lbV + fmod(v - ubV, ubV - lbV) : ubV;
271  }
272 
273  const double r4 = ((u - previousU) * S[1][0] + (v - previousV) * S[0][1]).getLengthSquared();
274  if (r4 < tolSquared) /*third convergence criterion: parameters fixed*/ {
275  if (r1 < radius * radius) {
276  logger(VERBOSE,"parameters fixed, contact at % after % iterations", S[0][0], i);
277  distance = sqrt(r1);
278  normal = r / distance;
279  return true;
280  } else {
281  logger(VERBOSE,"parameters fixed, but too distant");
282  return false;
283  }
284  }
285  }
286  /* iteration fails */
287  //JWB See comments at the start of this method for possible reasons for failure and how it might be solved.
288  // For now a warning is given and whatever was found so far is returned (which is basically rubbish).
289  logger(WARN,"P=%: Number of allowed iterations exceeded; this should never be reached", P);
290  if (r1 < radius * radius) {
291  logger(VERBOSE,"contact found at %", S[0][0]);
292  distance = sqrt(r1);
293  normal = r / distance;
294  return true;
295  } else {
296  logger(VERBOSE,"contact found, but too distant");
297  return false;
298  }
299 }
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
Logger< MERCURYDPM_LOGLEVEL > logger("MercuryKernel")
Definition of different loggers with certain modules. A user can define its own custom logger here.
@ WARN
@ VERBOSE
Scalar * b
Definition: benchVecAdd.cpp:17
std::vector< Mdouble > startingKnotsU_
Definition: NurbsSurface.h:193
void evaluateDerivatives(double u, double v, std::array< std::array< Vec3D, 3 >, 3 > &S) const
Definition: NurbsSurface.cc:308
bool closedInU_
make it a periodic system
Definition: NurbsSurface.h:187
std::vector< Vec3D > startingPoints_
Definition: NurbsSurface.h:192
double getLowerBoundV() const
Definition: NurbsSurface.h:110
double getUpperBoundU() const
Definition: NurbsSurface.h:104
bool closedInV_
Definition: NurbsSurface.h:187
double getLowerBoundU() const
Definition: NurbsSurface.h:98
double getUpperBoundV() const
Definition: NurbsSurface.h:116
std::vector< Mdouble > startingKnotsV_
Definition: NurbsSurface.h:194
Mdouble getLengthSquared() const
Calculates the squared length of this Vec3D: .
Definition: Vector.cc:164
static Mdouble dot(const Vec3D &a, const Vec3D &b)
Calculates the dot product of two Vec3D: .
Definition: Vector.cc:56
static int f(const TensorMap< Tensor< int, 3 > > &tensor)
Definition: cxx11_tensor_map.cpp:237
const Scalar * a
Definition: level2_cplx_impl.h:32
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 fmod(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:648
double P
Uniform pressure.
Definition: TwenteMeshGluing.cpp:77
r
Definition: UniformPSDSelfTest.py:20
radius
Definition: UniformPSDSelfTest.py:15
void normal(const Vector< double > &x, Vector< double > &normal)
Definition: free_surface_rotation.cc:65
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117
const Mdouble inf
Definition: GeneralDefine.h:23
double epsilon
Definition: osc_ring_sarah_asymptotics.h:43

References a, b, Vec3D::dot(), oomph::SarahBL::epsilon, f(), boost::multiprecision::fabs(), Eigen::bfloat16_impl::fmod(), Vec3D::getLengthSquared(), i, constants::inf, logger, WallFunction::normal(), Global_Physical_Variables::P, UniformPSDSelfTest::r, UniformPSDSelfTest::radius, oomph::QuadTreeNames::S, sqrt(), v, 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
98  {
99  return knotsU_[degreeU_];
100  }

References degreeU_, and knotsU_.

Referenced by NurbsWall::writeVTK().

◆ getLowerBoundV()

double NurbsSurface::getLowerBoundV ( ) const
inline
Returns
Lowest allowed value for v
110  {
111  return knotsV_[degreeV_];
112  }

References degreeV_, and knotsV_.

Referenced by NurbsWall::writeVTK().

◆ getUpperBoundU()

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

References degreeU_, and knotsU_.

Referenced by NurbsWall::writeVTK().

◆ getUpperBoundV()

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

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.

451 {
452  // Add degree-1 amount of control points to the back.
453  wrapAroundInU(degreeU_ - 1, 0, true);
454  setClosedInU(true);
455 }
void setClosedInU(bool closedInU)
Definition: NurbsSurface.cc:121
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:464

◆ 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.

458 {
459  // Add degree-1 amount of control points to the back.
460  wrapAroundInV(degreeV_ - 1, 0, true);
461  setClosedInV(true);
462 }
void setClosedInV(bool closedInV)
Definition: NurbsSurface.cc:125
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:517

◆ 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.

437 {
438  // Add degree-1 amount of control points to the front and back.
439  wrapAroundInU(degreeU_ - 1, degreeU_ - 1);
440  periodicInU_ = true;
441 }
bool periodicInU_
Definition: NurbsSurface.h:189

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.

444 {
445  // Add degree-1 amount of control points to the front and back.
446  wrapAroundInV(degreeV_ - 1, degreeV_ - 1);
447  periodicInV_ = true;
448 }
bool periodicInV_
Definition: NurbsSurface.h:189

Referenced by WearableNurbsWall::set().

◆ moveControlPoint()

void NurbsSurface::moveControlPoint ( unsigned int  indexU,
unsigned int  indexV,
Vec3D  dP,
bool  includingClosedOrPeriodic 
)
638 {
639  // When the surface is closed or periodic, there might be other control points which should also move.
640  // To that end, store all the indices in u and v in two vectors, starting with the current one.
641  // Then later each combination of indices of u and v is moved.
642  std::vector<unsigned int> moveIndexU, moveIndexV;
643  moveIndexU.push_back(indexU);
644  moveIndexV.push_back(indexV);
645 
646  if (includingClosedOrPeriodic)
647  {
648  // Assuming either closed or periodic in u/v, not both.
649  unsigned int lowestIndexU, lowestIndexV, shiftU, shiftV;
650 
651  // For closed, degree-1 control points were added to the end
652  if (closedInU_)
653  {
654  lowestIndexU = degreeU_ - 1;
655  shiftU = controlPoints_.size() - degreeU_;
656  }
657  if (closedInV_)
658  {
659  lowestIndexV = degreeV_ - 1;
660  shiftV = controlPoints_[0].size() - degreeV_;
661  }
662 
663  // For periodic, degree-1 control points were added in front and to the end
664  if (periodicInU_)
665  {
666  lowestIndexU = 2 * (degreeU_ - 1);
667  shiftU = controlPoints_.size() - 2 * degreeU_ + 1;
668  }
669  if (periodicInV_)
670  {
671  lowestIndexV = 2 * (degreeV_ - 1);
672  shiftV = controlPoints_[0].size() - 2 * degreeV_ + 1;
673  }
674 
675  if (closedInU_ || periodicInU_)
676  {
677  // Note, no else if, because technically it might be possible for e.g. the middle control point to be the degree-1
678  // from the starting index as well as the end index. Meaning that on both sides other control points need moving.
679  if (indexU <= lowestIndexU)
680  moveIndexU.push_back(indexU + shiftU);
681  if (indexU >= shiftU)
682  moveIndexU.push_back(indexU - shiftU);
683  }
684  if (closedInV_ || periodicInV_)
685  {
686  if (indexV <= lowestIndexV)
687  moveIndexV.push_back(indexV + shiftV);
688  if (indexV >= shiftV)
689  moveIndexV.push_back(indexV - shiftV);
690  }
691  }
692 
693  for (unsigned int u : moveIndexU)
694  for (unsigned int v : moveIndexV)
695  controlPoints_[u][v] += dP;
696 }

References v.

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
41 {
42  controlPoints_ = controlPoints;
43  weights_ = weights;
44  knotsU_ = knotsU;
45  knotsV_ = knotsV;
46 
47  if ( controlPoints.size()<2 || controlPoints[0].size()<2 ) {
48  logger(ERROR,"At least two control points are necessary");
49  }
50 
51  if (std::any_of(controlPoints.begin(), controlPoints.end(),
52  [controlPoints](auto v) { return v.size() != controlPoints[0].size(); }))
53  {
54  logger(ERROR, "All rows of the control matrix must have the same size");
55  }
56 
57  if (controlPoints.size() != weights.size() ||
58  std::any_of(weights.begin(), weights.end(),
59  [controlPoints](auto v) { return v.size() != controlPoints[0].size(); }))
60  {
61  logger(ERROR, "Number of control points and weights must match");
62  }
63 
64  if (knotsU.size() < 2 || knotsV.size() < 2)
65  {
66  logger(ERROR, "At least two knots are necessary");
67  }
68 
69  if (!isKnotVectorMonotonic(knotsU) || !isKnotVectorMonotonic(knotsV))
70  {
71  logger(ERROR, "Knot vector(s) is not monotonic");
72  }
73 
74  if (knotsU.size() - controlPoints.size() < 2 || knotsV.size() - controlPoints[0].size() - 1 < 1)
75  {
76  logger(ERROR, "Degree has to be at least 1");
77  }
78 
79  // Reset the u/v interval to [0,1]
82 
83  degreeU_ = knotsU.size() - controlPoints.size() - 1;
84  degreeV_ = knotsV.size() - controlPoints[0].size() - 1;
85 
86  logger(INFO,"Created Nurbs surface.");
87  logger(INFO," %x% knots",knotsU.size(), knotsV.size());
88  logger(INFO," %x% control points",controlPoints.size(), controlPoints[0].size());
89  logger(INFO," %x% degrees",degreeU_,degreeV_);
90 
91  //\todo TW
92  closedInU_ = false;
93  closedInV_ = false;
94  periodicInU_ = false;
95  periodicInV_ = false;
96 
97  // This simply evaluates the positions for a bunch of u's and v's and stores them.
98  // As it is now, the u's and v's are simply taken uniform.
99  // The more points the better (sort of). Too little points can cause a possible convergence to a non-global minimum,
100  // or possible no convergence at all.
101  // For smooth surfaces this already helps a lot. For non-smooth surfaces it most likely doesn't suffice.
102  //\todo JWB The convergence still fails from time to time
103  //\todo JWB These aren't updated when the surface changes shape during simulation (moving control points)
104  unsigned nu = knotsU_.size() * 3;
105  unsigned nv = knotsV_.size() * 3;
106  startingPoints_.clear();
107  startingKnotsU_.clear();
108  startingKnotsV_.clear();
109  for (double i = 0; i <= nu; i++) {
110  double u = getLowerBoundU() + (getUpperBoundU() - getLowerBoundU()) * i / nu;
111  for (double j = 0; j <= nv; j++) {
112  double v = getLowerBoundV() + (getUpperBoundV() - getLowerBoundV()) * j / nv;
113  Vec3D p = evaluate(u, v);
114  startingPoints_.push_back(p);
115  startingKnotsU_.push_back(u);
116  startingKnotsV_.push_back(v);
117  }
118  }
119 }
@ INFO
@ ERROR
float * p
Definition: Tutorial_Map_using.cpp:9
Vec3D evaluate(double u, double v) const
Definition: NurbsSurface.cc:149
void normalizeKnotVector(std::vector< Mdouble > &knots)
Resets the knot vector to the interval [0, 1].
Definition: NurbsUtils.cc:267
bool isKnotVectorMonotonic(const std::vector< double > &knots)
Definition: NurbsUtils.cc:12

References ERROR, NurbsUtils::evaluate(), i, INFO, NurbsUtils::isKnotVectorMonotonic(), j, logger, NurbsUtils::normalizeKnotVector(), p, v, and calibrate::weights.

◆ setClosedInU()

void NurbsSurface::setClosedInU ( bool  closedInU)
121  {
122  closedInU_ = closedInU;
123 }

◆ setClosedInV()

void NurbsSurface::setClosedInV ( bool  closedInV)
125  {
126  closedInV_ = closedInV;
127 }

◆ splitSurface()

void NurbsSurface::splitSurface ( int  spanU,
int  spanV 
)
inline
81  {
82 
83  }

◆ 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
558 {
559  // Algorithm from Nurbs book A12.1 extended to surfaces
560 
561  std::vector<Mdouble>& knots = inU ? knotsU_ : knotsV_;
562  unsigned int n = inU ? controlPoints_.size() - 1 : controlPoints_[0].size() - 1;
563  unsigned int p = inU ? degreeU_ : degreeV_;
564 
565  if (atStart)
566  {
567  // Unclamp at left end
568  for (int i = 0; i <= p-2; i++)
569  {
570  knots[p - i - 1] = knots[p - i] - (knots[n - i + 1] - knots[n - i]);
571  int k = p - 1;
572  for (int j = i; j >= 0; j--)
573  {
574  Mdouble alpha = (knots[p] - knots[k]) / (knots[p + j + 1] - knots[k]);
575  k--;
576 
577  // Update changed for each control point and weight in other direction
578  // Only difference between inU or not is the indices are swapped: [j][l] -> [l][j]
579  if (inU)
580  {
581  for (int l = 0; l < controlPoints_[0].size(); l++)
582  {
583  controlPoints_[j][l] = (controlPoints_[j][l] - alpha * controlPoints_[j+1][l]) / (1.0 - alpha);
584  weights_[j][l] = (weights_[j][l] - alpha * weights_[j+1][l]) / (1.0 - alpha);
585  }
586  }
587  else
588  {
589  for (int l = 0; l < controlPoints_.size(); l++)
590  {
591  controlPoints_[l][j] = (controlPoints_[l][j] - alpha * controlPoints_[l][j+1]) / (1.0 - alpha);
592  weights_[l][j] = (weights_[l][j] - alpha * weights_[l][j+1]) / (1.0 - alpha);
593  }
594  }
595  }
596  }
597  // Set first knot
598  knots[0] = knots[1] - (knots[n - p + 2] - knots[n - p + 1]);
599  }
600  else
601  {
602  // Unclamp at right end
603  for (int i = 0; i <= p-2; i++)
604  {
605  knots[n + i + 2] = knots[n + i + 1] + (knots[p + i + 1] - knots[p + i]);
606  for (int j = 1; j >= 0; j--)
607  {
608  Mdouble alpha = (knots[n + 1] - knots[n - j]) / (knots[n - j + i + 2] - knots[n - j]);
609 
610  // Update changed for each control point and weight in other direction
611  // Only difference between inU or not is the indices are swapped: [j][l] -> [l][j]
612  if (inU)
613  {
614  for (int l = 0; l < controlPoints_[0].size(); l++)
615  {
616  controlPoints_[n - j][l] = (controlPoints_[n - j][l] - (1.0 - alpha) * controlPoints_[n - j -1][l]) / alpha;
617  weights_[n - j][l] = (weights_[n - j][l] - (1.0 - alpha) * weights_[n - j -1][l]) / alpha;
618  }
619  }
620  else
621  {
622  for (int l = 0; l < controlPoints_.size(); l++)
623  {
624  controlPoints_[l][n - j] = (controlPoints_[l][n - j] - (1.0 - alpha) * controlPoints_[l][n - j -1]) / alpha;
625  weights_[l][n - j] = (weights_[l][n - j] - (1.0 - alpha) * weights_[l][n - j -1]) / alpha;
626  }
627  }
628  }
629  }
630  // Set last knot
631  knots[n + p + 1] = knots[n + p] + (knots[2*p] - knots[2*p - 1]);
632  }
633 
634  normalizeKnotVector(knots);
635 }
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
RealScalar alpha
Definition: level1_cplx_impl.h:151

References alpha, i, j, k, n, NurbsUtils::normalizeKnotVector(), and p.

◆ 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
465 {
466  // This method copies the given number of control points from the start to the end and vice versa (not counting the
467  // first and last control point).
468  // The first and last control point are used to know the amount that the control points to be copied have to be
469  // shifted by. In case of closing a surface (circle like shape) the shifted distance is simply 0.
470  // The knot vector is updated in such a way that only the start and end of the shape might be influenced (both ends
471  // should be uniform), however the inner shape remains intact.
472 
473  // A vector of offsets, which is the difference between the first and last row of control points.
474  // These are the values the copied control points need to be shifted by.
475  std::vector<Vec3D> offsets;
476  offsets.reserve(controlPoints_[0].size());
477  for (int j = 0; j < controlPoints_[0].size(); j++)
478  {
479  offsets.push_back(controlPoints_.back()[j] - controlPoints_.front()[j]);
480  }
481 
482  // Temporarily store "ghost" control points and weights to be added in front
483  std::vector<std::vector<Vec3D>> frontControlPoints;
484  std::vector<std::vector<Mdouble>> frontWeights;
485  // Get the numEndToStart amount, in order, ignoring the last one
486  for (unsigned int i = numEndToStart; i > 0; i--)
487  {
488  std::vector<Vec3D> tmpCP = controlPoints_[controlPoints_.size() - 1 - i];
489  for (int j = 0; j < tmpCP.size(); j++)
490  {
491  tmpCP[j] -= offsets[j];
492  }
493  frontControlPoints.push_back(tmpCP);
494  frontWeights.push_back(weights_[weights_.size() - 1 - i]);
495  }
496 
497  // Add "ghost" control points and weights at the back
498  // Get the numStartToEnd amount, in order, ignoring the first one
499  for (int i = 1; i <= numStartToEnd; i++)
500  {
501  std::vector<Vec3D> tmpCP = controlPoints_[i];
502  for (int j = 0; j < tmpCP.size(); j++)
503  {
504  tmpCP[j] += offsets[j];
505  }
506  controlPoints_.push_back(tmpCP);
507  weights_.push_back(weights_[i]);
508  }
509 
510  // Now actually add the "ghost" control points and weights in front
511  controlPoints_.insert(controlPoints_.begin(), frontControlPoints.begin(), frontControlPoints.end());
512  weights_.insert(weights_.begin(), frontWeights.begin(), frontWeights.end());
513 
514  extendKnotVector(knotsU_, degreeU_, numEndToStart, numStartToEnd, forceBothEndsUniform);
515 }
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:283

References NurbsUtils::extendKnotVector(), i, j, and size.

◆ 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
518 {
519  // This method copies the given number of control points from the start to the end and vice versa (not counting the
520  // first and last control point).
521  // The first and last control point are used to know the amount that the control points to be copied have to be
522  // shifted by. In case of closing a surface (circle like shape) the shifted distance is simply 0.
523  // The knot vector is updated in such a way that only the start and end of the shape might be influenced (both ends
524  // should be uniform), however the inner shape remains intact.
525 
526  for (int i = 0; i < controlPoints_.size(); i++)
527  {
528  // Current offset
529  Vec3D offset = controlPoints_[i].back() - controlPoints_[i].front();
530 
531  // Temporarily store "ghost" control points and weights to be added in front
532  std::vector<Vec3D> frontControlPoints;
533  std::vector<Mdouble> frontWeights;
534  // Get the numEndToStart amount, in order, ignoring the last one
535  for (unsigned int j = numEndToStart; j > 0; j--)
536  {
537  frontControlPoints.push_back(controlPoints_[i][controlPoints_[i].size() - 1 - j] - offset);
538  frontWeights.push_back(weights_[i][weights_[i].size() - 1 - j]);
539  }
540 
541  // Add "ghost" control points and weights at the back
542  // Get the numStartToEnd amount, in order, ignoring the first one
543  for (int j = 1; j <= numStartToEnd; j++)
544  {
545  controlPoints_[i].push_back(controlPoints_[i][j] + offset);
546  weights_[i].push_back(weights_[i][j]);
547  }
548 
549  // Now actually add the "ghost" control points and weights in front
550  controlPoints_[i].insert(controlPoints_[i].begin(), frontControlPoints.begin(), frontControlPoints.end());
551  weights_[i].insert(weights_[i].begin(), frontWeights.begin(), frontWeights.end());
552  }
553 
554  extendKnotVector(knotsV_, degreeV_, numEndToStart, numStartToEnd, forceBothEndsUniform);
555 }

References NurbsUtils::extendKnotVector(), i, j, and size.

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
356 {
357  os << "mu " << a.knotsU_.size() << ' ';
358  os << "mv " << a.knotsV_.size() << ' ';
359  os << "nu " << a.controlPoints_.size() << ' ';
360  os << "nv " << a.controlPoints_[0].size() << ' ';
361  os << "knotsU ";
362  for (const auto k : a.knotsU_) os << k << ' ';
363  os << "knotsV ";
364  for (const auto k : a.knotsV_) os << k << ' ';
365  os << "controlPoints ";
366  for (const auto& cp0 : a.controlPoints_) for (const auto cp : cp0) os << cp << ' ';
367  os << "weights ";
368  for (const auto& w0 : a.weights_) for (const auto w : w0) os << w << ' ';
369  os << "closedInUV " << a.closedInU_ << ' ' << a.closedInV_;
370  os << " periodicInUV " << a.periodicInU_ << ' ' << a.periodicInV_;
371  return os;
372 }

◆ 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
382 {
383  std::string dummy;
384  unsigned mu, mv, nu, nv;
385  is >> dummy >> mu;
386  is >> dummy >> mv;
387  is >> dummy >> nu;
388  is >> dummy >> nv;
389 
390  is >> dummy;
391  std::vector<double> knotsU;
392  knotsU.resize(mu);
393  for (auto& k : knotsU) is >> k;
394 
395  is >> dummy;
396  std::vector<double> knotsV;
397  knotsV.resize(mv);
398  for (auto& k : knotsV) is >> k;
399 
400  is >> dummy;
401  std::vector<std::vector<Vec3D>> controlPoints;
402  controlPoints.resize(nu);
403  for (auto& cp0 : controlPoints) {
404  cp0.resize(nv);
405  for (auto& cp : cp0) is >> cp;
406  }
407 
408  is >> dummy;
409  std::vector<std::vector<double>> weights;
410  weights.resize(nu);
411  for (auto& w0 : weights) {
412  w0.resize(nv);
413  for (auto& w : w0) is >> w;
414  }
415 
416  a.set(knotsU,knotsV,controlPoints,weights);
417 
418  // After setting, since in that method the defaults are set to false.
419  // Also, check if dummy variable exist, for backwards compatibility.
420  is >> dummy;
421  if (dummy == "closedInUV")
422  {
423  is >> a.closedInU_;
424  is >> a.closedInV_;
425  }
426  is >> dummy;
427  if (dummy == "periodicInUV")
428  {
429  is >> a.periodicInU_;
430  is >> a.periodicInV_;
431  }
432 
433  return is;
434 }
std::complex< double > mu
Definition: time_harmonic_fourier_decomposed_linear_elasticity/cylinder/cylinder.cc:52
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286

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: