/**
* Manages all arrows
* @param {VelocityField} velocityField - the related velocityField
* @param {DistanceMap} distanceMap - the related distanceMap
* @param {number} num_seed_points - the number of seed points
* @param {number} num_arrows - number of maximum arrows
* @constructor
*/
var ArrowManager = function (velocityField, distanceMap, num_seed_points, num_arrows) {
var parent = this;
//private variables
var arrows = [];
var arrowHeads = [];
var seedPositions = [];
var NUM_ARROWS = num_arrows;
var NUM_SEED_POINTS = num_seed_points;
var velocityField = velocityField;
var distanceMap = distanceMap;
var RESPAWN_DELAY = 2;
var COLOR_DELAY = 3;
var activeSeedPosition = 0;
/**
* inits the Seedpositions and Arrows
*/
this.init = function () {
initSeedPositions();
initArrowsAtSeedPositions();
}
/**
* inits the Seedpositions
*/
var initSeedPositions = function () {
/*
velocityField.wheaterStations.forEach(function(wheaterStation) {
var velocityFieldPoint = velocityField.getVelocityFieldPoint(wheaterStation.xPos, wheaterStation.yPos);
console.log("velocity: " + velocityFieldPoint.velocity);
seedPositions.push(velocityFieldPoint);
});
*/
for(var i = 0; i < NUM_SEED_POINTS; i++)
{
var randomX = Math.floor(Math.random() * (velocityField.widthOfField + 1));
var randomY = Math.floor(Math.random() * (velocityField.heightOfField + 1));
//TODO DEBUG
//randomX = 330 + 100*i;
//randomY = 100;
var velocityFieldPoint = velocityField.getVelocityFieldPoint(randomX, randomY);
seedPositions.push(velocityFieldPoint);
}
}
/**
* creates arrows at seed positions
*/
var initArrowsAtSeedPositions = function() {
for(var i = 0; i < NUM_ARROWS; i++) {
var velocityFieldPoint = seedPositions[activeSeedPosition];
//increase activeSeedPosition
activeSeedPosition++;
if(activeSeedPosition >= seedPositions.length)
activeSeedPosition = 0;
var newArrow = new Arrow(velocityFieldPoint.xPos, velocityFieldPoint.yPos, velocityFieldPoint.velocity, velocityField, distanceMap);
newArrow.advectArrow(1);
newArrow.integrateArrow();
newArrow.checkAndMarkOccupied();
arrows.push(newArrow);
arrowHeads.push(new ArrowHead(0, 0, 0));
console.log("ADDED ARROW");
}
}
/**
* updates all arrows
*/
this.updateArrows = function() {
//first advect and integrate existing arrows
advectArrows();
integrateArrows();
//clear distanceMap
distanceMap.resetDistanceMap();
//copy arrows array to order it and check collision
//d3 needs unordered array
var arrowsCopy = arrows.slice();
//sort arrows by length so that long arrows live longer
sortArrowsByLengthAndLifetime(arrowsCopy);
//if some arrows are dead, respawn them at a different place
checkCollisionForLivingArrows(arrowsCopy);
respawnDeadArrows(arrowsCopy);
calcArrowheads();
//hack to avoid visual artifacts
adjustOpacity();
//TODO DEBUG output
var countAlive = 0;
for(var i = 0; i < arrows.length; i++) {
if(arrows[i].isAlive)
countAlive++;
//console.log("lifetime: " + arrows[i].getLifetime());
}
//console.log("Arrows alive: " + countAlive);
}
this.setArrows = function (arrows) {
parent.arrows = arrows;
}
/**
*
* @returns [Arrow] all arrows
*/
this.getArrows = function () {
return arrows;
}
/**
*
* @returns [ArrowHead] all Arrowheads
*/
this.getArrowheads = function () {
return arrowHeads;
}
/**
* advects all arrows
*/
var advectArrows = function () {
var dt = 1;
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
arrow.advectArrow(dt);
}
;
}
/**
* integrates all arrows
*/
var integrateArrows = function () {
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
arrow.integrateArrow();
}
;
}
/**
* checks collision of living arrows
* @param {Array.<Arrow>} arrows array of arrows
*/
var checkCollisionForLivingArrows = function (arrows) {
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
if(arrow.isAlive)
arrow.checkAndMarkOccupied();
};
}
/**
* respawns dead arrows in array
* @param {Array.<Arrow>} arrows array of arrows
*/
var respawnDeadArrows = function (arrows) {
for (var i = arrows.length-1; i >= 0; i--) {
var arrow = arrows[i];
//delay before being respawned
if (!arrow.isAlive && arrow.respawnTime < RESPAWN_DELAY) {
arrow.respawnTime++;
//console.log("respawn wait: " + arrow.respawnTime);
}
if(!arrow.isAlive && arrow.respawnTime >= RESPAWN_DELAY) {
console.log("respawn");
//reposition it
var velocityFieldPoint = seedPositions[activeSeedPosition];
//increase activeSeedPosition
activeSeedPosition++;
if(activeSeedPosition >= seedPositions.length)
activeSeedPosition = 0;
arrow.respawn(velocityFieldPoint.xPos, velocityFieldPoint.yPos);
//integrate to render it at the new position
arrow.integrateArrow();
//check collision
arrow.checkAndMarkOccupied();
}
};
}
/**
* function that regulates with a counter when a respawned arrow is set to full opacity again.
* kinda hack-ish, but avoids visual artifacts
*/
var adjustOpacity = function() {
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
//var arrowHead = arrowHeads[i];
if(arrow.recolor) {
arrow.recolorTime = arrow.recolorTime + 1;
}
if(arrow.recolor && arrow.recolorTime >= COLOR_DELAY){
arrow.opacity = 1;
arrow.strokeWidth = 2;
arrow.recolorTime = 0;
arrow.recolor = false;
//arrowHead.opacity = 1;
//arrowHead.strokeWidth = 2;
}
};
}
/**
* calculates the correct position and rotation of the Arrowheads
*/
var calcArrowheads = function () {
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
var angle = arrow.calcArrowheadRotation();
arrowHeads[i].rotation = angle;
arrowHeads[i].posX = arrow.currp3x;
arrowHeads[i].posY = arrow.currp3y;
};
}
/**
* sorts arrows by length first and then by lifetime
* @param {Array.<Arrow>} arrows array of arrows
*/
var sortArrowsByLengthAndLifetime = function(arrows) {
//Source: https://github.com/Teun/thenBy.js
var firstBy=function(){function n(n){return n}function t(n){return"string"==typeof n?n.toLowerCase():n}function r(r,e){if(e="number"==typeof e?{direction:e}:e||{},"function"!=typeof r){var u=r;r=function(n){return n[u]?n[u]:""}}if(1===r.length){var i=r,o=e.ignoreCase?t:n;r=function(n,t){return o(i(n))<o(i(t))?-1:o(i(n))>o(i(t))?1:0}}return-1===e.direction?function(n,t){return-r(n,t)}:r}function e(n,t){return n=r(n,t),n.thenBy=u,n}function u(n,t){var u=this;return n=r(n,t),e(function(t,r){return u(t,r)||n(t,r)})}return e}();
arrows.sort(
firstBy(function(v) { return v.getArrowLength(); }, -1)
.thenBy(function(v) { return v.getLifetime(); }, -1)
);
}
}