loremIpsum1771
loremIpsum1771

Reputation: 2527

Issue with rendering b-spline animation in javascript

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);
   }

Upvotes: 0

Views: 149

Answers (1)

Spektre
Spektre

Reputation: 51845

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

Upvotes: 1

Related Questions