# la dataviz pour les hipsters

Web2Day 2017, Nantes

Alexis Jacomy

(twitter|github|slides).com/jacomyal

# Approche théorique

## approche théorique :

(x - x_0)^2 + (y - y_0)^2 \le r_0^2
$(x - x_0)^2 + (y - y_0)^2 \le r_0^2$
\dfrac{r_0^2}{(x - x_0)^2 + (y - y_0)^2} \ge 1
$\dfrac{r_0^2}{(x - x_0)^2 + (y - y_0)^2} \ge 1$

## approche théorique :

\displaystyle\max_{i=0}^n\dfrac{r_i^2}{(x - x_i)^2 + (y - y_i)^2} \ge 1
$\displaystyle\max_{i=0}^n\dfrac{r_i^2}{(x - x_i)^2 + (y - y_i)^2} \ge 1$
\displaystyle\sum_{i=0}^n\dfrac{r_i^2}{(x - x_i)^2 + (y - y_i)^2} \ge 1
$\displaystyle\sum_{i=0}^n\dfrac{r_i^2}{(x - x_i)^2 + (y - y_i)^2} \ge 1$

# Les heatmaps, donc

## Étape 1 : nuage de points

import DATA from '../assets/data.json';

const MAX_X = Math.max(...DATA.map(([vx, vy]) => vx));
const MAX_Y = Math.max(...DATA.map(([vx, vy]) => vy));
const POINTS = document.getElementById('points');

// Créé un point pour chaque individu :
DATA.forEach(([ vx, vy ]) => {
const point = document.createElement('div');
point.classList.add('point');
point.style.left = (vx / MAX_X * 100) + '%';
point.style.bottom = (vy / MAX_Y * 100) + '%';

POINTS.appendChild(point);
});

## Étape 2 : Filtres

<!DOCTYPE html>
<html>
<head>
<style>
#points {
filter: url('#posterize');
}
</style>
</head>
<body>
<div style="visibility:hidden;">
<svg>
<filter id="posterize">
<feComponentTransfer>
<feFuncR type="discrete" tableValues="0 0.25 0.5 0.75 1" />
<feFuncG type="discrete" tableValues="0 0.25 0.5 0.75 1" />
<feFuncB type="discrete" tableValues="0 0.25 0.5 0.75 1" />
</feComponentTransfer>
</filter>
</svg>
</div>
</body>
</html>

# Development !

## Étape 1 : Le camembert

const total = DATA.map(a => a.value).reduce((a, b) => a + b);

DATA.forEach(({ value, color, label }) => {
const angle = 2 * Math.PI * value / total;

const material = new THREE.MeshPhongMaterial({ color });

// Forme en 2D :
const geometry = new THREE.Shape();
geometry.moveTo(0, 0);
geometry.arc(0, 0, SIZE, acc, acc + angle, false);
geometry.lineTo(0, 0);

// Forme en 3D :
const extruded = new THREE.ExtrudeGeometry(
geometry,
{ amount: value * SIZE / 2 }
);

const slice = new THREE.Mesh(extruded, material);
scene.add(slice);

acc += angle;
});

## Étape 2 : Les labels

function makeTextSprite(message) {
// On créé la texture :
//   1. On créé un canvas
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

context.fillStyle = '#000';
context.font = '30px sans-serif';

//   2. On dessine le texte
context.fillText(message, 0, 30);

//   3. On génère une texture
const map = new THREE.Texture(canvas);
map.needsUpdate = true;
const material = new THREE.SpriteMaterial({ map });

//   4. On génère une Sprite
const sprite = new THREE.Sprite(material);
sprite.scale.set(100, 50, 1);

return sprite;
}

## Étape 3 : LE swag

// On créé d'abord 2 caméras, puis on merge les images
// grace au fragment shader suivant :

uniform sampler2D mapLeft;
uniform sampler2D mapRight;
varying vec2 vUv;

void main() {
vec4 colorL, colorR;
vec2 uv = vUv;

colorL = texture2D(mapLeft, uv);
colorR = texture2D(mapRight, uv);

gl_FragColor = vec4(
colorL.g * 0.7 + colorL.b * 0.3,
colorR.g,
colorR.b,
colorL.a + colorR.a
);
}

By Alexis Jacomy

# La dataviz pour les hipsters

Web2Day 2017, Nantes

• 2,300