Manually Coding B-Splines

So I’ve been trying to manually code a b-spline for a set of any number of user defined points, but I’ve hit a bit of a snag when it comes to the actually coding part of it. I first tested it with a specific knot vector and time value, t, as an example and it matched up for the most part. However, now that I’m trying to do it from a span from t = 0 to 1, it’s suddenly stopped working as I expected. I have tried to checking each individual array manually by printing them as they come and despite the fact that the N array has values in it, I can’t seem to get the point array to have values no matter what I’ve tried, and the line function just doesn’t work at all because of it. I’ve looked through it several times, and I am at a loss for what it is I’m missing or needs work so if anyone could just point out what it is the problem in the code would be very helpful. For context, I’ve included both the main B-spline code as well as the code that creates the knot vector below.

function drawBSpline()
{
  stroke('white');
  strokeWeight(1);
  p = 2;
  l = pointArray.length;
  posx = []; posy = [];
  um = knotVector(l,p); 
  for (var t = 0; t < 1.001; t += 0.01){
    if (t == 0){
      posx[t] = pointArray[0].x;
      posy[t] = pointArray[0].y;
    }
    if (t == 1){
      posx[t] = pointArray[pointArray.length - 1].x;
      posy[t] = pointArray[pointArray.length - 1].y;
    }
    for (var f = 0; f < um.length; f++){
      if(t < um[f]){
        N = make2DArray(f,p+1);
        break;
      }
    }
    for (var d = 0; d < p+1; d++){
      if (d == 0){
         for (var i = 0; i < um.length; i++){
           if (t >= um[i] && t < um[i+1]){
             N[d][i] = 1;
           }
           else if (t == 0){
             N[d][0] = 1;
             for (var i = 1; i < um.length - p + 1; i++){
               N[d][i] = 0;
             }
             break;
           }
           else if (t == 1){
             N[d][um.length - p] = 1;
             for (j = um.length - p - 1; j >= 0; j--){
               N[d][j] = 0;
             }
             break;
           }
           else if (i > f+1){
             break;
           }
           else {
             N[d][i] = 0;
           } 
         }
      }
      else {
        for (var i = 0; i < um.length; i++){
          a1 = (t - um[i]) / (um[i+d] - um[i]);
          a2 = (um[i+d+1] - t) / (um[i+d+1] - um[i+1]);
          if (a1 == Infinity || a1 == -Infinity){
            a1 = 0;
          }
          if (a2 == Infinity || a2 == -Infinity){
            a2 = 0;
          }
          if (i > f+1){
            break;
          }
          N[d][i] = a1*N[d-1][i] + a2*N[d-1][i+1];
        }
      }
    }
	for (var i = 0; i < pointArray.length; i++){
	  posx[t] += pointArray[i].x * N[p][i];
	  posy[t] += pointArray[i].y * N[p][i];
	}
  }
  for (var x = 0; x < posx.length-1; x++){
    line(posx[x],posy[x],posx[x+1],posy[x+1])
  }
}

function knotVector(l,p){
  u = new Array(l+p+1);
  if (u.length < 2*(p+1)){
    return false;
  }
  for (var c = 0; c < u.length; c++){
    if (c <= p){
      u[c] = 0;
    }
    else if (c >= u.length - p - 1){
      u[c] = 1;
    }
    else {
      u[c] = (c - p) / (u.length - 2*p - 1);
    }
  }
  return u;
}

Those won’t work maybe because of rounding errors (you add t += 0.01)

Remark

Anyway. Your code is hard to read (the function is too long) and we can’t run it, so we can’t see what’s going on. You won’t get too much help. In general, complex code for a special subject is hard to get help on.
Try to post a running version.

Remark

After this, you see a very long block and then else and a very long block.

These 2 blocks could each be a function.

2 Likes

I agree with @Chrisir that the problem is almost certainly to do with rounding errors - try this

function setup() {
  for (var t = 0; t < 1.001; t += 0.01) {
    if (t == 0 || t == 1)
      console.log("t = " + t);
  }
}

function draw() {
}

It gives the output

t= 0

notice it didn’t find t == 1

You could try something like

  for (var tt = 0; tt <= 100; t t++){
    var t = 1/100;
    if (tt == 0){
      posx[t] = pointArray[0].x;
      posy[t] = pointArray[0].y;
    }
    if (tt == 1){
      posx[t] = pointArray[pointArray.length - 1].x;
      posy[t] = pointArray[pointArray.length - 1].y;
    }

The loop index tt will be treated as an integer

It’s more than that. That code is also adding fractional properties to the arrays!

All array indices gotta be an integer value; otherwise they become regular properties & the accessor property length won’t track them down:

And if an array’s length is fixed & it stores just numbers I advise to use a TypedArray instead:

In case the OP wanna stick w/ fractional “indices”, a Map container is the sane choice, b/c it can track down its size:

1 Like