0

I've written a function that calculates the points for a b-spline curve, drawing the curve on the screen as the points are generated using the built in setinterval() function. The problem is that when these points are drawn to the screen, there is merely a portion or small segment of the curve that is visible moving along the interpolated curve, but the entire curve doesn't persist as the curve is plotted. In addition to this, the curve will trail off the screen once it reaches a certain point. The code appears to be designed in such a way that the function will end (i.e. the interval will be cleared) once the value of t reaches 1, but for some reason, the function continues to be called, causing the curve to continue being animated off screen.

Conversely, I have another function that simply uses a for loop to draw the curve with the same points, but with this function a small curve is drawn once, clearly fitting on the screen. Below is the code for both functions:

       //function that draws the spline one time
       function bspline(context, points) {
       context.beginPath();
       for (var t = 0; t < 1; t += 0.1) {
           var ax = (-points[0].x + 3 * points[1].x - 3 * points[2].x + points[3].x) / 6;
           var ay = (-points[0].y + 3 * points[1].y - 3 * points[2].y + points[3].y) / 6;
           var bx = (-points[0].x - 2 * points[1].x + points[2].x) / 2;
           var by = (-points[0].y - 2 * points[1].y + points[2].y) / 2;
           var cx = (-points[0].x + points[2].x) / 2;
           var cy = (-points[0].y + points[2].y) / 2;
           var dx = (points[0].x + 4 * points[1].x + points[2].x) / 6;
           var dy = (points[0].y + 4 * points[1].y + points[2].y) / 6;
           context.moveTo(
               ax*Math.pow(t,3) + bx*Math.pow(t,2) + cx*t + dx,
               ay*Math.pow(t,3) + by*Math.pow(t,2) + cy*t + dy
               );
           context.lineTo(
               ax*Math.pow(t+0.1, 3) + bx*Math.pow(t+0.1, 2) + cx*(t+0.1) + dx,
               ay*Math.pow(t+0.1,3) + by*Math.pow(t+0.1,2)  +  cy*(t+0.1) + dy
               );
           //m.translate(ax * Math.pow(t + 0.1, 3) + bx * Math.pow(t + 0.1, 2) + cx * (t + 0.1) + dx, ay * Math.pow(t + 0.1, 3) + by * Math.pow(t + 0.1, 2) + cy * (t + 0.1) + dy,0);
       }
       context.stroke();
   }


var interval;
   //sets the interval for the spline to be animated over
   function drawSpline(context, points, newpts) {
       interval = setInterval(splineAnim(context, points, newpts), 1600.67);
       console.log("interval set");
   }
   var t = 0;

   //determines and draws the points of the spline
   function splineAnim(context, points, newpts) {
       // Draw curve segment
       var ax = (-points[0].x + 3 * points[1].x - 3 * points[2].x + points[3].x) / 6;
       var ay = (-points[0].y + 3 * points[1].y - 3 * points[2].y + points[3].y) / 6;
       var bx = (-points[0].x - 2 * points[1].x + points[2].x) / 2;
       var by = (-points[0].y - 2 * points[1].y + points[2].y) / 2;
       var cx = (-points[0].x + points[2].x) / 2;
       var cy = (-points[0].y + points[2].y) / 2;
       var dx = (points[0].x + 4 * points[1].x + points[2].x) / 6;
       var dy = (points[0].y + 4 * points[1].y + points[2].y) / 6;
       context.beginPath();
       context.moveTo(
           ax * Math.pow(t, 3) + bx * Math.pow(t, 2) + cx * t + dx,
           ay * Math.pow(t, 3) + by * Math.pow(t, 2) + cy * t + dy
           );
       var ax2 = ax * Math.pow(t + 0.1, 3) + bx * Math.pow(t + 0.1, 2) + cx * (t + 0.1) + dx;
       var ay2 = ay * Math.pow(t + 0.1, 3) + by * Math.pow(t + 0.1, 2) + cy * (t + 0.1) + dy;

       context.lineTo(
           ax * Math.pow(t + 0.1, 3) + bx * Math.pow(t + 0.1, 2) + cx * (t + 0.1) + dx,
           ay * Math.pow(t + 0.1, 3) + by * Math.pow(t + 0.1, 2) + cy * (t + 0.1) + dy
           );
       context.stroke();
       //m.translate(ax * Math.pow(t + 0.1, 3) + bx * Math.pow(t + 0.1, 2) + cx * (t + 0.1) + dx, ay * Math.pow(t + 0.1, 3) + by * Math.pow(t + 0.1, 2) + cy * (t + 0.1) + dy, 0);
       //console.log("ax2: " + ax2 + ", ay2: " + ay2);
       var arr = [ax2, ay2];
       newpts.push(arr);
       t += 0.02;

       //Reached end of curve
       if (t > 1) clearInterval(interval);
   }
loremIpsum1771
  • 2,497
  • 5
  • 40
  • 87

1 Answers1

1

not a JAVAscript coder but in many languages 3 means integer

so try to add .0 to all floating constants... My bet is that your compiler/interpreter detect integer and truncate the result changing subresults so the curve does not match and sometimes it could look like is above 1.0 range even if it is not.

Also better for stop for this is:

for (t=0.0,e=1;e;t=0.1)
 {
 if (t>=1.0) { e=0; t=1.0; }
 // here do your stuff
 }

This way you can use any step not just exact division of range. Also this take care of not exact number representation of step and cumulative round off error.

Just few hints

  • using pow for small integer exponents is a huge overkill and performance killer
  • why are you computing points twice? you can move to once and then just line to ... or remember the last x,y position instead of computation ...

so when put all together (C++)

int e;
float t,tt,ttt,dt;
vec2 a0,a1,a2,a3; // coefficients 2D vectors
vec2 p0,p1,p2,p3; // your input control points 2D vectors
vec2 p;           // temp point

a0=                           (    p0);
a1=                  (3.0*p1)-(3.0*p0);
a2=         (3.0*p2)-(6.0*p1)+(3.0*p0);
a3=(    p3)-(3.0*p2)+(3.0*p1)-(    p0);

t=0.0; dt=0.1; tt=t*t; ttt=tt*t;
p=a0+(a1*t)+(a2*tt)+(a3*ttt);
MoveTo(p.x,p.y);

for (t+=dt,e=1;e;t=dt)
 {
 if (t>=1.0) { e=0; t=1.0; }
 // here do your stuff
 tt=t*t;
 ttt=tt*t;
 p=a0+(a1*t)+(a2*tt)+(a3*ttt);
 LineTo(p.x,p.y);
 }
  • vec2 is just 2D vector containing x,y members
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thanks for the answer! Just a couple of questions though. First, what is the purpose of e inside of the for and if statements and how are you getting the x and y values for p? It only seems like there is one component of p being set. Also, how should I determine which control points should be used that will fit on the screen? thanks – loremIpsum1771 Nov 11 '15 at 17:42
  • @loremIpsum1771 `e` is just end condition if `t` is out of range then `t` is clamped to the `1.0` and `e` is reset so the loop stops after rendering the last point (`t=1.0`) you could use `bool` instead of `int` too. The vector algebra is simple ... `3.0*p0` is the same as `(3.0*p0.x,3.0*p0.y)` you just do the fing per axis ... so `p0,p1,p2,p3` is the same as your `points[]` and `a0,a1,a2,a3` is the same as your `(ax,ay),(bx,by),...` – Spektre Nov 11 '15 at 22:01