/**
* TrajectoryHelper handles trajectories
*
* @alias TrajectoryHelper
* @constructor
*
* @param {WindDataProvider} windDataProvider The wind data file.
* @param {number} [arrowLengthProportion] scale wind length data, default 10
*/
function TrajectoryHelper(windDataProvider, arrowLengthProportion) {
this.windDataProvider = windDataProvider;
// step size for Runge-Kutta (RK2)
this._stepSize = 1;
this._arrowLengthProportion = arrowLengthProportion || 10;
this._arrowMaxProportion = 20;
}
/**
* Returns the head/tail positions of an arrow on the trajectory and its length
*
* @param {Arrow} arrow The arrow to calculate
* @param {int} time Timestep to calculate for
* @return {Object} Object with points in "head" and "tail" and "length"
*/
TrajectoryHelper.prototype.getTimeData = function(arrow, time){
if (arrow.timeData[time] === undefined){
// TODO We have some problems when we get out of the world at poles (<-90 >-90), what to do?
var dir = this.windDataProvider.interpolate(arrow.position[time].x, arrow.position[time].y); // local direction
if (dir === null) console.log(arrow);
var length = Math.min(dir[2] * this._arrowLengthProportion, arrow.width * this._arrowMaxProportion); // length is proportional to length of local direction
// forward integration
var head = this.integrate(arrow.position[time].clone(), this._stepSize, length, arrow);
// backward integration
var tail = this.integrate(arrow.position[time].clone(), this._stepSize * -1, length, arrow);
return {length: length, head: head, tail: tail};
} else {
var dir = this.windDataProvider.interpolate(arrow.timeData[time].head.x, arrow.timeData[time].head.y); // local direction
var length = Math.min(dir[2] * this._arrowLengthProportion, arrow.width * this._arrowMaxProportion); // length is proportional to length of local direction
var head = this.integrate(arrow.timeData[time].head.clone(), this._stepSize, length, arrow);
var tail = arrow.position[time];
arrow.position[time] = arrow.timeData[time].head;
return {length: length, head: head, tail: tail};
}
}
/**
* Integration using Runge-Kutta (RK2)
*
* @param {Object} s Point to integrate
* @param {Number} stepSize Step size
* @param {Number} trajectoryLength Length of the trajectory that is integrated.
* @return {Object} Endpoint of integration
*/
TrajectoryHelper.prototype.integrate = function(s, stepSize, trajectoryLength, arrow){
var length = 0;
var d1, d2, s1, newlength, p1;
while (length < trajectoryLength * 0.5) {
d1 = this.windDataProvider.interpolate(s.x, s.y); // half euler step for stepSize = 1
d2 = this.windDataProvider.interpolate(s.x+d1[0]* (stepSize/2), s.y+d1[1]* (stepSize/2)); // flow vector at half euler step
if (stepSize < 0) { // backward integration
d2[0] = d2[0] * -1;
d2[1] = d2[1] * -1;
}
var newlength = Math.min(d2[2] * this._arrowLengthProportion, arrow.width * this._arrowMaxProportion);
if (newlength < d2[2]){
//we would overestimate this, maybe...
p1 = new Point(d2[0],d2[1]);
p1.normalize();
p1.scale(newlength);
s1 = Point.add(s, p1);
} else {
s1 = new Point(s.x + d2[0],s.y + d2[1]); // origin + flow vector at half euler step
}
length = length + newlength;
s = s1;
if (d2[2]<0.01) break;
}
return s1;
}