# Manually Coding B-Splines

**URL:** <https://discourse.processing.org/t/manually-coding-b-splines/35673>\
**Category:** Coding Questions\
**Created:** [March 11, 2022, 7:10am UTC](https://discourse.processing.org/t/manually-coding-b-splines/35673 "2022-03-11T07:10:44Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![TheSynchronomicon](https://avatars.discourse-cdn.com/v4/letter/t/b487fb/32.png) [@TheSynchronomicon](https://discourse.processing.org/u/TheSynchronomicon)\
**Post date:** [March 11, 2022, 7:10am UTC](https://discourse.processing.org/t/manually-coding-b-splines/35673/1 "2022-03-11T07:10:44Z")

</div>

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.

```auto
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;
}

```

---

<div class="post-metadata">

**Author:** ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)\
**Post date:** [March 11, 2022, 10:12am UTC](https://discourse.processing.org/t/manually-coding-b-splines/35673/2 "2022-03-11T10:12:01Z")

</div>

> [@TheSynchronomicon](#):
>
> ` if (t == 1){`

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**

> [@TheSynchronomicon](#):
>
> ` if (d == 0){`

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

These 2 blocks could each be a function.

---

<div class="post-metadata">

**Author:** ![quark](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/quark/32/26_2.png) [@quark](https://discourse.processing.org/u/quark)\
**Post date:** [March 11, 2022, 10:35am UTC](https://discourse.processing.org/t/manually-coding-b-splines/35673/3 "2022-03-11T10:35:17Z")

</div>

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

```auto
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

```auto
t= 0

```

notice it didn’t find t == 1

You could try something like

```auto
  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

---

<div class="post-metadata">

**Author:** ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)\
**Post date:** [March 11, 2022, 4:05pm UTC](https://discourse.processing.org/t/manually-coding-b-splines/35673/4 "2022-03-11T16:05:36Z")

</div>

> [@quark](#):
>
> …that the problem is almost certainly to do with rounding errors -

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_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length) won’t track them down:

> **[Array - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#notes)**
>
> The Array object, as with arrays in other programming languages, enables storing a collection of multiple items under a single variable name, and has members for performing common array operations.

And if an array’s [_length_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/length) is fixed & it stores just numbers I advise to use a TypedArray instead:

> **[TypedArray - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)**
>
> A TypedArray object describes an array-like view of an
> underlying binary data buffer.
> There is no global property named TypedArray, nor is there a
> directly visible TypedArray constructor. Instead, there are a number of
> different global...

In case the OP wanna stick w/ fractional “indices”, a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) container is the sane choice, b/c it can track down its _size_:

> **[Map.prototype.size - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size)**
>
> The size accessor property of Map instances returns the number of elements in this map.
