odourTimeLaps = {
const svg = d3.select(DOM.svg(w, h));
svg.attr("width", w )
.attr("height", h )
;
svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', w )
.attr('height', h )
.style('stroke',"black")
.style("stroke-width",1)
.attr('opacity', 0.1)
.attr("class", "border");;
;
var percContainer = svg.append("g")
.attr("transform", "translate(" + (xScale2(0) ) + ","+(yScale2(0) )+ ")");
var blowL=30;
var borderVisibility = "hidden";
// Initialize the odour source node
// La sorgente è rappresentata da un cerchio rosso al centro del canvas
const center = svg.append("g")
//.attr("class", "nodes")
.attr('stroke', "#ff0000")
.selectAll("circle")
//.data([ xScale2(0) -(w/2)/*+40*/,yScale2(0)-(h/2)/*+20*/])
.data([200,/*+40*/200/*+20*/])
.enter()
.append("circle")
.attr('fill','#cccccc')
.attr("r",10)
.attr("cx", (w/2)/* +margin.l*/)
.attr("cy", (h/2)/* + margin.t*/)
;
var percC = percContainer.selectAll("g.recettore")
.data(dataset2.nodes)
.enter()
.append("g")
.attr("class", "recettore")
.each(function(d, i) {
var cn = d.name
cn = cn.replace(/ /g,"")
//console.log(cn);
d3.select(this)
.attr("class", cn)
.attr('stroke', "#00ff00")
//.attr('fill' ,"none")
.append("circle")
//.attr('fill','none')
.attr("r",10);
}); //each percC container
// initilize the blowing wind represented by the rotating outer arc from source
let xSource = xScale2(0)
let sector =svg.append("path")
.attr("transform", "translate ("+(xScale2(0)/*+40*/)+", "+(yScale2(0)/*+20*/)+")")
.attr("d", d3.arc()
.innerRadius( 15 )
.outerRadius( 150 )
.startAngle (-Math.PI/outer_angle ) // It's in radian, so Pi = 3.14 = bottom.
.endAngle( Math.PI/outer_angle ) // 2*Pi = 6.28 = top
)
.attr('stroke', 'black')
.attr('fill', '#1010ff')
.attr('opacity', 0.5);
// add an inner arc, representing the theta spread angle ov view from source
let wind =svg.append("path")
.attr("transform", "translate("+(xScale2(0)/*+40*/)+", "+(yScale2(0)/*+20*/)+")")
.attr("d", d3.arc()
.innerRadius( 15 )
.outerRadius( 150 )
.startAngle (-Math.PI/inner_angle ) // It's in radian, so Pi = 3.14 = bottom.
.endAngle( Math.PI/inner_angle ) // 2*Pi = 6.28 = top
)
.attr('stroke', 'black')
.attr('fill', '#69b3a2')
.attr('opacity', 0.3);
// Text for date
const date = svg.append("g")
.attr("class", "text")
.selectAll("text")
.data([ xScale2(0) -(w/2)+40,yScale2(0)-(h/2)+20])
.enter().append("text")
//.text("Hello")
.style("fill","black")
.style("font-size","15px")
// Text to perceptors nodes
const text = svg.append("g")
.attr("class", "text")
.selectAll("textr")
.data(dataset2.nodes)
.enter().append("text")
.text(d => d.name)
.style("fill","white")
.style("font-size","5px")
var ww = 830, hw = 830;
var t0 = Date.now();
var blows=[]
//var blowL =30 //edge length of blow object
var cy = 30;
var cx = 30;
var gap=0;
var ph =1
// var borderVisibility = "visible";//"hidden";//"visible"
// costruisce la griglia del vento
// Ricordarsi che un oggetto blow può essere amplificato con blowLen in un range da 0.33 a 2
// Praticamente si crea una griglia 16 X 16
// Per ogni riga pari si piazzano 8 blow
// Ogni 4 righe, a partire dalla seconda, si sfasano di 60 px
// viene creato l'array blows che contiene le coordinate ed i parametri di oggni oggetto blow
for (var i = 0; i < 16; i++) {
if(i%2==0){
if(i%4 ==2){
cx+=2*blowL
}//if(i%4 ==2)
for(var j =0; j < 16; j++){
if(j%2==0){
blows.push({ centery: cy, centerx: cx ,R: 70, r: 5, speed: 2, phi0: 0});
//console.log(i,j,cx,cy)
}//if(j%2==0)
cx+=(2*blowL + gap)
}//for j
cx =30;
}//if(i%2==0)
cy+=(2*blowL + gap)
} // for i
/*var blows = [
{ center: 50, R: 70, r: 5, speed: 2, phi0: 90},
{ center: 150, R: 50, r: 10, speed: 2, phi0: 90}
];*/
// Crea il contenitore degli oggetti blow
var blowContainer = svg.append("g")
//.attr("transform", "translate(" + w/3 + "," + h/3 + ")")
//.attr("transform", "translate(" + 0 + ","+ 0 + ")")
//Insertisce nel container blowContainer gli oggetti (grupp gi) blow secondo l'array blow
// Ad ogni oggetto blow viene aggiunto un bordo quadrato blowBorder di tipo path
// Ad ogni oggetto blow viene aggiunto una sinusoide besier con frecciA blowArrow di tipo path
// LA FRECCIA È INIZIALMENTE RIVOLTA VERSO IL BASSO
var blowC = blowContainer.selectAll("g.blow")
.data(blows)
.enter()
.append("g")
.attr("class", "blow")
.each(function(d, i) {
d3.select(this).append("path").attr("class", "blowBorder")
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", "1")
.attr("opacity", 1)
.attr("visibility", borderVisibility)
.attr("d", function(d) {
return"M-"+(blowL)+",-"+blowL+" L"+blowL+",-"+blowL+" L"+blowL+","+blowL+", L-"+blowL+","+blowL+", L-"+blowL+",-"+blowL+""; });
// moving shape
d3.select(this)
.append("path")
.attr("d", "M0, -25 Q11, -12.5 0,0 T0,25 M0,33 L-3,25 L3,25 Z")
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", "3")
.attr("opacity", 1)
.attr("class", "blowArrow")
.attr("transform", "rotate(180)") ;
}); //each blowContainer
// blowA seleziona tutti gli ogggetti frecce di classe blowArrow
var blowA = blowContainer.selectAll(".blowArrow")
var tickWind =0; // tickWind viene usata per modellare la curva della freccia del vento.
//E' un valore ciclico
//parte da 0 si incremeta fino a size, per poi decemetare fino -size e reincremetarsi
var inc=1;
var lengthB = 1
var tickInc =0.1
var tickL =0.3 // tickL oscilla tra 0.3 ed 1 con incrementi di 0.1
// Set the timer for frame animation
// animationSpeed è regolata dallo slider
// ticked() is invoked on a particular time delay interval (animationSpeed). If delay is not specified, it takes the timer time.
var t = d3.interval(ticked, animationSpeed);
var tick = 0 // tick è il contatore dei frames. E' l'indice che scorre per indicizzare gli array delle date, delle velocità, delle direzioni dei venti.
// tick si incrementa di una unità ad ogni delay interval e viene resettato a zero quando supera
// la lunghezza dell'array delle date (ha spazzato tutti i frames)
let outerTick=0;
var rotate = 0;
var rotateBlow = 0;
var direction = "";
var velocity = 0;
var wind_color = "#000000";
var blowLength = 0;
var blowTick =0;
function ticked() {
//vars for wind field
var delta = (Date.now() - t0);
var start =0;
var end =0;
var sin = 0;
// size E
var size=15
//************************
//controllo di tickWind
tickWind+=inc
if (tickWind >= size){
inc *= -1
}
if (tickWind <= -size ){
inc *= -1
}
//**********************
//if(tickWind%10==0){
//**********************
//controllo di tickL
tickL+=tickInc;
if (tickL >= 1){
tickInc *= -1
}
if (tickL <= 0.3 ){
tickInc *= -1
}
//***********************
//}
//svg.selectAll(".blow")
// Animazione. Sposta tutti i blows da (0,0) fino le coordinate stabilite nell'array blows
// tutto è relativo al canvas principale
blowC.transition()
.attr("transform", function(d) {
//return "translate("+d.centerx+","+d.centery+") rotate(" + d.phi0 + delta * d.speed/200 + ") scale("+tickL+" "+tickL+")";
return "translate("+d.centerx+","+d.centery+") rotate(" + (rotateBlow) + ") scale("+blowLength+" "+blowLength+")";
})
.duration(animationSpeed/2);
/*svg.selectAll(".blowArrow")
.attr("d", function(d){return "M0, -25 Q"+tickWind+",-12.5 0,0 T0,25 M0,30 L-3,25 L3,25 L0,30"})*/
//Animazione. Fa scillare gli oggetti freccia
blowA.transition()
.attr("d", function(d){return "M0, -25 Q"+tickWind+",-12.5 0,0 T0,25 M0,33 L-3,25 L3,25 Z"})
.attr("stroke", wind_color)
.duration(animationSpeed/2);
//end of vars for wind field
// node.attr("fill", d3.hsl(tick,100,50))
// Animazione, porta i recettori al loro posto
percC.transition()
.attr("transform", function(d) {
return "translate("+(d.x -(xScale2(0) +(xScale2(0)-(w/2)) /*+40*/))+","+( d.y -(yScale2(0)+(yScale2(0)-(h/2))/*+20*/))+")";
})
.duration(animationSpeed/2);
// center.attr("cx", xScale2(0)-(xScale2(0)-(w/2))/* +margin.l*/)
// .attr("cy", yScale2(0)-(yScale2(0)-(h/2))/* + margin.t*/)
// Muove la sorgente al suo posto
center.transition()
.attr("cx", (w/2)/* +margin.l*/)
.attr("cy", (h/2)/* + margin.t*/)
.duration(animationSpeed/2);
text.attr("x", d => d.x - 5-(xScale2(0)-(w/2))) //position of the lower left point of the text
.attr("y", d => d.y /*+ 5*/ -(yScale2(0)-(h/2))); //position of the lower left point of the text
date.attr("x", 40) //position of the lower left point of the text
.attr("y", 20) //position of the lower left point of the text
.text(dates[tick]+" - Wind direction:"+rotate+"° Wind sector: "+direction + " - Wind speed: " + velocity); // color: "+ wind_color);
mutable frame =dates[tick]+" - Wind direction: "+rotate+"° - Wind sector: "+direction + " - Wind speed: " + velocity;
mutable date_time = dates[tick];
mutable frame_angle = rotate;
mutable frame_direction = direction;
mutable frame_velocity = velocity;
mutable frame_wind_color = wind_color;
//*****************************************
// controllo del settore esterno del vento
sector.transition()
.attr("transform", " translate("+(xScale2(0)-(xScale2(0)-(w/2))/*+40*/)+", "+(yScale2(0)-(yScale2(0)-(h/2))/*+20*/)+") rotate(" + rotate + ")")
.attr("d", d3.arc()
.innerRadius( 15 )
.outerRadius( 1500 )
.startAngle (-Math.PI/outer_angle ) // It's in radian, so Pi = 3.14 = bottom.
.endAngle( Math.PI/outer_angle ) // 2*Pi = 6.28 = top
)
.attr('fill', wind_color)
.duration(animationSpeed/2)
//******************************************
//controllo del settore interno del vento
wind.transition()
.attr("transform", " translate("+(xScale2(0)-(xScale2(0)-(w/2))/*+40*/)+", "+(yScale2(0)-(yScale2(0)-(h/2))/*+20*/)+") rotate(" + rotate + ")")
.attr("d", d3.arc()
.innerRadius( 15 )
.outerRadius( 1500 )
.startAngle (-Math.PI/inner_angle ) // It's in radian, so Pi = 3.14 = bottom.
.endAngle( Math.PI/inner_angle ) // 2*Pi = 6.28 = top
)
.duration(animationSpeed/2)
//console.log(node)
//outerTick+=1;
//if(outerTick%15==0){
//if(b){
tick=tick+1 //incrementa l'indice principale
//}
//}
// riparte da capo quando ha spazzato l'array intero
//if (tick >dates.length){
if (tick > limit){
tick = 0
svg.selectAll(".rc").remove();
}
let daylyWindDir = rawWindDir
.filter(v=> v["DateTime"]===dates[tick] )
.map(v => ({DateTime: v.DateTime,
direction: v["direction"],
angle: v["angle"]
}))
rotate = daylyWindDir[0]["angle"]
rotateBlow = rotate;
direction = daylyWindDir[0]["direction"]
// console.log(rotateBlow)
//console.log(daylyWindDir[0]["angle"])
// get wind velocity
let daylyWindVel = rawWindVel
.filter(v=> v["DateTime"]===dates[tick] )
.map(v => ({DateTime: v.DateTime,
velocity: v["velocity"]
}))
velocity = daylyWindVel[0]["velocity"]
//**************************
// controllo della grandezza del blow. Si ricorda che scala linearmente da 0.33 a due in base alla velocità del vento
blowLength = blowLen(velocity);
//*****************************
//controllo del colore del vento. Sia del settore che delle frecce dei blow
var myColor = d3.scaleSequential().domain([min_wind_vel,max_wind_vel])
.interpolator(d3.interpolatePuRd);
wind_color = myColor(velocity)
// console.log(daylyWindDir[0]["angle"])
//***********************************
// Seleziona il Frame
// Filtra i dati relativi alla data indicata dal tick
let dayly2 = rawPerceptions
.filter(v=> v["DateTime"]==dates[tick] )
.map(v => ({name:v.recettore,
id: recettori.indexOf(v.recettore),
color:v.color
}))
//
//
//*************************************************
// Funzione per disegnare
dataset2
.nodes.forEach(function (n){
n['color'] = dayly2[dataset2.nodes.indexOf(n)].color
//console.log(n['color'])
//console.log(n['color'])
//date.attr("value", dates[tick]);
// if(n['name'] == "R 14"){
// if(n['color'] != "#000000"){ // secondo me va tolto
//console.log(n['color'])
var cn2 =n['name']
cn2 = cn2.replace(/ /g,"");
cn2 = "."+cn2;
svg.selectAll(cn2)
.attr("stroke", n['color'])
.attr("fill", n['color'])
// ***************************************
// Aggiunge i rettangoli per source detection
if(show_rects=='toggle'){
svg.selectAll(cn2)
.filter(v=>n['color']!="#000000" )
//.attr("stroke", n['color'])
//.attr("fill", n['color'])
.append("path")
.attr("class" ,"rc")
.attr("transform", " translate("+(xScale2(0)-(xScale2(0)-(w/2)))+", "+(yScale2(0)-(yScale2(0)-(h/2)))+") rotate(" + rotate + ")")
.attr("d", d3.arc()
.innerRadius( 15 )
.outerRadius( 1500 )
.startAngle (-Math.PI/inner_angle ) // It's in radian, so Pi = 3.14 = bottom.
.endAngle( Math.PI/inner_angle ) // 2*Pi = 6.28 = top
)
.style('stroke',"red")
.style('fill', "red")
.style("stroke-width",1)
.attr('opacity', 0.004)
.attr("transform", "rotate("+(rotate-180)+")")
}else{}
/*if(show_rects==true){
svg.selectAll(cn2)
.attr("stroke", n['color'])
.attr("fill", n['color'])
.append('rect')
.attr("class" ,"rc")
//.attr('x', n['x']-(xScale2(0)+40))
//.attr('y', n['y']-(yScale2(0)+20)-25)
.attr('x', -50)
.attr('y', 0)
//.attr('x', (xScale2(0) -(w/2) +40))
//.attr('y', (yScale2(0) -(w/2) +20))
.attr('width', 100)
.attr('height', 1000)
.style('stroke',"red")
.style('fill', "red")
.style("stroke-width",1)
.attr('opacity', 0.004)
.attr("transform", "rotate("+(180+rotate)+")")
}
else{
svg.selectAll(cn2)
.attr("stroke", n['color'])
.attr("fill", n['color'])
}*/
//*********************fine rettangoli
;
// } // if(n['color'] != "#000000"){
// } // if(n['name'] == "R 34"){
});
//svg.selectAll(".R40").attr("stroke", "red");
//console.log(tick)
}
return svg.node();
}