Unlisted
Edited
Mar 5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Inputs.table(csv_augmente.filter(d => Math.abs(d.coor_balle_x) < 300).filter(d => d.appartient_zone == true).filter(d => d.coor_balle_x != null))
Insert cell
function calcul_separateur_cluster(csv) {
var liste_separateurs = []
const nb_clusters = [... new Set(csv.map(d => {return d.cluster}))].length
for (let index = 0; index < nb_clusters; ++index) {
if (csv.filter(d => d.cluster == index).length > 4 &&
(csv.filter(d => d.cluster == index && d.winner == players[0]).length > 0) &&
(csv.filter(d => d.cluster == index && d.winner == players[1]).length > 0)){
var csv_filtre_cluster = csv.filter(d => d.cluster == index)
var liste_proportion_gagnant = []
var liste_proportion_perdant = []
for (let index2 = 0; index2 < csv_filtre_cluster.length; ++index2) {
var angle = csv_filtre_cluster.filter(d => d.cluster == index)[index2].angle
if (csv_filtre_cluster.filter(d => d.angle > angle).length == 0){
liste_proportion_gagnant.push(0)
} else{
liste_proportion_gagnant.push(csv_filtre_cluster.filter(d => d.winner == players[0] && d.angle > angle).length
/
csv_filtre_cluster.filter(d => d.angle > angle).length)
liste_proportion_perdant.push(csv_filtre_cluster.filter(d => d.winner == players[1] && d.angle > angle).length
/
csv_filtre_cluster.filter(d => d.angle > angle).length)
}
}
console.log(liste_proportion_gagnant)
console.log(liste_proportion_perdant)
console.log(indexOfMax(liste_proportion_gagnant))
console.log(indexOfMax(liste_proportion_perdant))
console.log(csv_filtre_cluster[indexOfMax(liste_proportion_gagnant)].angle)
console.log(csv_filtre_cluster[indexOfMax(liste_proportion_perdant)].angle)
}
}
}
Insert cell
function indexOfMax(arr) {
if (arr.length === 0) {
return -1;
}

var max = arr[0];
var maxIndex = 0;

for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}

return maxIndex;
}
Insert cell
function calcul_pourcetage_victoire_separateur(csv,angle) {
csv.filter(d => d.winner == players[0] && d.angle > angle).length/csv.filter(d => d.winner == players[1] && d.angle > angle).length
}
Insert cell
function moyenne_array(arr) {
var sum = 0
for (let index = 0; index < arr.length; ++index) {
sum += arr[index]
}
return sum/arr.length
}
Insert cell
csv_centres_clusters = calcul_cluster(csv_augmente.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => d.coor_balle_x != null)
.map(u => Object.assign({}, u, { approved: true }))
.filter(d => d.nom == ((cone_joueur == players[0])?players[1]:players[0])),
algo_clusters,
{epsilon:epsilon_dbscan,minPts:minPts_dbscan})
Insert cell
csv_augmente_un_joueur = csv_augmente.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => d.coor_balle_x != null)
.map(u => Object.assign({}, u, { approved: true }))
.filter(d => d.nom == ((cone_joueur == players[0])?players[1]:players[0]))
Insert cell
csv_augmente_un_joueur_vainqueur_et_perdant = calcul_cluster(csv_augmente.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => d.coor_balle_x != null)
.map(u => Object.assign({}, u, { approved: true }))
.filter(d => d.nom == ((cone_joueur == players[0])?players[1]:players[0]))
,"dbscan",
{epsilon:epsilon_dbscan,minPts:minPts_dbscan})
Insert cell
csv_augmente_un_joueur_vainqueur = calcul_cluster(csv_augmente.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => d.coor_balle_x != null)
.map(u => Object.assign({}, u, { approved: true }))
.filter(d => d.nom == ((cone_joueur == players[0])?players[1]:players[0]))
.filter(d => d.winner == ((cone_joueur == players[0])?players[0]:players[1]))
,"dbscan",
{epsilon:epsilon_dbscan,minPts:minPts_dbscan})
Insert cell
csv_augmente_un_joueur_perdant = calcul_cluster(csv_augmente.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => d.coor_balle_x != null)
.map(u => Object.assign({}, u, { approved: true }))
.filter(d => d.nom == ((cone_joueur == players[0])?players[1]:players[0]))
.filter(d => d.winner == ((cone_joueur == players[0])?players[1]:players[0]))
,"dbscan",
{epsilon:epsilon_dbscan,minPts:minPts_dbscan})
Insert cell
function reuperer_plus_gros_custer(csv) {
var plus_gros_cluster = 0
var taille_cluster = 0
const nb_clusters = [... new Set(csv.map(d => {return d.cluster}))].length
for (let index = 0; index < nb_clusters; ++index) {
if (taille_cluster < csv.filter(d => d.cluster == index).length){
plus_gros_cluster = index
taille_cluster = csv.filter(d => d.cluster == index).length
}
}
return {num_cluster:plus_gros_cluster,point_cluster:csv.filter(d => d.cluster == plus_gros_cluster)[0].num_point}
}
Insert cell
function calcul_cluster(csv,fonction_ml,param) {
if (fonction_ml == "dbscan"){
const dbscanner = jDBSCAN()
.eps(param.epsilon)
.minPts(param.minPts)
.distance('EUCLIDEAN')
.timeEps(1800)
.data(csv);
const point_assignment_result = dbscanner()
return {csv:csv.map(u => Object.assign({}, u, { approved: true })).map((d,i) => {
d.cluster = point_assignment_result[i]
return d
}),centres_clusters:[]}
}else if (fonction_ml == "kmeans"){
var centres_clusters = kmeans(csv.map(u => Object.assign({}, u, { approved: true })), param.nb_clusters)
return {csv:csv,centres_clusters:centres_clusters}
}
}
Insert cell
taux_victoire(csv_centres_clusters.csv)
Insert cell
function taux_victoire(csv) {
var liste = []
for (let index = 0; index < csv.length; ++index) {
var victoires = csv.filter(d => d.cluster == index).map(d => {return d.winner})
if (victoires.length > 0){
liste.push(victoires.filter(d => d == cone_joueur).length/victoires.length)
}
}
return(liste)
}
Insert cell
taux_coup_droit(csv_augmente_un_joueur)
Insert cell
function taux_coup_droit(csv) {
var liste = []
for (let index = 0; index < csv.length; ++index) {
var lateralite = csv.filter(d => d.cluster == index).map(d => {return d.lateralite_suiv})
if (lateralite.length > 0){
liste.push(lateralite.filter(d => d == "coup_droit").length/lateralite.length)
}
}
return(liste)
}
Insert cell
url_video = url_pipeline + "/" + competition + "/" + game + "/clips/" + clip + "/" + clip + ".mp4"
Insert cell
chart()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function c_winner(couleur) {
return (couleur == cone_joueur)?"#60c257":"#de4b07"
}
Insert cell
c_clusters = d3.scaleOrdinal(d3.schemeTableau10);
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof couleur = Inputs.radio(["vainqueur","joueur_frappe","clusters"], {value: "vainqueur", label: "Couleur"})
Insert cell
viewof algo_clusters = Inputs.radio(["dbscan","kmeans"], {value: "dbscan", label: "Algorithme clusters"})
Insert cell
viewof epsilon_dbscan = Inputs.range([0, 100], {value: 20, step: 1, label: "Epsilon Dbscan"})
Insert cell
viewof minPts_dbscan = Inputs.range([0, 50], {value: 1, step: 1, label: "MinPts Dbscan"})
Insert cell
viewof nb_clusters_kmeans = Inputs.range([0, 10], {value: 5, step: 1, label: "Nb clusters Kmeans"})
Insert cell
viewof effet_coup = Inputs.checkbox(["service","topspin","bloc","poussette","coup","flip"], {value: ["service","topspin","bloc","poussette","coup","flip"], label: "Effet de coup:"})
Insert cell
viewof num_coup = Inputs.checkbox([1,2,3,4,"5+"], {value: [1,2,3,4,"5+"], label: "Numéro du coup:"})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart2 = visu_changment_ref()
/*csv_annotation.filter(d => numero_set.includes(d.set))
.filter(d => serveur.includes(d.serveur))
.filter(d => d.coor_balle_x != null)
.filter(d => Math.abs(d.coor_balle_x) < 300)
)*/
Insert cell
Insert cell
viewof nb_clusters = Inputs.range([0, 10], {value: 4, step: 1, label: "Distance maximum gagnante"})
Insert cell
objet_options_affichage2 = {
//kmeans(csv_augmente_un_joueur, nb_clusters)
var a = [{csv:csv_augmente.filter(d => d.nom == cone_joueur).concat(csv_centres_clusters.csv/*.map(u => Object.assign({}, u, { approved: true }))
.map(d => {
d.cluster = (d.cluster == csv_centres_clusters.csv.filter(d => d.num_point == reuperer_plus_gros_custer(csv_augmente_un_joueur_perdant.csv).point_cluster)[0].cluster)?0:1
return d
})*/)
.filter(d => numero_set.includes(d.set))
.filter(d => effet_coup.includes(d.effet_coup)||(effet_coup.includes("service") && d.type_service != null))
.filter(d => serveur.includes(d.serveur))
.filter(d => Math.abs(d.coor_balle_x) < 300)
.filter(d => (num_coup.length == 5)?true:((num_coup.includes("+5"))?num_coup.includes(d.num_coup) || d.num_coup >= 5:num_coup.includes(d.num_coup)))
,
options_affichage:options_affichage,
opacite_cone:opacite_cone,
opacite_densite:opacite_densite,
opacite_table:opacite_table,
taille_cercle_maximal:500-taille_cercle_maximal,
taille_cercle_minimal:500-taille_cercle_minimal,
mode:mode,
bandwidth:bandwidth,
angle_coup_droit:angle_coup_droit,
angle_revers:angle_revers,
cone_joueur:cone_joueur,
couleur:(couleur == "clusters")?"cluster":((couleur == "vainqueur")?"winner":"nom"),
centres_clusters:csv_centres_clusters.centres_clusters}];
return a
}
Insert cell
chart2.update(objet_options_affichage2[0]) // we pass in an anonymous function
Insert cell
viewof b = Inputs.button("test",{value: "Update", description: "Update dataset."})
Insert cell
function visu_changment_ref() {//csv) {

console.log("init (ne doit apparaître qu'une seule fois!")
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width/2, height]);

