Grass like smoothing animation on beziercurve?


This is what I am trying to achieve--GRASS Animation(Desired animation)

This is where the project is standing currently --My hair animation

This is a more structurised code of the above code --My hair animation(by markE)--markE`s code of hair animation

PROBLEM:--

I am able to give movements to hairs but animation should be more like wavy grass like freeflowing.Its not very smooth now.What can be done to make the hairs flow in more natural manner. Please provide me with a small sample if possible!!!

    <canvas id="myCanvas" width="500" height="500" style="background-color: antiquewhite" ></canvas>

JAVASCRIPT

//mouse position
var x2=0;
var y2=0;

window.addEventListener("mousemove",function(){moving(event);init()},false)

//these variables define the bend in our bezier curve
var bend9=0;
var bend8=0;
var bend7=0;
var bend6=0;
var bend5=0;
var bend4=0;
var bend3=0;
var bend2=0;
var bend1=0;

//function to get the mouse cordinates
function moving(event) {
    bend_value();//this function is defined below
    try
    {
        x2 = event.touches[0].pageX;
        y2 = event.touches[0].pageY;
    }
    catch (error)
    {
        try
        {
            x2 = event.clientX;
            y2 = event.clientY;
        }
        catch (e)
        {
        }
    }

    try
    {
        event.preventDefault();
    }
    catch (e)
    {
    }
    if(between(y2,204,237) && between(x2,115,272))
    {
    console.log("Xmove="+x2,"Ymove="+y2)
    }

}

//function for declaring range of bezier curve
function between(val, min, max)
{
    return val >= min && val <= max;
}

(function() {
    hair = function() {
        return this;
    };

    hair.prototype={

     draw_hair:function(a,b,c,d,e,f,g,h){

            var sx  =136+a;//start position of curve.used in moveTo(sx,sy)
            var sy  =235+b;
            var cp1x=136+c;//control point 1
            var cp1y=222+d;
            var cp2x=136+e;//control point 2
            var cp2y=222+f;
            var endx=136+g;//end points
            var endy=210+h;

         var canvas = document.getElementById('myCanvas');
         var context = canvas.getContext('2d');
//         context.clearRect(0, 0,500,500);
         context.strokeStyle="grey";
         context.lineWidth="8";
         context.beginPath();
         context.moveTo(sx,sy);
         context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,endx,endy);
         context.lineCap = 'round';
         context.stroke();
//         context.restore();
//         context.save();
    }
};
})();

//this function provides and calculate the bend on mousemove
function bend_value(){
    var ref1=135;//this is ref point for  hair or curve no 1
    var ref2=150;//hair no 2 and so on
    var ref3=165;
    var ref4=180;
    var ref5=195;
    var ref6=210;
    var ref7=225;
    var ref8=240;
    var ref9=255;
if(between(x2,115,270) && between(y2,205,236))
{
    if(x2>=135 && x2<=145){bend1=(x2-ref1)*(2.2);}
    if(x2<=135 && x2>=125){bend1=(x2-ref1)*(2.2);}

    if(x2>=150 && x2<=160){bend2=(x2-ref2)*(2.2);}
    if(x2<=150 && x2>=140){bend2=(x2-ref2)*(2.2);}

    if(x2>=165 && x2<=175){bend3=(x2-ref3)*(2.2);}
    if(x2<=165 && x2>=155){bend3=(x2-ref3)*(2.2);}

    if(x2>=180 && x2<=190){bend4=(x2-ref4)*(2.2);}
    if(x2<=180 && x2>=170){bend4=(x2-ref4)*(2.2);}

    if(x2>=195 && x2<=205){bend5=(x2-ref5)*(2.2);}
    if(x2<=195 && x2>=185){bend5=(x2-ref5)*(2.2);}

    if(x2>=210 && x2<=220){bend6=(x2-ref6)*(2.2);}
    if(x2<=210 && x2>=200){bend6=(x2-ref6)*(2.2);}

    if(x2>=225 && x2<=235){bend7=(x2-ref7)*(2.2);}
    if(x2<=225 && x2>=215){bend7=(x2-ref7)*(2.2);}

    if(x2>=240 && x2<=250){bend8=(x2-ref8)*(2.2);}
    if(x2<=240 && x2>=230){bend8=(x2-ref8)*(2.2);}

    if(x2>=255 && x2<=265){bend9=(x2-ref9)*(2.2);}
    if(x2<=255 && x2>=245){bend9=(x2-ref9)*(2.2);}
    }
}

function init(){//this function draws each hair/curve
    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    var clear=context.clearRect(0, 0,500,500);
    var save=context.save();

//   /* console.log("bend2="+bend2)
//    console.log("bend3="+bend3)
//    console.log("bend4="+bend4)
//    console.log("bend5="+bend5)
//    console.log("bend6="+bend6)
//    console.log("bend7="+bend7)
//    console.log("bend8="+bend8)
//    console.log("bend9="+bend9)*/

    hd1 = new hair();//hd1 stands for hair draw 1.this is an instance created for drawing hair no 1
    clear;
    hd1.draw_hair(0,0,0,0,0,0,0+bend1/2,0);//these parameters passed to function drawhair and bend is beint retrieved from function bend_value()
    save;

    hd2 = new hair();
    clear;
    hd2.draw_hair(15,0,15,0,15,0,15+bend2/2,0);
    save;

    hd3 = new hair();
    clear;
    hd3.draw_hair(30,0,30,0,30,0,30+bend3/2,0);
    save;

    hd4 = new hair();
    clear;
    hd4.draw_hair(45,0,45,0,45,0,45+bend4/2,0);
    save;

    hd5 = new hair();
    clear;
    hd5.draw_hair(60,0,60,0,60,0,60+bend5/2,0);
    save;
 }

window.onload = function() {
    init();
    disableSelection(document.body)
}

function disableSelection(target){
    if (typeof target.onselectstart!="undefined") //IE
        target.onselectstart=function(){return false}
    else if (typeof target.style.MozUserSelect!="undefined") //Firefox
        target.style.MozUserSelect="none"
    else //All other ie: Opera
        target.onmousedown=function(){return false}
    target.style.cursor = "default"
}

Answers:


Update: I'm currently adjusting the code to produce the requested result and commenting it.

(function() { // The code is encapsulated in a self invoking function  to isolate the scope
  "use strict";

   // The following lines creates shortcuts to the constructors of the Box2D types used
   var B2Vec2 = Box2D.Common.Math.b2Vec2,
      B2BodyDef = Box2D.Dynamics.b2BodyDef,
      B2Body = Box2D.Dynamics.b2Body,
      B2FixtureDef = Box2D.Dynamics.b2FixtureDef,
      B2Fixture = Box2D.Dynamics.b2Fixture,
      B2World = Box2D.Dynamics.b2World,
      B2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
      B2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint,
      B2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef;

  // This makes sure that there is a method to request a callback to update the graphics for next frame
  window.requestAnimationFrame =
    window.requestAnimationFrame || // According to the standard
    window.mozRequestAnimationFrame || // For mozilla
    window.webkitRequestAnimationFrame || // For webkit
    window.msRequestAnimationFrame || // For ie
    function (f) { window.setTimeout(function () { f(Date.now()); }, 1000/60); }; // If everthing else fails

  var world = new B2World(new B2Vec2(0, -10), true), // Create a world with gravity
      physicalObjects = [], // Maintain a list of the simulated objects
      windInput = 0, // The input for the wind in the current frame
      wind = 0, // The current wind (smoothing the input values + randomness)
      STRAW_COUNT = 10, // Number of straws
      GRASS_RESET_SPEED = 2, // How quick should the straw reset to its target angle
      POWER_MOUSE_WIND = 120, // How much does the mouse affect the wind
      POWER_RANDOM_WIND = 180; // How much does the randomness affect the wind

  // GrassPart is a prototype for a piece of a straw. It has the following properties
  //  position: the position of the piece
  //  density: the density of the piece
  //  target: the target angle of the piece
  //  statik: a boolean stating if the piece is static (i.e. does not move)
  function GrassPart(position, density, target, statik) {
    this.width = 0.05;
    this.height = 0.5;
    this.target = target;

    // To create a physical body in Box2D you have to setup a body definition
    // and create at least one fixture.
    var bdef = new B2BodyDef(), fdef = new B2FixtureDef();
    // In this example we specify if the body is static or not (the grass roots 
    // has to be static to keep the straw in its position), and its original
    // position.
    bdef.type = statik? B2Body.b2_staticBody : B2Body.b2_dynamicBody;
    bdef.position.SetV(position);

    // The fixture of the piece is a box with a given density. The negative group index
    // makes sure that the straws does not collide.
    fdef.shape = new B2PolygonShape();
    fdef.shape.SetAsBox(this.width/2, this.height/2);
    fdef.density = density;
    fdef.filter.groupIndex = -1;

    // The body and fixture is created and added to the world
    this.body = world.CreateBody(bdef);
    this.body.CreateFixture(fdef);
  }

  // This method is called for every frame of animation. It strives to reset the original
  // angle of the straw (the joint). The time parameter is unused here but contains the
  // current time.
  GrassPart.prototype.update = function (time) {
    if (this.joint) {
      this.joint.SetMotorSpeed(GRASS_RESET_SPEED*(this.target - this.joint.GetJointAngle()));
    }
  };

  // The link method is used to link the pieces of the straw together using a joint
  // other: the piece to link to
  // torque: the strength of the joint (stiffness)
  GrassPart.prototype.link = function(other, torque) {
    // This is all Box2D specific. Look it up in the manual.
    var jdef = new B2RevoluteJointDef();
    var p = this.body.GetWorldPoint(new B2Vec2(0, 0.5)); // Get the world coordinates of where the joint
    jdef.Initialize(this.body, other.body, p);
    jdef.maxMotorTorque = torque;
    jdef.motorSpeed = 0;
    jdef.enableMotor = true;

    // Add the joint to the world
    this.joint = world.CreateJoint(jdef);
  };

  // A prototype for a straw of grass
  // position: the position of the bottom of the root of the straw
  function Grass(position) {
    var pos = new B2Vec2(position.x, position.y);
    var angle = 1.2*Math.random() - 0.6; // Randomize the target angle

    // Create three pieces, the static root and to more, and place them in line.
    // The second parameter is the stiffness of the joints. It controls how the straw bends.
    // The third is the target angle and different angles are specified for the pieces.
    this.g1 = new GrassPart(pos, 1, angle/4, true); // This is the static root
    pos.Add(new B2Vec2(0, 1));
    this.g2 = new GrassPart(pos, 0.75, angle);
    pos.Add(new B2Vec2(0, 1));
    this.g3 = new GrassPart(pos, 0.5);

    // Link the pieces into a straw
    this.g1.link(this.g2, 20);
    this.g2.link(this.g3, 3);

    // Add the pieces to the list of simulate objects
    physicalObjects.push(this.g1);
    physicalObjects.push(this.g2);
    physicalObjects.push(this.g3);
  }

  Grass.prototype.draw = function (context) {
      var p = new B2Vec2(0, 0.5);
      var p1 = this.g1.body.GetWorldPoint(p);
      var p2 = this.g2.body.GetWorldPoint(p);
      var p3 = this.g3.body.GetWorldPoint(p);

      context.strokeStyle = 'grey';
      context.lineWidth = 0.4;
      context.lineCap = 'round';

      context.beginPath();
      context.moveTo(p1.x, p1.y);
      context.quadraticCurveTo(p2.x, p2.y, p3.x, p3.y);
      context.stroke();
  };

    var lastX, grass = [], context = document.getElementById('canvas').getContext('2d');

    function updateGraphics(time) {
      window.requestAnimationFrame(updateGraphics);

      wind = 0.95*wind + 0.05*(POWER_MOUSE_WIND*windInput + POWER_RANDOM_WIND*Math.random() - POWER_RANDOM_WIND/2);
      windInput = 0;
      world.SetGravity(new B2Vec2(wind, -10));

      physicalObjects.forEach(function(obj) { if (obj.update) obj.update(time); });
      world.Step(1/60, 8, 3);
      world.ClearForces();

      context.clearRect(0, 0, context.canvas.width, context.canvas.height);
      context.save();
      context.translate(context.canvas.width/2, context.canvas.height/2);
      context.scale(context.canvas.width/20, -context.canvas.width/20);
      grass.forEach(function (o) { o.draw(context); });
      context.restore();
    }

    document.getElementsByTagName('body')[0].addEventListener("mousemove", function (e) {
      windInput = Math.abs(lastX - e.x) < 200? 0.2*(e.x - lastX) : 0;
      lastX = e.x;
    });

    var W = 8;
    for (var i = 0; i < STRAW_COUNT; i++) {
      grass.push(new Grass(new B2Vec2(W*(i/(STRAW_COUNT-1))-W/2, -1)));
    }

    window.requestAnimationFrame(updateGraphics);
})();