2016-04-26 11 views
0

Ich versuche herauszufinden, wie man einen Kreis entlang eines Pfades am besten interpoliert, wie es Mike Bostock in diesem Beispiel tut: http://bl.ocks.org/mbostock/1705868. Anstatt jedoch einen Übergangswert festzulegen, möchte ich in der Lage sein, eine eindeutige Dauer für jede Punkt-zu-Punkt-Interpolation festzulegen. B. Übergang des Knotens von Knoten [0] zu Knoten [1] über x Millisekunden, Übergang von Knoten [1] nach Knoten [2] über y Millisekunden, usw. Gibt es eine Möglichkeit, dies zu tun, ohne den Pfad nach oben zu zerlegen Bündel kleinerer getrennter Pfade und sie nacheinander durchlaufen? Der begrenzende Faktor scheint path.getTotalLength() zu sein - gibt es eine Möglichkeit, die Länge nur der Teilmenge eines Pfades zu erhalten?Wie wird eine bestimmte Dauer für die Interpolation entlang eines Pfads um jeweils einen Punkt festgelegt?

transition(); 

function transition() { 
    circle.transition() 
    .duration(10000) 
    .attrTween("transform", translateAlong(path.node())) 
    .each("end", transition); 
} 

// Returns an attrTween for translating along the specified path element. 
function translateAlong(path) { 
    var l = path.getTotalLength(); 
    return function(d, i, a) { 
     return function(t) { 
     var p = path.getPointAtLength(t * l); 
     return "translate(" + p.x + "," + p.y + ")"; 
    }; 
}; 
} 

Antwort

2

Es gibt in der Tat eine Art und Weise, aber es ist viel zu hässlich (weil es eine anfängliche Brute-Force-Berechnung benötigt), beinhaltet die Lösung des folgenden:

Zunächst einmal müssen Sie einen Array mit den Übergangszeiten zwischen den Knoten, in meinem Beispiel ist times, beispielsweise das erste Element in 3000 ms zu der Zeit entspricht, für die d von [480,200] zu [580,400]

  • berechnen die Summe der Übergangszeiten (erforderlich erhalten Der
  • berechnet die lineare Zeit in ms, um jeden der Punkte zu erreichen, die diesen Pfad gebildet haben. Dies ist tatsächlich schwierig, wenn der Pfad zwischen zwei Punkten keine Zeile ist, z. eine Kurve, in meinem Beispiel berechne ich diese Zeiten mit roher Gewalt, was es hässlich macht, es wäre toll, wenn es eine Methode gäbe, die die Weglänge berechnet, die benötigt wird, um zu einem Punkt auf dem Pfad selbst zu kommen soweit ich weiß, gibt es sie nicht
  • Schließlich, wenn Sie die linearen Zeiten kennen, müssen Sie die korrekte Zeit berechnen, als ob sie der Liste der Zahlen im times Array folgen würde, z

Lasst uns sagen, dass die lineare Zeit auf den ersten Punkt zu bekommen 50ms ist, und wir sind momentan in der Zeit t < 50ms, haben wir diesen Wert auf der Karte, die zwischen [0 ms, 50 ms], um irgendwo im Bereich [ 0 ms, 3000 ms], die durch die Formel gegeben ist 3000 * (t ms - 0ms)/(50ms - 0ms)

var points = [ 
 
    [480, 200], 
 
    [580, 400], 
 
    [680, 100], 
 
    [780, 300], 
 
    [180, 300], 
 
    [280, 100], 
 
    [380, 400] 
 
]; 
 

 
var times = [3000, 100, 5000, 100, 3000, 100, 1000] 
 
var totalTime = times.reduce(function (a, b) {return a + b}, 0) 
 

 
var svg = d3.select("body").append("svg") 
 
    .attr("width", 960) 
 
    .attr("height", 500); 
 

 
var path = svg.append("path") 
 
    .data([points]) 
 
    .attr("d", d3.svg.line() 
 
    .tension(0) // Catmull–Rom 
 
    .interpolate("cardinal-closed")); 
 

 
svg.selectAll(".point") 
 
    .data(points) 
 
    .enter().append("circle") 
 
    .attr("r", 4) 
 
    .attr("transform", function(d) { return "translate(" + d + ")"; }); 
 

 
var circle = svg.append("circle") 
 
    .attr("r", 13) 
 
    .attr("transform", "translate(" + points[0] + ")"); 
 

 
function transition() { 
 
    circle.transition() 
 
     .duration(totalTime) 
 
     .ease('linear') 
 
     .attrTween("transform", translateAlong(path.node())) 
 
     .each("end", transition); 
 
} 
 

 
// initial computation, linear time needed to reach a point 
 
var timeToReachPoint = [] 
 
var pathLength = path.node().getTotalLength(); 
 
var pointIndex = 0 
 
for (var t = 0; pointIndex < points.length && t <= 1; t += 0.0001) { 
 
    var data = points[pointIndex] 
 
    var point = path.node().getPointAtLength(t * pathLength) 
 
    // if the distance to the point[i] is approximately less than 1 unit 
 
    // make `t` the linear time needed to get to that point 
 
    if (Math.sqrt(Math.pow(data[0] - point.x, 2) + Math.pow(data[1] - point.y, 2)) < 1) { 
 
    timeToReachPoint.push(t); 
 
    pointIndex += 1 
 
    } 
 
} 
 
timeToReachPoint.push(1) 
 

 
function translateAlong(path) { 
 
    return function(d, i, a) { 
 
    return function(t) { 
 
     // TODO: optimize 
 
     var timeElapsed = t * totalTime  
 
     var acc = 0 
 
     for (var it = 0; acc + times[it] < timeElapsed; it += 1) { 
 
     acc += times[it] 
 
     } 
 
     var previousTime = timeToReachPoint[it] 
 
     var diffWithNext = timeToReachPoint[it + 1] - timeToReachPoint[it] 
 
     // range mapping 
 
     var placeInDiff = diffWithNext * ((timeElapsed - acc)/times[it])  
 
     var p = path.getPointAtLength((previousTime + placeInDiff) * pathLength) 
 
     return "translate(" + p.x + "," + p.y + ")" 
 
    } 
 
    } 
 
} 
 

 
transition();
path { 
 
    fill: none; 
 
    stroke: #000; 
 
    stroke-width: 3px; 
 
} 
 

 
circle { 
 
    fill: steelblue; 
 
    stroke: #fff; 
 
    stroke-width: 3px; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>