const dim_joueur = 500
const dim_table = 300

var domaine_vue = [-dim_joueur, dim_joueur]

const x = d3.scaleLinear()
.domain(domaine_vue)
.range([margin.left, height - margin.right])

const y = d3.scaleLinear()
.domain(domaine_vue)
.range([height - margin.bottom, margin.top])
const scale_x_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([margin.left,height - margin.right])
const scale_y_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([height - margin.bottom, margin.top])
const scale_x_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([margin.left,height - margin.right])
const scale_y_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([height - margin.bottom, margin.top])

var scale_x = scale_x_table
var scale_y = scale_y_table

//////////////////////////////////////////////
//////////////// Density map ////////////////
////////////////////////////////////////////

var contours = d3.contourDensity()
.x(d => scale_x(d.coor_balle_x))
//.x(d => (objet_options_affichage.mode == 'joueur')?scale_x(d.pos_balle_new_ref_x):scale_x(d.coor_balle_x))
.y(d => scale_y(-d.coor_balle_y))
//.y(d => (objet_options_affichage.mode == 'joueur')?scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)):scale_y(d.coor_balle_y))
.size([width, height])
.bandwidth(11)
.thresholds(30)
(csv)
const color = d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value));

var opacite_d = 1
const densite = svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.selectAll(".density")
.attr("class", "density")
.data(contours)
.enter().append("path")
.attr("stroke-width", (d, i) => i % 5 ? 0.25 : 1)
.attr("d", d3.geoPath())
.attr("opacity", opacite_d)
.attr("fill", d => d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value))(d.value))

///////////////////////////////////////////
//////////////// Pie chart ////////////////
///////////////////////////////////////////
var arc = d3.arc().innerRadius(50).outerRadius(200);
const data = [50,180-40-50,40]

var cote_joueur = 1
const start_angle = Math.PI/2
const end_angle = Math.PI*1.5
const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(start_angle)
.endAngle(end_angle)
.value(d => d)
const arcs = pie(data);

var opacite_c = 0
svg.append("g")
.selectAll(".arcs")
.data(arcs)
.join("path")
.attr("class", "arcs")
.attr("opacity", opacite_c)
.attr("d", arc).attr('transform', function(d) {
return 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur)+')';
})
.text(d => `${d.data}`);


// Piechart 2
const domEl = d3.create('div')
.style('text-align', 'center')
.style('font-family', 'Sans-serif').style("font", "14px times");
const r = 200,
secMinScale = d3.scaleLinear().domain([0, 60]).range([0, 360]),
hourScale = d3.scaleLinear().domain([0, 180]).range([0, 360]);
const axe_circulaire = svg.append('g').classed('axis', true)
.call(d3.axisRadialInner(
hourScale.copy().range([start_angle, end_angle]),
scale_y(200 - 3)
)
.ticks(12)
.tickSize(4)
)
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')


axe_circulaire.selectAll("text").style("font-size","10px")
axe_circulaire.selectAll("line").style("stroke-width", "1px").style("stroke","black").style("opacity",1)
axe_circulaire.select(".domain").style("stroke-width","1px").style("stroke","black").style("opacity",1)


const arcGenerator = d3.arc()
.outerRadius(scale_y(50))
.innerRadius(scale_y(50-1))
.startAngle(end_angle)
.endAngle(start_angle);
const axe_ortho = svg.append("path")
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator());

const start_angle_graduation = (cone_joueur == players[0])?Math.PI/2:-Math.PI/2
const end_angle_graduation = (cone_joueur == players[0])?Math.PI*1.5:Math.PI/2
const cote_joueur_graduation = (cone_joueur == players[0])?1:-1
for (let index = 0; index < 10; ++index) {
var arcGenerator_graduation = d3.arc()
.outerRadius(scale_y_joueur(index*50)-margin.top)
.innerRadius(scale_y_joueur(index*50-1)-margin.top)
.startAngle(end_angle_graduation)
.endAngle(start_angle_graduation);
svg.append("path")
.attr('transform', 'translate(' + scale_x_joueur(0) + ',' + scale_y_joueur(cote_joueur_graduation*dim_joueur) + ')')
.attr("d", arcGenerator_graduation())
.attr("opacity", 0.2);
}

//Ligne 1 cone
const ligne1 = svg.append('line')
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*scale_y(200)*Math.sin(Math.PI*(50+90)/180))
.attr("y2", -cote_joueur*scale_y(200)*Math.cos(Math.PI*(50+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')');
//Ligne 2 cone
const ligne2 = svg.append('line')
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*scale_y(200)*Math.sin(Math.PI*(180-40+90)/180))
.attr("y2", -cote_joueur*scale_y(200)*Math.cos(Math.PI*(180-40+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')');
///////////////////////////////////////
//////////////// Axis ////////////////
/////////////////////////////////////
const xAxis = g => g
.attr("transform", `translate(0,${scale_y(0)})`)
.call(d3.axisBottom(x))
const yAxis = g => g
.attr("transform", `translate(${scale_x(0)},0)`)
.call(d3.axisLeft(y))
const axe_x = svg.append("g")
.call(xAxis);
const axe_y = svg.append("g")
.call(yAxis);

//////////////////////////////////////////////
//////////////// Interactions ////////////////
//////////////////////////////////////////////

const tooltip = d3.select("body").append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
var symbolGenerator = d3.symbol()
.type(d3.symbolCross)
.size(30);
var pathData = symbolGenerator();
const pos_joueur1 = svg.selectAll(".cross_j1")
.data(csv.filter(d => d.nom == players[1]))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(-d.pos_y_joueurA_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")
.attr("stroke", "white")

const pos_joueur2 = svg.selectAll(".cross_j2")
.data(csv.filter(d => d.nom == players[0]))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurB_rebond) + ',' + scale_y(-d.pos_y_joueurB_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")
.attr("stroke", "white")
pos_joueur2.on("mouseenter", (event, d) => {
console.log("rebond hover", d)
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv+'\n'
+'Cluster: ' + d.cluster)
});
pos_joueur2.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
});
pos_joueur2.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
});
let domElement = svg.node()
// FONCTION UPDATE https://observablehq.com/@observablehq/simple-d3-update-pattern

