2017-07-25 88 views
8

Próbuję stworzyć symulację, w której piłka podąża ścieżką ruchu, podobnego do tego:Jak animować wzdłuż zakrzywionej ścieżki za pomocą grawitacji

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

Jednak zamiast korzystania z niego animację, ja chciałoby ruch piłkę, który ma być określony przez ciężkości i prędkości danego obiektu - podobnie jak rollercoaster, tak:

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

to co Mam tak daleko, ale jest mały problem w tym, że roller coaster jest nieznacznie zyskuje energię, zamiast tracić go, każda klatka:

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

x

Wszelkie sugestie, w jaki sposób rozwiązać ten problem będzie być bardzo docenionym!

Odpowiedz

4

Ty możesz spróbować tego JS Bin. Zmodyfikowałem kod, aby dopasować się do mojego rozumienia skutków grawitacji. W obliczeniach użyć składowe pionowe zbocza, obliczoną w bieżącym położeniu (przy użyciu niewielkiej delta z każdej strony, nie powołując się na poprzednim stanowisku)

function getEffectiveGravityFactor() { 
    // Get the vertical component of the slope at 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; 
} 

Granice drodze reakcji jak poduszki na stół bilardowy. Nie wiem, czy to właśnie zamierzasz zrobić. Odbicie nie zawsze jest doskonale sprężyste, więc po osiągnięciu limitu może nastąpić nieznaczne wzmocnienie lub utrata energii.

Wprowadziłem również współczynnik tarcia, który jest dość trudny, ale daje wyobrażenie o możliwej implementacji.

Ponieważ nie jestem pewien, czy requestAnimationFrame jest wykonywany w bardzo stałych odstępach czasu, wziąłem pod uwagę faktyczny przedział czasu w obliczeniach. Ta część może nie być konieczna.

Oto kompletny kod:

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() { 
    // Get the vertical component of the slope at 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) { 
     // The limit was reached: change direction 
     direction = !direction; 
     pathPos = Math.min(maxRange, Math.max(0, pathPos)); 
    } else { 
     speedChange = gravity * diffTime * getEffectiveGravityFactor(); 
     if (speedChange < -speed) { 
     // Direction change caused by 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)); 
}); 
+1

To jest genialne, bardzo dziękuję !!! – d13

-1

Wydajesz się zmierzać we właściwym kierunku. d3.js jest najlepszym rozwiązaniem, że mogę myśleć, i istnieje kilka pytań d3.js grawitacyjne już tutaj, że mają doskonałe odpowiedzi:

Convert d3.js bubbles into forced/gravity based layout

Understanding D3.js Force Layout - 8: gravity może być również pomocne jako punkt wyjścia.

chodzi o 'każdym' rada: Fail szybciej (czyli utrzymać cykl pisania i testowania jako szybkie i zautomatyzowane, jak to możliwe) oraz dokument as you go, (w przyszłości będzie wdzięczny.)