javascript - How to animate along a curved path using gravity -


i'm trying create simulation in ball follows motion path, similar this:

https://bl.ocks.org/mbostock/1705868

however, instead of using tweening, ball's motion determined gravity , object's velocity - similar rollercoaster, this:

https://www.myphysicslab.com/roller/roller-single-en.html

this have far, there's small problem in roller coaster gaining energy, instead of losing it, each frame:

https://jsbin.com/jidazom/edit?html,js,output

x

any suggestions on how fix appreciated!

you can try js bin. modified code fit own understanding of effects of gravity. in calculations, use vertical component of slope, calculated @ current position (using small delta on each side, , not relying on previous position):

function geteffectivegravityfactor() {   // vertical component of slope @ current position   var delta = 0.001;   var pathpos1 = math.min(maxrange, math.max(delta, pathpos));   var pos1 = pathel.getpointatlength(pathpos1 - delta);   var pos2 = pathel.getpointatlength(pathpos1 + delta);   var dx, dy;   if (direction) {     dx = pos2.x - pos1.x;     dy = pos2.y - pos1.y;   } else {     dx = pos1.x - pos2.x;     dy = pos1.y - pos2.y;   }   var total = math.sqrt(dx * dx + dy * dy);   return dy / total; } 

the limits of path react pool table cushions. don't know if plan do. rebound not elastic, there can slight gain or loss of energy when limit reached.

i introduced friction coefficient, quite rough gives idea of possible implementation.

since not sure if requestanimationframe executed @ fixed intervals, took actual time interval account in calculations. part may not necessary.

here complete code:

var svg = d3.select("#line").append("svg:svg").attr("width", "100%").attr("height", "100%"); var data = d3.range(50).map(function(){return math.random()*10;}); var x = d3.scale.linear().domain([0, 10]).range([0, 700]); var y = d3.scale.linear().domain([0, 10]).range([10, 290]); var line = d3.svg.line()   .interpolate("cardinal")   .x(function(d,i) {return x(i);})   .y(function(d) {return y(d);})  var path = svg.append("svg:path").attr("d", line(data)); var circle =      svg.append("circle")       .attr("cx", 100)       .attr("cy", 350)       .attr("r", 3)       .attr("fill", "red");  var circlebehind =      svg.append("circle")       .attr("cx", 50)       .attr("cy", 300)       .attr("r", 3)       .attr("fill", "blue");  var circleahead =      svg.append("circle")       .attr("cx", 125)       .attr("cy", 375)       .attr("r", 3)       .attr("fill", "green");  var pathel = path.node(); var pathlength = pathel.gettotallength(); var bbox = pathel.getbbox(); var scale = pathlength/bbox.width; var offsetleft = document.getelementbyid("line").offsetleft; var randomizebutton = d3.select("#randomize"); var pathpos = 600; var pos = {x: 0, y: 0}; var speed = 10; var friction = 0; var direction = true; var gravity = 0.01; var maxrange = 1500; var speedchange; var currenttime, prevtime, difftime;  function geteffectivegravityfactor() {   // vertical component of slope @ current position   var delta = 0.001;   var pathpos1 = math.min(maxrange, math.max(delta, pathpos));   var pos1 = pathel.getpointatlength(pathpos1 - delta);   var pos2 = pathel.getpointatlength(pathpos1 + delta);   var dx, dy;   if (direction) {     dx = pos2.x - pos1.x;     dy = pos2.y - pos1.y;   } else {     dx = pos1.x - pos2.x;     dy = pos1.y - pos2.y;   }   var total = math.sqrt(dx * dx + dy * dy);   return dy / total; }  function play() {   requestanimationframe(play);    currenttime = date.now();   difftime = currenttime - prevtime;    if (difftime > 20) {      prevtime = currenttime;      if (pathpos < 0 || pathpos > maxrange) {       // limit reached: change direction       direction = !direction;       pathpos = math.min(maxrange, math.max(0, pathpos));     } else {       speedchange = gravity * difftime * geteffectivegravityfactor();       if (speedchange < -speed) {         // direction change caused gravity         direction = !direction;         speed = 0;       } else {         speed += speedchange;         speed = math.max(0, speed - friction * difftime * (0.0002 + 0.00002 * speed));       }     }      pathpos += (direction ? 1 : -1) * speed;     pos = pathel.getpointatlength(pathpos);      circle       .attr("opacity", 1)       .attr("cx", pos.x)       .attr("cy", pos.y);      posbehind = pathel.getpointatlength(pathpos - 10);      circlebehind       .attr("opacity", 1)       .attr("cx", posbehind.x)       .attr("cy", posbehind.y);      posahead = pathel.getpointatlength(pathpos + 10);      circleahead       .attr("opacity", 1)       .attr("cx", posahead.x)       .attr("cy", posahead.y);   } }  prevtime = date.now(); play();  var txtspeed = document.getelementbyid("txtspeed"); var txtfriction = document.getelementbyid("txtfriction");  txtspeed.value = speed; txtfriction.value = friction;  randomizebutton.on("click", function(){   speed = parsefloat(txtspeed.value);   friction = parsefloat(txtfriction.value);   pathpos = 400;   direction = true;   prevtime = date.now();   data = d3.range(50).map(function(){return math.random()*10;});   circle.attr("opacity", 0);                                       path     .transition()     .duration(300)     .attr("d", line(data)); }); 

Comments

Popular posts from this blog

networking - Vagrant-provisioned VirtualBox VM is not reachable from Ubuntu host -

c# - ASP.NET Core - There is already an object named 'AspNetRoles' in the database -

ruby on rails - ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true -