///////////////////////////////////////////////
/////////// Fonction de transitions ///////////
///////////////////////////////////////////////
domElement.update = function(objet_options_affichage) {
// in this case we are expecting a function that will give us a y accessor
console.log("update")
// y = updateY
// yScale.domain(d3.extent(data, y))
const temps_transition = 1000

var domaine_vue = (objet_options_affichage.mode == 'joueur')?[-dim_joueur, dim_joueur]:[-dim_table,dim_table]
// definition des axes ici
const x = d3.scaleLinear()
.domain(domaine_vue)
.range([margin.left, height - margin.right])
const y = d3.scaleLinear()
.domain(domaine_vue)
.range([height - margin.bottom, margin.top])
var scale_x = scale_x_joueur
if (objet_options_affichage.mode == 'table'){
scale_x = scale_x_table
}
var scale_y = scale_y_joueur
if (objet_options_affichage.mode == 'table'){
scale_y = scale_y_table
}

const xAxis = g => g
.attr("transform", `translate(0,${scale_y(0)})`)
.call(d3.axisBottom(x))
const yAxis = g => g
.attr("transform", `translate(${scale_x(0)},0)`)
.call(d3.axisLeft(y))

axe_x.transition()
.duration(temps_transition)
.call(xAxis)

axe_y.transition()
.duration(temps_transition)
.call(yAxis)

var contours = d3.contourDensity()
.x(d => (objet_options_affichage.mode == 'joueur')?scale_x(d.pos_balle_new_ref_x):scale_x(d.coor_balle_x))
.y(d => (objet_options_affichage.mode == 'joueur')?scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)):scale_y(-d.coor_balle_y))
.size([width, height])
.bandwidth(objet_options_affichage.bandwidth)
.thresholds(30)
(objet_options_affichage.csv)
const color = d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value));
densite.data(contours)
.transition()
.duration(temps_transition)
.attr("stroke-width", (d, i) => i % 5 ? 0.25 : 1)
.attr("d", d3.geoPath())
.attr("opacity", objet_options_affichage.opacite_densite)
.attr("fill", d => d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value))(d.value))



//POS Joueurs
/*var symbolGenerator = d3.symbol()
.type(d3.symbolCross)
.size(30);
var pathData = symbolGenerator();
svg.append("cross_j1")
.selectAll(".cross_j1")
.data(objet_options_affichage.csv.filter(d => d.nom == players[1]), d => d.id_coup)
.enter()
.append("cross_j1")
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(d.pos_y_joueurA_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text(d.faute_du_point+' '+d.num_point)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
});

svg.selectAll(".cross_j1")
.data(objet_options_affichage.csv.filter(d => d.nom == players[1]), d => d.id_coup)
.exit()
.remove()
svg.selectAll(".cross_j1").data(objet_options_affichage.csv.filter(d => d.nom == players[0]), d => d.id_coup)
.join("path")
.transition().duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(d.pos_y_joueurA_rebond)+')';
})

svg.append("cross_j2")
.selectAll(".cross_j2")
.data(objet_options_affichage.csv.filter(d => d.nom == players[0]), d => d.id_coup)
.enter()
.append("cross_j2")
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(d.pos_y_joueurA_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text(d.faute_du_point+' '+d.num_point)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
});

svg.selectAll(".cross_j2")
.data(objet_options_affichage.csv.filter(d => d.nom == players[0]), d => d.id_coup)
.exit()
.remove()
svg.selectAll(".cross_j2").data(objet_options_affichage.csv.filter(d => d.nom == players[0]), d => d.id_coup)
.join("path")
.transition().duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(d.pos_y_joueurA_rebond)+')';
})

*/
pos_joueur1.transition()
.duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + ((objet_options_affichage.mode == 'table')?scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(-d.pos_y_joueurA_rebond):scale_x(0) + ',' + scale_y(dim_joueur))+')';
})

pos_joueur2.transition()
.duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + ((objet_options_affichage.mode == 'table')?scale_x(d.pos_x_joueurB_rebond) + ',' + scale_y(-d.pos_y_joueurB_rebond):scale_x(0) + ',' + scale_y(-dim_joueur))+')';
})


const data = [objet_options_affichage.angle_coup_droit,
180-objet_options_affichage.angle_revers-objet_options_affichage.angle_coup_droit,
objet_options_affichage.angle_revers
]

const cote_joueur = (objet_options_affichage.cone_joueur == players[0])?1:-1
const start_angle = (objet_options_affichage.cone_joueur == players[0])?Math.PI/2:-Math.PI/2
const end_angle = (objet_options_affichage.cone_joueur == players[0])?Math.PI*1.5:Math.PI/2


var arc = d3.arc()
.innerRadius(scale_y(objet_options_affichage.taille_cercle_minimal)-margin.top)
.outerRadius(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top);
const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(start_angle)
.endAngle(end_angle)
.value(d => d)
const arcs = pie(data);
svg.selectAll(".arcs")
.data(arcs).transition()
.duration(0)
.attr("opacity", (objet_options_affichage.mode == "joueur")?objet_options_affichage.opacite_cone:0)
.attr("d", arc).attr('transform', function(d) {
return 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur)+')';
})
.text(d => `${d.data}`);

axe_circulaire.transition()
.duration(0)
.call(d3.axisRadialInner(
hourScale.copy().range([start_angle, end_angle]),
scale_y(objet_options_affichage.taille_cercle_maximal - 1)-margin.top
)
.ticks(12)
.tickSize(8)
).attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0)

const arcGenerator_transition = d3.arc()
.outerRadius(scale_y(objet_options_affichage.taille_cercle_minimal)-margin.top)
.innerRadius(scale_y(objet_options_affichage.taille_cercle_minimal-3)-margin.top)
.startAngle(end_angle)
.endAngle(start_angle);
axe_ortho.transition()
.duration(0)
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator_transition())
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);
ligne1.transition()
.duration(0)
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.sin(Math.PI*(objet_options_affichage.angle_coup_droit+90)/180)) // On prend scale_y car c'est par rapport à la taille du cercle
.attr("y2", -cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.cos(Math.PI*(objet_options_affichage.angle_coup_droit+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);;

//Ligne 2 cone
ligne2.transition()
.duration(0)
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.sin(Math.PI*(180-objet_options_affichage.angle_revers+90)/180)) // On prend scale_y car c'est par rapport à la taille du cercle
.attr("y2", -cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.cos(Math.PI*(180-objet_options_affichage.angle_revers+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);;

// TABLES

svg.append("g")
.selectAll(".rect")
.data(objet_options_affichage.csv, d => d.id_coup)
.enter()
.append("rect")
.attr("x",d => (objet_options_affichage.mode == 'table')?scale_x(-TABLE_WIDTH/2):scale_x(d.hg_x))
.attr("y", d => (objet_options_affichage.mode == 'table')?scale_y(TABLE_LENGTH/2):scale_y(d.hg_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
.attr("width", d => Math.max((objet_options_affichage.mode == 'table')?scale_x(TABLE_WIDTH)-scale_x(0):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_x(TABLE_WIDTH)-scale_x(0)),0))
.attr("height", d => Math.max((objet_options_affichage.mode == 'joueur')?scale_y(0)-scale_y(TABLE_LENGTH):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_y(0)-scale_y(TABLE_LENGTH)),0))
.attr("fill", "transparent")
.attr("class", "rect")
.attr("opacity", 1)
.attr("stroke", "black")

svg.selectAll(".rect").data(objet_options_affichage.csv, d => d.id_coup)
.transition().duration(temps_transition)
.attr("x",d => (objet_options_affichage.mode == 'table')?scale_x(-TABLE_WIDTH/2):scale_x(d.hg_x))
.attr("y", d => (objet_options_affichage.mode == 'table')?scale_y(TABLE_LENGTH/2):scale_y(d.hg_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
.attr("width", d => Math.max((objet_options_affichage.mode == 'table')?scale_x(TABLE_WIDTH)-scale_x(0):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_x(TABLE_WIDTH)-scale_x(0)),0))
.attr("height", d => Math.max((objet_options_affichage.mode == 'joueur')?scale_y(0)-scale_y(TABLE_LENGTH):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_y(0)-scale_y(TABLE_LENGTH)),0))
.attr("fill", "transparent")
.attr("opacity", objet_options_affichage.opacite_table)
.attr("stroke", "black")

svg.selectAll(".rect")
.data(objet_options_affichage.csv, d => d.id_coup)
.exit()
.remove()

// REBONDS
svg.selectAll(".circle")
.data(objet_options_affichage.csv, d => d.id_coup)
.enter()
.append("circle")
.attr("class", "circle element")
.attr("cx", d => (objet_options_affichage.mode == 'table')?scale_x(d.coor_balle_x):scale_x(d.pos_balle_new_ref_x))
.attr("cy", d => (objet_options_affichage.mode == 'table')?scale_y(-d.coor_balle_y):scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
.attr("fill", d => c(d.nom))
.attr("r", 3)
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv+'\n'
+'Cluster: ' + d.cluster)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
})
.on("click", (event, d) => {
d.__selected = true
mutable num_point = {num_point:d.num_point,num_coup:d.num_coup}
console.log("click1", d.num_point)
});
// EXIT
svg.selectAll(".circle")
.data(objet_options_affichage.csv, d => d.id_coup)
.exit()
.remove()

var fonction_couleur = (objet_options_affichage.couleur == "cluster")?c_clusters():((objet_options_affichage.couleur == "winner")?c_winner():c())
svg.selectAll(".circle").data(objet_options_affichage.csv, d => d.id_coup)
.transition().duration(temps_transition)
.attr("cx", d => (objet_options_affichage.mode == 'table')?scale_x(d.coor_balle_x):scale_x(d.pos_balle_new_ref_x))
.attr("cy", d => (objet_options_affichage.mode == 'table')?scale_y(-d.coor_balle_y):scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
.attr("fill", d => (objet_options_affichage.couleur == "cluster")?c_clusters(d.cluster):((objet_options_affichage.couleur == "winner")?c_winner(d[objet_options_affichage.couleur]):c(d[objet_options_affichage.couleur])))
.attr("r", 3)
.attr("stroke", "white")
.attr("opacity", d => (d.appartient_zone)?1:0.5)
// .attr("stroke", d => (d.appartient_zone)?"red":((objet_options_affichage.couleur == "cluster")?c_clusters(d.cluster):c(d[objet_options_affichage.couleur])))
}
return domElement
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
objet_options_affichage_small_multiple = {
//kmeans(csv_augmente_un_joueur, nb_clusters)
var a = [{csv:csv_augmente,
options_affichage:options_affichage,
opacite_cone:opacite_cone,
opacite_densite:opacite_densite,
opacite_table:opacite_table,
taille_cercle_maximal:500-taille_cercle_maximal,
taille_cercle_minimal:500-taille_cercle_minimal,
mode:mode,
bandwidth:bandwidth,
angle_coup_droit:angle_coup_droit,
angle_revers:angle_revers,
cone_joueur:cone_joueur,
couleur:(couleur == "clusters")?"cluster":((couleur == "vainqueur")?"winner":"nom"),
centres_clusters:csv_centres_clusters.centres_clusters}];
return a
}
Insert cell
function small_multiple(csv) {

console.log("init (ne doit apparaître qu'une seule fois!")
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width/2, height]);

