ON_NurbsSurface::InsertKnot does not work as expected

Hello,

As far as I know, the difference between a function called SetKnot and InsertKnot is that a set function requires that memory has already been allocated, whereas an insert/append/push_back is able to extend the memory in order to insert a new element.

Keeping this in mind I was very surprised to see that InsertKnot does not extend the Knot vector and does not insert a new knot value. Also, I was a bit puzzled when I tried to set a value on unallocated memory and saw that the program was not crashing.

What am I missing here?

ON_NurbsSurface nurbs_surface( degree : 3, true, degree+1 : 4, degree+1 : 4, u_size : 11, v_size : 7 );

// size of each custom knot vector is n = #cvs + #degree + 1 (https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
auto ku = ct::generate_knotvector( degree : 3, u_size : 11 );
auto kv = ct::generate_knotvector( degree : 3, v_size : 7 );

std::cout<< "Ku size = " << ku.size() <<std::endl; // out: 15
std::cout<< "Kv size = " << kv.size() <<std::endl; // out: 11

std::cout<< "(B) ON Ku size = " << nurbs_surface.KnotCount(0) <<std::endl; // out: 13
std::cout<< "(B) ON Kv size = " << nurbs_surface.KnotCount(1) <<std::endl; // out: 9

for (int i = 0; i < ku.size(); ++i)
{
   if (i < nurbs_surface.KnotCount(0))
   {
      double old_knot = nurbs_surface.Knot(0,i);
      nurbs_surface.SetKnot(0, i, ku[i]);
      std::cout<< "setting knot U[" << i << "] = " << old_knot << " -> " << nurbs_surface.Knot(0,i) <<std::endl;
   }
   else
   {
      nurbs_surface.InsertKnot(0,ku[i]);
      std::cout<< "inserting knot U[" << i << "] = " << ku[i] << " -> " << nurbs_surface.Knot(0,i) <<std::endl;
   }
}

for (int i = 0; i < kv.size(); ++i)
{
   if ( i < nurbs_surface.KnotCount(1) )
      nurbs_surface.SetKnot(1,i,kv[i]);
   else
      nurbs_surface.InsertKnot(1,kv[i]);
}

std::cout<< "(A) ON Ku size = " << nurbs_surface.KnotCount(0) <<std::endl; //out: 13 ???
std::cout<< "(A) ON Kv size = " << nurbs_surface.KnotCount(1) <<std::endl; //out: 9 ???

I have also tried using:

nurbs_surface.ReserveKnotCapacity(0, ku.size());
nurbs_surface.ReserveKnotCapacity(1, kv.size());

without much success, i.e. did not make a difference.

Full output:

u_size = 11
v_size = 7
Ku size = 15
Kv size = 11

(B) ON Ku size = 13
(B) ON Kv size = 9

setting knot U[0] = -0.54400643667890747 -> 0.00000000000000000
setting knot U[1] = 11.83328550806970547 -> 0.00000000000000000
setting knot U[2] = 10.02935199996707283 -> 0.00000000000000000
setting knot U[3] = -0.53781791885622077 -> 0.00000000000000000
setting knot U[4] = 12.21595752388964939 -> 0.12500000000000000
setting knot U[5] = 10.00900402987057625 -> 0.25000000000000000
setting knot U[6] = -0.49935833839056237 -> 0.37500000000000000
setting knot U[7] = 12.54947070285905752 -> 0.50000000000000000
setting knot U[8] = 9.88254816181600759 -> 0.62500000000000000
setting knot U[9] = -0.46806856448411210 -> 0.75000000000000000
setting knot U[10] = 12.86163479356979344 -> 0.87500000000000000
setting knot U[11] = 9.77966676080580655 -> 1.00000000000000000
setting knot U[12] = 0.15298294665255666 -> 1.00000000000000000
inserting knot U[13] = 1.00000000000000000 -> 0.00000000000000000
inserting knot U[14] = 1.00000000000000000 -> 0.00000000000000000

(A) ON Ku size = 13
(A) ON Kv size = 9

Hi @Constantinos_Glynos,

Here is how can define a NURBS curve:

  // Specify dimension, degree and number of control points.
  // The degree must be >= 1 and the number of control points
  // must be >= (degree + 1). The number of knots is always
  // (number of control points + degree - 1).
  int dimension = 3;
  bool bIsRational = true;
  int degree = 2;
  int order = degree + 1;
  int cv_count = 9;
  int knot_count = cv_count + degree - 1;

  // Make a rational, degree 2 NURBS curve with 9 control points
  ON_NurbsCurve nc(dimension, bIsRational, order, cv_count);

  // Set the 9 control points
  nc.SetCV(0, ON_4dPoint(1.0, 0.0, 0.0, 1.0));
  nc.SetCV(1, ON_4dPoint(0.707107, 0.707107, 0.0, 0.707107));
  nc.SetCV(2, ON_4dPoint(0.0, 1.0, 0.0, 1.0));
  nc.SetCV(3, ON_4dPoint(-0.707107, 0.707107, 0.0, 0.707107));
  nc.SetCV(4, ON_4dPoint(-1.0, 0.0, 0.0, 1.0));
  nc.SetCV(5, ON_4dPoint(-0.707107, -0.707107, 0.0, 0.707107));
  nc.SetCV(6, ON_4dPoint(0.0, -1.0, 0.0, 1.0));
  nc.SetCV(7, ON_4dPoint(0.707107, -0.707107, 0.0, 0.707107));
  nc.SetCV(8, ON_4dPoint(1.0, 0.0, 0.0, 1.0));

  // Set the 10 knots
  nc.SetKnot(0, 0.0);
  nc.SetKnot(1, 0.0);
  nc.SetKnot(2, 0.5*ON_PI);
  nc.SetKnot(3, 0.5*ON_PI);
  nc.SetKnot(4, ON_PI);
  nc.SetKnot(5, ON_PI);
  nc.SetKnot(6, 1.5*ON_PI);
  nc.SetKnot(7, 1.5*ON_PI);
  nc.SetKnot(8, 2.0*ON_PI);
  nc.SetKnot(9, 2.0*ON_PI);

  if (nc.IsValid())
  {
    // TODO...
  }

Does this help?

– Dale

Hi Dale! Thank you for your response. Unfortunately no, because I want to insert a knot in the existing array of knots, not set their values. Is there a way to accomplish this?

Hi @Constantinos_Glynos,

Use ON_NurbsCurve::InsertKnot like you’ve already mentioned.

ON_NurbsCurve* TestInsertKnot(
  const ON_NurbsCurve* pCurve, 
  double t, 
  int knot_multiplicity
)
{
  if (pCurve && pCurve->Domain().Includes(t))
  {
    ON_NurbsCurve* pNewCurve = new ON_NurbsCurve(*pCurve);
    pNewCurve->InsertKnot(t, min(pNewCurve->Degree(), knot_multiplicity));
    return pNewCurve;
  }
  return nullptr;
}

– Dale