const dim_joueur = 500
const dim_table = 300

var domaine_vue = [-dim_joueur, dim_joueur]

const x = d3.scaleLinear()
.domain(domaine_vue)
.range([margin.left, height - margin.right])

const y = d3.scaleLinear()
.domain(domaine_vue)
.range([height - margin.bottom, margin.top])
var domaine_vue = (mode == 'joueur')?[-dim_joueur, dim_joueur]:[-dim_table,dim_table]
// definition des axes ici
const scale_x_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([margin.left,height - margin.right])
const scale_y_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([height - margin.bottom, margin.top])
const scale_x_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([margin.left,height - margin.right])
const scale_y_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([height - margin.bottom, margin.top])

var scale_x = scale_x_table
var scale_y = scale_y_table
var scale_x = scale_x_joueur
if (mode == 'table'){
scale_x = scale_x_table
}
var scale_y = scale_y_joueur
if (mode == 'table'){
scale_y = scale_y_table
}

//////////////////////////////////////////////
//////////////// Density map ////////////////
////////////////////////////////////////////

var contours = d3.contourDensity()
.x(d => scale_x(d.coor_balle_x))
//.x(d => (objet_options_affichage.mode == 'joueur')?scale_x(d.pos_balle_new_ref_x):scale_x(d.coor_balle_x))
.y(d => scale_y(-d.coor_balle_y))
//.y(d => (objet_options_affichage.mode == 'joueur')?scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)):scale_y(d.coor_balle_y))
.size([width, height])
.bandwidth(11)
.thresholds(30)
(csv)
const color = d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value));

var opacite_d = 0
const densite = svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.selectAll(".density")
.attr("class", "density")
.data(contours)
.enter().append("path")
.attr("stroke-width", (d, i) => i % 5 ? 0.25 : 1)
.attr("d", d3.geoPath())
.attr("opacity", opacite_d)
.attr("fill", d => d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value))(d.value))

///////////////////////////////////////////
//////////////// Pie chart ////////////////
///////////////////////////////////////////
var arc = d3.arc().innerRadius(50).outerRadius(200);
const data = [50,180-40-50,40]

var cote_joueur = 1
const start_angle = Math.PI/2
const end_angle = Math.PI*1.5
const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(start_angle)
.endAngle(end_angle)
.value(d => d)
const arcs = pie(data);

var opacite_c = 0
svg.append("g")
.selectAll(".arcs")
.data(arcs)
.join("path")
.attr("class", "arcs")
.attr("opacity", opacite_c)
.attr("d", arc).attr('transform', function(d) {
return 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur)+')';
})
.text(d => `${d.data}`);


// Piechart 2
const domEl = d3.create('div')
.style('text-align', 'center')
.style('font-family', 'Sans-serif').style("font", "14px times");
const r = 200,
secMinScale = d3.scaleLinear().domain([0, 60]).range([0, 360]),
hourScale = d3.scaleLinear().domain([0, 180]).range([0, 360]);
const axe_ortho = svg.append('g').classed('axis', true)
.call(d3.axisRadialInner(
hourScale.copy().range([start_angle, end_angle]),
scale_y(200 - 3)
)
.ticks(12)
.tickSize(4)
)
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')

axe_ortho.selectAll("text").style("font-size","10px")
axe_ortho.selectAll("line").style("stroke-width", "1px").style("stroke","black").style("opacity",1)
axe_ortho.select(".domain").style("stroke-width","1px").style("stroke","black").style("opacity",1)

const arcGenerator = d3.arc()
.outerRadius(scale_y(50))
.innerRadius(scale_y(50-1))
.startAngle(end_angle)
.endAngle(start_angle);
const axe_circulaire = svg.append("path")
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator());

const start_angle_graduation = (cone_joueur == players[0])?Math.PI/2:-Math.PI/2
const end_angle_graduation = (cone_joueur == players[0])?Math.PI*1.5:Math.PI/2
var arcGenerator_graduation = d3.arc()
.outerRadius(scale_y(400)-margin.top)
.innerRadius(scale_y(400-3)-margin.top)
.startAngle(end_angle_graduation)
.endAngle(start_angle_graduation);
const test = svg.append("path")
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator_graduation())
.attr("opacity", 1);
for (let index = 0; index < 10; ++index) {
var arcGenerator_graduation = d3.arc()
.outerRadius(scale_y(index*50)-margin.top)
.innerRadius(scale_y(index*50-3)-margin.top)
.startAngle(end_angle_graduation)
.endAngle(start_angle_graduation);
svg.append("path")
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator_graduation())
.attr("opacity", 0.3);
}

//Ligne 1 cone
const ligne1 = svg.append('line')
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*scale_y(200)*Math.sin(Math.PI*(50+90)/180))
.attr("y2", -cote_joueur*scale_y(200)*Math.cos(Math.PI*(50+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')');
//Ligne 2 cone
const ligne2 = svg.append('line')
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*scale_y(200)*Math.sin(Math.PI*(180-40+90)/180))
.attr("y2", -cote_joueur*scale_y(200)*Math.cos(Math.PI*(180-40+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')');
///////////////////////////////////////
//////////////// Axis ////////////////
/////////////////////////////////////
const xAxis = g => g
.attr("transform", `translate(0,${scale_y(0)})`)
.call(d3.axisBottom(x))
const yAxis = g => g
.attr("transform", `translate(${scale_x(0)},0)`)
.call(d3.axisLeft(y))
const axe_x = svg.append("g")
.call(xAxis);
const axe_y = svg.append("g")
.call(yAxis);

//////////////////////////////////////////////
//////////////// Interactions ////////////////
//////////////////////////////////////////////

const tooltip = d3.select("body").append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
var symbolGenerator = d3.symbol()
.type(d3.symbolCross)
.size(30);
var pathData = symbolGenerator();
const pos_joueur1 = svg.selectAll(".cross_j1")
.data(csv.filter(d => d.nom == players[1]))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(-d.pos_y_joueurA_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")

const pos_joueur2 = svg.selectAll(".cross_j2")
.data(csv.filter(d => d.nom == players[0]))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.pos_x_joueurB_rebond) + ',' + scale_y(-d.pos_y_joueurB_rebond)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")
pos_joueur2.on("mouseenter", (event, d) => {
console.log("rebond hover", d)
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv+'\n'
+'Cluster: ' + d.cluster)
});
pos_joueur2.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
});
pos_joueur2.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
});


svg.append("g")
.selectAll(".rect")
.data(csv, d => d.id_coup)
.enter()
.append("rect")
.attr("x",d => (mode == 'table')?scale_x(-TABLE_WIDTH/2):scale_x(d.hg_x))
.attr("y", d => (mode == 'table')?scale_y(TABLE_LENGTH/2):scale_y(d.hg_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
.attr("width", d => Math.max((mode == 'table')?scale_x(TABLE_WIDTH)-scale_x(0):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_x(TABLE_WIDTH)-scale_x(0)),0))
.attr("height", d => Math.max((mode == 'joueur')?scale_y(0)-scale_y(TABLE_LENGTH):((d.pos_y_joueurA_rebond > 0)?-1:1)*(scale_y(0)-scale_y(TABLE_LENGTH)),0))
.attr("fill", "transparent")
.attr("class", "rect")
.attr("opacity", 1)
.attr("stroke", "black")
.attr("opacity", opacite_table)


svg.selectAll(".circle")
.data(csv, d => d.id_coup)
.enter()
.append("circle")
.attr("class", "circle element")
.attr("cx", d => (mode == 'table')?scale_x(d.coor_balle_x):scale_x(d.pos_balle_new_ref_x))
.attr("cy", d => (mode == 'table')?scale_y(-d.coor_balle_y):scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)))
//.attr("fill", d => (couleur == "cluster")?c_clusters(d.cluster):c(d[couleur]))
.attr("fill", d => (couleur == "vainqueur")?c_winner(d["winner"]):c(d["nom"]))
.attr("r", 7)
.attr("stroke", "white")
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv+'\n'
+'Cluster: ' + d.cluster)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
})
.on("click", (event, d) => {
d.__selected = true
mutable num_point = {num_point:d.num_point,num_coup:d.num_coup}
console.log("click1", d.num_point)
});
let domElement = svg.node()
// FONCTION UPDATE https://observablehq.com/@observablehq/simple-d3-update-pattern

///////////////////////////////////////////////
/////////// Fonction de transitions ///////////
///////////////////////////////////////////////
domElement.update = function(objet_options_affichage) {
// in this case we are expecting a function that will give us a y accessor
console.log("update")
// y = updateY
// yScale.domain(d3.extent(data, y))
const temps_transition = 1000

var domaine_vue = (objet_options_affichage.mode == 'joueur')?[-dim_joueur, dim_joueur]:[-dim_table,dim_table]
// definition des axes ici
const x = d3.scaleLinear()
.domain(domaine_vue)
.range([margin.left, height - margin.right])
const y = d3.scaleLinear()
.domain(domaine_vue)
.range([height - margin.bottom, margin.top])
var scale_x = scale_x_joueur
if (objet_options_affichage.mode == 'table'){
scale_x = scale_x_table
}
var scale_y = scale_y_joueur
if (objet_options_affichage.mode == 'table'){
scale_y = scale_y_table
}

const xAxis = g => g
.attr("transform", `translate(0,${scale_y(0)})`)
.call(d3.axisBottom(x))
const yAxis = g => g
.attr("transform", `translate(${scale_x(0)},0)`)
.call(d3.axisLeft(y))

axe_x.transition()
.duration(temps_transition)
.call(xAxis)

axe_y.transition()
.duration(temps_transition)
.call(yAxis)

var contours = d3.contourDensity()
.x(d => (objet_options_affichage.mode == 'joueur')?scale_x(d.pos_balle_new_ref_x):scale_x(d.coor_balle_x))
.y(d => (objet_options_affichage.mode == 'joueur')?scale_y(d.pos_balle_new_ref_y+((d.nom == players[0])?-dim_joueur:dim_joueur)):scale_y(-d.coor_balle_y))
.size([width, height])
.bandwidth(objet_options_affichage.bandwidth)
.thresholds(30)
(objet_options_affichage.csv)
const color = d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value));
densite.data(contours)
.transition()
.duration(temps_transition)
.attr("stroke-width", (d, i) => i % 5 ? 0.25 : 1)
.attr("d", d3.geoPath())
.attr("opacity", objet_options_affichage.opacite_densite)
.attr("fill", d => d3.scaleSequential(d3.interpolateYlOrBr)
.domain(d3.extent(contours, d => d.value))(d.value))
pos_joueur1.transition()
.duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + ((objet_options_affichage.mode == 'table')?scale_x(d.pos_x_joueurA_rebond) + ',' + scale_y(-d.pos_y_joueurA_rebond):scale_x(0) + ',' + scale_y(dim_joueur))+')';
})

pos_joueur2.transition()
.duration(temps_transition)
.attr('transform', function(d) {
return 'translate(' + ((objet_options_affichage.mode == 'table')?scale_x(d.pos_x_joueurB_rebond) + ',' + scale_y(-d.pos_y_joueurB_rebond):scale_x(0) + ',' + scale_y(-dim_joueur))+')';
})

const data = [objet_options_affichage.angle_coup_droit,
180-objet_options_affichage.angle_revers-objet_options_affichage.angle_coup_droit,
objet_options_affichage.angle_revers
]

const cote_joueur = (objet_options_affichage.cone_joueur == players[0])?1:-1
const start_angle = (objet_options_affichage.cone_joueur == players[0])?Math.PI/2:-Math.PI/2
const end_angle = (objet_options_affichage.cone_joueur == players[0])?Math.PI*1.5:Math.PI/2


var arc = d3.arc()
.innerRadius(scale_y(objet_options_affichage.taille_cercle_minimal)-margin.top)
.outerRadius(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top);
const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(start_angle)
.endAngle(end_angle)
.value(d => d)
const arcs = pie(data);
svg.selectAll(".arcs")
.data(arcs).transition()
.duration(0)
.attr("opacity", (objet_options_affichage.mode == "joueur")?objet_options_affichage.opacite_cone:0)
.attr("d", arc).attr('transform', function(d) {
return 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur)+')';
})
.text(d => `${d.data}`);

axe_ortho.transition()
.duration(0)
.call(d3.axisRadialInner(
hourScale.copy().range([start_angle, end_angle]),
scale_y(objet_options_affichage.taille_cercle_maximal - 1)-margin.top
)
.ticks(12)
.tickSize(8)
).attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0)

const arcGenerator_transition = d3.arc()
.outerRadius(scale_y(objet_options_affichage.taille_cercle_minimal)-margin.top)
.innerRadius(scale_y(objet_options_affichage.taille_cercle_minimal-3)-margin.top)
.startAngle(end_angle)
.endAngle(start_angle);
axe_circulaire.transition()
.duration(0)
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("d", arcGenerator_transition())
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);
ligne1.transition()
.duration(0)
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.sin(Math.PI*(objet_options_affichage.angle_coup_droit+90)/180)) // On prend scale_y car c'est par rapport à la taille du cercle
.attr("y2", -cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.cos(Math.PI*(objet_options_affichage.angle_coup_droit+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);;

//Ligne 2 cone
ligne2.transition()
.duration(0)
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.sin(Math.PI*(180-objet_options_affichage.angle_revers+90)/180)) // On prend scale_y car c'est par rapport à la taille du cercle
.attr("y2", -cote_joueur*(scale_y(objet_options_affichage.taille_cercle_maximal)-margin.top)*Math.cos(Math.PI*(180-objet_options_affichage.angle_revers+90)/180))
.attr('transform', 'translate(' + scale_x(0) + ',' + scale_y(cote_joueur*dim_joueur) + ')')
.attr("opacity", (objet_options_affichage.mode == "joueur")?1:0);;
}
return domElement
}
Insert cell
document.getElementById('density')
Insert cell
Insert cell
Inputs.table(csv_augmente.filter(d => d.nom != cone_joueur).filter(d => d.coor_balle_x != null))
Insert cell
csv_augmente.filter(d => d.nom != cone_joueur).filter(d => d.coor_balle_x != null).filter(d => Math.abs(d.coor_balle_x) < 300).map(d => {return d.angle})
Insert cell
csv_augmente = csv_annotation.filter(d => d.coor_balle_x != null).filter(d => Math.abs(d.coor_balle_x) < 300).map(u => Object.assign({}, u, { approved: true })).map(d =>{
d.distance = Math.sqrt(d.pos_balle_new_ref_x ** 2 + d.pos_balle_new_ref_y ** 2)
d.angle = ((d.pos_balle_new_ref_y/d.pos_balle_new_ref_x >= 0)?180-Math.atan(Math.abs(d.pos_balle_new_ref_y/d.pos_balle_new_ref_x))*180/Math.PI:Math.atan(Math.abs(d.pos_balle_new_ref_y/d.pos_balle_new_ref_x))*180/Math.PI)
d.appartient_zone = (d.nom != cone_joueur &&
d.distance < taille_cercle_maximal &&
d.distance > taille_cercle_minimal &&
d.angle > angle_coup_droit &&
d.angle < 180-angle_revers)

d.angle_coup_prece = (d.pos_balle_x_prece == null)?null:(((d.pos_balle_y_prece-d.pos_balle_new_ref_y)/(d.pos_balle_x_prece-d.pos_balle_new_ref_x) >= 0)?180-Math.atan(Math.abs((d.pos_balle_y_prece-d.pos_balle_new_ref_y)/(d.pos_balle_x_prece-d.pos_balle_new_ref_x)))*180/Math.PI:Math.atan(Math.abs((d.pos_balle_y_prece-d.pos_balle_new_ref_y)/(d.pos_balle_x_prece-d.pos_balle_new_ref_x)))*180/Math.PI)
return d
})
Insert cell
viewof cone_joueur2 = Inputs.radio([players[0],players[1]], {value: players[0], label: "Joueur cone"})
Insert cell
{const svg = d3
.create("svg")
.attr("viewBox", [
-width / 2,
-margin.top,
width,
height / 2 + margin.bottom + margin.top
]);
var enda = (cone_joueur2 == players[0])?Math.PI*1.5:Math.PI/2
const cote_joueur = (cone_joueur2 == players[0])?1:-1
const start_angle = (cone_joueur2 == players[0])?Math.PI/2:-Math.PI/2
const end_angle = (cone_joueur2 == players[0])?Math.PI*1.5:Math.PI/2
const arcGenerator = d3.arc()
.outerRadius(100)
.innerRadius(99)
.startAngle(enda)
.endAngle(start_angle);

svg.append("path")
.attr("transform", "translate(150,120)")
.attr("d", arcGenerator());
return svg.node()}
Insert cell
piechart2 = {

const data = [angle_coup_droit,180-angle_revers-angle_coup_droit,angle_revers]
var arc = d3.arc().innerRadius(0).outerRadius(Math.min(width, height) / 4 - 1)
const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(Math.PI/2)
.endAngle(Math.PI * 1.5)
.value(d => d)
const arcs = pie(data);

const svg = d3
.create("svg")
.attr("viewBox", [
-width / 2,
-margin.top,
width,
height / 2 + margin.bottom + margin.top
]);
svg
.selectAll("path")
.data(arcs)
.join("path")
.attr("opacity", 0.05)
.attr("d", arc)
.append("title")
.text(d => `${d.data}`);
return svg.node();
}
Insert cell
marge_table = 10
Insert cell
Insert cell
piechart = {

const data = [angle_coup_droit,180-angle_revers-angle_coup_droit,angle_revers]
var arc = d3.arc().innerRadius(0).outerRadius(Math.min(width, height) / 2 - 1)

const pie = d3.pie()
.padAngle(0.01)
.sort(null)
.startAngle(Math.PI/2)
.endAngle(Math.PI * 1.5)
.value(d => d)
const arcs = pie(data);

const svg = d3
.create("svg")
.attr("viewBox", [
-width / 2,
-margin.top,
width,
height / 2 + margin.bottom + margin.top
]);
svg
.selectAll("path")
.data(arcs)
.join("path")
.attr("opacity", 0.05)
.attr("d", arc)
.append("title")
.text(d => `${d.data}`);

svg
.append("g")
.attr("font-family", "Acumin Pro")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${arc.centroid(d)})`)
.call(text =>
text
.filter(d => d.endAngle - d.startAngle > 0.1)
.append("tspan")
.attr("x", 0)
.attr("y", "0.7em")
.attr("fill-opacity", 0.7)
.text(d => d.data)
);
svg.append('line')
.style("stroke", "lightgreen")
.style("stroke-width", 2)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", (taille_cercle_maximal)*Math.sin(Math.PI*(angle_coup_droit+90)/180))
.attr("y2", -(taille_cercle_maximal)*Math.cos(Math.PI*(angle_coup_droit+90)/180));
return svg.node();
}
Insert cell
Insert cell
//test_chart.update([{csv:csv_augmente.filter(d => d.appartient_zone == true)}][0]) // we pass in an anonymous function
Insert cell
function test_deuxieme_visu(csv) {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width/2, height]);

const dim_joueur = 500
const dim_table = 200

var domaine_vue = [-dim_table, dim_table]
// definition des axes ici
const x = d3.scaleLinear()
.domain(domaine_vue)
.range([margin.left, height - margin.right])
const y = d3.scaleLinear()
.domain(domaine_vue)
.range([height - margin.bottom, margin.top])

const scale_x_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([margin.left,height - margin.right])
const scale_y_joueur = d3.scaleLinear().domain([-dim_joueur, dim_joueur]).range([height - margin.bottom, margin.top])
const scale_x_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([margin.left,height - margin.right])
const scale_y_table = d3.scaleLinear().domain([-dim_table, dim_table]).range([height - margin.bottom, margin.top])

var scale_x = scale_x_table
var scale_y = scale_y_table
const xAxis = g => g
.attr("transform", `translate(0,${scale_y(0)})`)
.call(d3.axisBottom(x))
const yAxis = g => g
.attr("transform", `translate(${scale_x(0)},0)`)
.call(d3.axisLeft(y))
const axe_x = svg.append("g")
.call(xAxis);
const axe_y = svg.append("g")
.call(yAxis);
const tables = svg.append("g")
.selectAll(".rect")
.data(csv)
.enter()
.append("rect")
.attr("x",d => scale_x(-TABLE_WIDTH/2))
.attr("y", d => scale_y(TABLE_LENGTH/2))
.attr("width", d => scale_x(TABLE_WIDTH)-scale_x(0))
.attr("height", d => scale_y(0)-scale_y(TABLE_LENGTH))
.attr("fill", "transparent")
.attr("opacity", 1)
.attr("stroke", "black")

/*const rebonds = svg.append("g")
.selectAll(".circle")
.data(csv)
.enter()
.append("circle")
.attr("cx", d => scale_x(d.coor_balle_x_meme_cote))
.attr("cy", d => scale_y(d.coor_balle_y_meme_cote))
.attr("fill", d => c(d[(couleur == "vainqueur")?"winner":"nom"]))
.attr("r", 3)*/

const tooltip = d3.select("body").append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
var symbolGenerator = d3.symbol()
.type(d3.symbolTriangle)
.size(30);
var pathData = symbolGenerator();
const revers = svg.selectAll(".triangle")
.data(csv.filter(d => d.lateralite_suiv == "revers"))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.coor_balle_x_meme_cote) + ',' + scale_y(-d.coor_balle_y_meme_cote)+')';
})
.attr('d', pathData)
.attr("fill", d => ((couleur == "joueur_frappe")?c(d["nom"]):c_winner(d["winner"])))
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
})
.on("click", (event, d) => {
d.__selected = true
mutable num_point = {num_point:d.num_point,num_coup:d.num_coup}
console.log("click1", d.num_point)
})

var symbolGenerator = d3.symbol()
.type(d3.symbolSquare)
.size(30);
var pathData = symbolGenerator();
const coup_droit = svg.selectAll(".square")
.data(csv.filter(d => d.lateralite_suiv == "coup_droit"))
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.coor_balle_x_meme_cote) + ',' + scale_y(-d.coor_balle_y_meme_cote)+')';
})
.attr('d', pathData)
.attr("fill", d => ((couleur == "joueur_frappe")?c(d["nom"]):c_winner(d["winner"])))
.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text('Faute: ' + d.faute_du_point+'\n'
+'Num point: ' + d.num_point+'\n'
+'Num coup: ' + d.num_coup+'\n'
+'Lateralité: ' + d.lateralite+'\n'
+'Lateralité suiv: ' + d.lateralite_suiv)
})
.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
})
.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
})
.on("click", (event, d) => {
d.__selected = true
mutable num_point = {num_point:d.num_point,num_coup:d.num_coup}
console.log("click1", d.num_point)
})


var symbolGenerator = d3.symbol()
.type(d3.symbolCross)
.size(30);
var pathData = symbolGenerator();
const pos_joueur = svg.selectAll(".cross_j1")
.data(csv)
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x((cone_joueur == players[0])?d.pos_x_joueurA_rebond_meme_cote:d.pos_x_joueurB_rebond_meme_cote) + ',' + scale_y((cone_joueur == players[0])?-d.pos_y_joueurA_rebond_meme_cote:-d.pos_y_joueurB_rebond_meme_cote)+')';
})
.attr('d', pathData)
.attr("fill", d => "red")


/////////////////////////
//////// Legende ////////
/////////////////////////
const debut_x = 100
const debut_y = 200
svg.append("text")
.text("Latéralité coup suivant:")
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x) + ',' + scale_y(debut_y)+')';
});;
var symbolGenerator = d3.symbol()
.type(d3.symbolTriangle)
.size(40);
var pathData = symbolGenerator();
const legende_revers = svg.selectAll(".legende")
.data([{x:debut_x+20,y:debut_y-20*2}])
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.x) + ',' + scale_y(d.y)+')';
})
.attr('d', pathData)
.attr("fill", d => d)
svg.append("text")
.text("Backhand")
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x+10+20) + ',' + scale_y(debut_y-20*2-2)+')';
});;
var symbolGenerator = d3.symbol()
.type(d3.symbolSquare)
.size(40);
var pathData = symbolGenerator();
const legende_coup_droit = svg.selectAll(".legende")
.data([{x:debut_x+20,y:debut_y-20}])
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.x) + ',' + scale_y(d.y)+')';
})
.attr('d', pathData)
.attr("fill", d => d)

svg.append("text")
.text("Forehand")
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x+10+20) + ',' + scale_y(debut_y-20-2)+')';
});


var symbolGenerator = d3.symbol()
.type(d3.symbolCircle)
.size(40);
var pathData = symbolGenerator();
const legende_couleur = svg.selectAll(".legende")
.data([{x:debut_x,y:debut_y-20*3,couleur:players[0]},{x:debut_x,y:debut_y-20*4,couleur:players[1]}])
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.x) + ',' + scale_y(d.y)+')';
})
.attr('d', pathData)
.attr("stroke", d => c(d.couleur))
.attr("fill", d => c(d.couleur))

svg.append("text")
.text(players[0])
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x+10) + ',' + scale_y(debut_y-20*3-2)+')';
});
svg.append("text")
.text(players[1])
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x+10) + ',' + scale_y(debut_y-20*4-2)+')';
});

var csv_cluster = []
for (let index = 0; index < nb_clusters; ++index) {
csv_cluster.push({x:debut_x,y:debut_y-20*5-index*20,couleur:index})
}

const legende_cluster = svg.selectAll(".legende")
.data(csv_cluster)
.join("path")
.attr('transform', function(d) {
return 'translate(' + scale_x(d.x) + ',' + scale_y(d.y)+')';
})
.attr('d', pathData)
.attr("stroke", d => c_clusters(d.couleur))
.attr("fill", d => c_clusters(d.couleur))

for (let index = 0; index < nb_clusters; ++index) {
svg.append("text")
.text("cluster " + index)
.attr("x", 0)
.attr("y", 0)
.attr('transform', function(d) {
return 'translate(' + scale_x(debut_x+10) + ',' + scale_y(debut_y-20*5-index*20)+')';
});
}
let domElement = svg.node()
// FONCTION UPDATE https://observablehq.com/@observablehq/simple-d3-update-pattern
/*domElement.update = function(objet_options_affichage) {
// in this case we are expecting a function that will give us a y accessor
console.log("update")
console.log(objet_options_affichage)

rebonds.data(objet_options_affichage.csv).transition()
.attr("cx", d => scale_x(d.coor_balle_x))
.attr("cy", d => scale_y(d.coor_balle_y))
.attr("fill", d => c(d[objet_options_affichage.couleur]))
.attr("r", 3)
}*/

return domElement
}
Insert cell
Insert cell
tooltip = {


// ICI ----> https://observablehq.com/@liris/viz-scatterplot
// TODO
// customize tooltip
// select point -> __selected
// brush https://observablehq.com/@john-guerra/d3-reusable-brushable-scatterplot-pattern
// programmatically select points
// hightlight

// from https://observablehq.com/@jianan-li/basic-tooltip
const w = 500
const h = 500
const svg = d3.select(DOM.svg(w, h));

// Object {x: -41.16970710754394, y: 165.39557558695475}
const x = d3.scaleLinear()
.domain(d3.extent(projectedData, d => d.x))
.range([margin.left, height - margin.right])

const y = d3.scaleLinear()
.domain(d3.extent(projectedData, d => d.y))
.range([margin.left, height - margin.right])
svg.selectAll("#circle").data(projectedData).enter().append("circle")
.attr("id", "circle")
.attr("cx", d => x(d.x))
.attr("cy", d => y(d.y))
.attr("r", 5)
.attr("fill", function(d) {
return d.__selected ? "red": "black"
})
const tooltip = d3.select("body").append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
const circle = svg.selectAll("#circle");

circle.on("click", (event, d) => {
d.__selected = true
console.log("click", d)
});
circle.on("mouseenter", (event, d) => {
tooltip.style("visibility", "visible")
.text(d.x)
});
circle.on("mousemove", (event, d) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px")
});
circle.on("mouseleave", (event, d) => {
tooltip.style("visibility", "hidden");
});
return svg.node();
}
Insert cell
Inputs.table(projectedData.filter(d => d.__selected))
Insert cell
Insert cell
styles = html`
<style>
.svg-tooltip {
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background: rgba(69,77,93,.9);
border-radius: .1rem;
color: #fff;
display: block;
font-size: 11px;
max-width: 320px;
padding: .2rem .4rem;
position: absolute;
text-overflow: ellipsis;
white-space: pre;
z-index: 300;
visibility: hidden;
}
</style>`
Insert cell
Insert cell
url_video_interactif = url_pipeline + "/" + competition + "/" + game + "/clips/" + nom_clip_selection + "/" + nom_clip_selection + ".mp4"+((moment_coup == "oui")?"#t="+debut:"")
Insert cell
nom_clip_selection = flat.filter(d=>d.competition_name == competition && d.game_name == game)[0]["game_clips"].filter(d=>d.name.includes('point_'+num_point.num_point))[0]['name']
Insert cell
debut = (csv.filter(d => d.num_point == num_point.num_point).filter(d => d.num_coup == num_point.num_coup)[0].debut-csv.filter(d => d.num_point == num_point.num_point).filter(d => d.num_coup == 1)[0].debut)/30
Insert cell
mutable num_point = {}
Insert cell
mutable debut_coup = 0
Insert cell
Insert cell
csv_annotation = csv.map(u => Object.assign({}, u, { approved: true })).map(d => {
d.y = (d.coor_balle_y == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - d.coor_balle_y:-(d.pos_y_joueurB_rebond - d.coor_balle_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - d.coor_balle_y:-(d.pos_y_joueurA_rebond - d.coor_balle_y)))
d.x = (d.coor_balle_x == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - d.coor_balle_x:-(d.pos_x_joueurB_rebond - d.coor_balle_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - d.coor_balle_x:-(d.pos_x_joueurA_rebond - d.coor_balle_x)))

d.y_frappe = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_frappe - d.coor_balle_y:-(d.pos_y_joueurB_frappe - d.coor_balle_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_frappe - d.coor_balle_y:-(d.pos_y_joueurA_frappe - d.coor_balle_y))
d.x_frappe = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_frappe - d.coor_balle_x:-(d.pos_x_joueurB_frappe - d.coor_balle_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_frappe - d.coor_balle_x:-(d.pos_x_joueurA_frappe - d.coor_balle_x))
return d
})
.map(d => {
d.pos_balle_new_ref_y = (d.coor_balle_y == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - d.coor_balle_y:-(d.pos_y_joueurB_rebond - d.coor_balle_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - d.coor_balle_y:-(d.pos_y_joueurA_rebond - d.coor_balle_y)))
d.pos_balle_new_ref_x = (d.coor_balle_x == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - d.coor_balle_x:-(d.pos_x_joueurB_rebond - d.coor_balle_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - d.coor_balle_x:-(d.pos_x_joueurA_rebond - d.coor_balle_x)))

d.pos_balle_new_ref_y_frappe = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_frappe - d.coor_balle_y:-(d.pos_y_joueurB_frappe - d.coor_balle_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_frappe - d.coor_balle_y:-(d.pos_y_joueurA_frappe - d.coor_balle_y))
d.pos_balle_new_ref_x_frappe = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_frappe - d.coor_balle_x:-(d.pos_x_joueurB_frappe - d.coor_balle_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_frappe - d.coor_balle_x:-(d.pos_x_joueurA_frappe - d.coor_balle_x))

d.x = d.pos_balle_new_ref_x
d.y = d.pos_balle_new_ref_y
return d
})
.map(d => {
d.coor_balle_y_meme_cote = (d.coor_balle_y == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.coor_balle_y:-d.coor_balle_y):
((d.pos_joueur_0_y < 0)?d.coor_balle_y:-d.coor_balle_y))
d.coor_balle_x_meme_cote = (d.coor_balle_x == null)?null:((d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.coor_balle_x:-d.coor_balle_x):
((d.pos_joueur_0_y < 0)?d.coor_balle_x:-d.coor_balle_x))

return d
})
.map(d => {
d.hg_x = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - TABLE_MINUS_2[3].distance_x:-(d.pos_x_joueurB_rebond - TABLE_MINUS_2[3].distance_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - TABLE_MINUS_2[3].distance_x:-(d.pos_x_joueurA_rebond - TABLE_MINUS_2[3].distance_x))
d.hg_y = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - TABLE_MINUS_2[3].distance_y:-(d.pos_y_joueurB_rebond - TABLE_MINUS_2[3].distance_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - TABLE_MINUS_2[3].distance_y:-(d.pos_y_joueurA_rebond - TABLE_MINUS_2[3].distance_y))

d.hd_x = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - TABLE_MINUS_2[2].distance_x:-(d.pos_x_joueurB_rebond - TABLE_MINUS_2[2].distance_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - TABLE_MINUS_2[2].distance_x:-(d.pos_x_joueurA_rebond - TABLE_MINUS_2[2].distance_x))
d.hd_y = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - TABLE_MINUS_2[2].distance_y:-(d.pos_y_joueurB_rebond - TABLE_MINUS_2[2].distance_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - TABLE_MINUS_2[2].distance_y:-(d.pos_y_joueurA_rebond - TABLE_MINUS_2[2].distance_y))

d.bg_x = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - TABLE_MINUS_2[0].distance_x:-(d.pos_x_joueurB_rebond - TABLE_MINUS_2[0].distance_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - TABLE_MINUS_2[0].distance_x:-(d.pos_x_joueurA_rebond - TABLE_MINUS_2[0].distance_x))
d.bg_y = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - TABLE_MINUS_2[0].distance_y:-(d.pos_y_joueurB_rebond - TABLE_MINUS_2[0].distance_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - TABLE_MINUS_2[0].distance_y:-(d.pos_y_joueurA_rebond - TABLE_MINUS_2[0].distance_y))

d.bd_x = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y > 0)?d.pos_x_joueurB_rebond - TABLE_MINUS_2[1].distance_x:-(d.pos_x_joueurB_rebond - TABLE_MINUS_2[1].distance_x)):
((d.pos_joueur_0_y > 0)?d.pos_x_joueurA_rebond - TABLE_MINUS_2[1].distance_x:-(d.pos_x_joueurA_rebond - TABLE_MINUS_2[1].distance_x))
d.bd_y = (d.joueur_frappe == players[0])?
((d.pos_joueur_0_y < 0)?d.pos_y_joueurB_rebond - TABLE_MINUS_2[1].distance_y:-(d.pos_y_joueurB_rebond - TABLE_MINUS_2[1].distance_y)):
((d.pos_joueur_0_y < 0)?d.pos_y_joueurA_rebond - TABLE_MINUS_2[1].distance_y:-(d.pos_y_joueurA_rebond - TABLE_MINUS_2[1].distance_y))

return d
})
.map(d => {
d.pos_y_joueurA_rebond_meme_cote = (d.pos_y_joueurA_frappe == null)?null:
((d.pos_y_joueurA_frappe < 0)?d.pos_y_joueurA_frappe:-d.pos_y_joueurA_frappe)
d.pos_x_joueurA_rebond_meme_cote = (d.pos_x_joueurA_frappe == null)?null:
((d.pos_y_joueurA_frappe < 0)?d.pos_x_joueurA_frappe:-d.pos_x_joueurA_frappe)
d.pos_y_joueurB_rebond_meme_cote = (d.pos_y_joueurB_frappe == null)?null:
((d.pos_y_joueurB_frappe < 0)?-d.pos_y_joueurB_frappe:d.pos_y_joueurB_frappe)
d.pos_x_joueurB_rebond_meme_cote = (d.pos_x_joueurB_frappe == null)?null:
((d.pos_y_joueurB_frappe < 0)?-d.pos_x_joueurB_frappe:d.pos_x_joueurB_frappe)

return d
})
.filter(d => Math.abs(d.pos_y_joueurA_rebond) < 700 && Math.abs(d.pos_balle_new_ref_y) < 700)
Insert cell
csv = d3.csv(url_csv_annotation, (d,i) => {
d = d3.autoType(d)
var num_point = d.num_point
d.id_coup = i
d.nom_jA_1 = players[0]
d.nom_jA_2 = players[1]
d.est_coup = 1
d.score_jA = csv_evolution_score[d.num_point]['score_jA']
d.score_jB = csv_evolution_score[d.num_point]['score_jB']
d.set_jA = csv_evolution_score[d.num_point]['set_jA']
d.set_jB = csv_evolution_score[d.num_point]['set_jB']
d.cote_jA = csv_evolution_score[d.num_point]['cote_jA']
d.cote_jB = csv_evolution_score[d.num_point]['cote_jB']
d.url = url_pipeline + "/" + competition + "/" + game + "/clips/" + flat.filter(d=>d.competition_name == competition && d.game_name == game)[0]["game_clips"].filter(d=>d.name.includes('point_'+num_point))[0]['name'] + "/" + flat.filter(d=>d.competition_name == competition && d.game_name == game)[0]["game_clips"].filter(d=>d.name.includes('point_'+num_point))[0]['name'] + "_crop_carre.jpg"
d.afficher_facette = true
d.afficher_similarite = false
return d
})
Insert cell
players = (json_game.playerA_2 != '' & json_game.playerA_2 != undefined)?[json_game.playerA_1,json_game.playerA_2,json_game.playerB_1,json_game.playerB_2]:[json_game.playerA,json_game.playerB,"balle"]
Insert cell
json_game = d3.json(url_json_game, d => {
d = d3.autoType(d)
return d
})
Insert cell
csv_evolution_score = d3.csv(url_csv_evolution_score, (d,i) => {
d = d3.autoType(d)
return d
})
Insert cell
url_json_game = url_pipeline + competition + "/" + game + "/" + game + "_game.json?test=test0"
Insert cell
url_csv_annotation = url_pipeline + competition + "/" + game +"/" + game + "_pos_fonction_joueur.csv?test=test3"
Insert cell
url_csv_evolution_score = url_pipeline + competition + "/" + game +"/" + game + "_evolution_score.csv?test=test1"
Insert cell
url_csv_score = url_pipeline + competition + "/" + game + "/" + "/" + game + "_evolution_score.csv?test=test3"
Insert cell
csv_score = d3.csv(url_csv_score, (d,i) => {
d = d3.autoType(d)
d.nom_jA = players[0]
d.nom_jB = players[1]
d.num_point = i+1
d.diff_jA = d.score_jA - d.score_jB
d.diff_jB = d.score_jB - d.score_jA
d.point_pour_jA = (d.point_pour == players[0])?1:0
d.point_pour_jB = (d.point_pour == players[1])?1:0
return d
})
Insert cell
Insert cell
Insert cell
Insert cell
import {url_pipeline} from "@liris/tt"
Insert cell
import {TABLE,TABLE_LENGTH,TABLE_WIDTH} from "@liris/tt-constantes"
Insert cell
import {TABLE_MINUS_2,CENTER_LINE,WHITE_NET,BLACK_NET_LEFT,BLACK_NET_RIGTH} from "@liris/tt-constantes"
Insert cell
import {querystrings} from "@liris/selecteur"
Insert cell
import {flat, select_competitions, select_games, select_match, select_clips} from "@liris/selecteur-tt"
Insert cell
import {evolution_score} from "@liris/tt-score"
Insert cell
d3 = require('d3@7', 'd3-radial-axis')
Insert cell
game_from_url = querystrings.get("game") || "NONE"
Insert cell
competition_from_url = querystrings.get("competition") || "NONE"
Insert cell
clip_from_url = querystrings.get("clip") || "NONE"
Insert cell
//import {kmeans} from "@spond/k-means-clustering-algorithm"
Insert cell
import {
Button,
Checkbox,
Toggle,
Radio,
Range,
Select,
Text,
Textarea,
Search,
Table
} from "@observablehq/inputs"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more