Aller au contenu principal

Champ de Vecteurs

Ouvrir le champ de vecteurs. Faites glisser la poignée circulaire rouge ou utilisez les touches fléchées pour déplacer le centre du vortex. Réglez l'intensité du tourbillon et régénérez de nouvelles phases d'onde.

L’aperçu sur l’accueil et la page dédiée démarrent en pause si le système demande de réduire les animations ; sinon, la lecture démarre automatiquement. Tous deux proposent un bouton pause/lecture permettant aussi de lancer l’animation manuellement. L’animation se suspend lorsque le champ sort de la zone visible ou que l’onglet est masqué.

Formulation Mathématique du Champ​

Le déplacement des particules est régi par un champ analytique V(x,y)=(vx,vy)\mathbf{V}(x, y) = (v_x, v_y) combinant un écoulement laminaire horizontal, une composante sinusoïdale et un vortex centré en (xs,ys)(x_s, y_s) :

  1. Noyau de lissage de la distance :

    r2=(x−xs)2+(y−ys)2+0,025,r^2 = (x - x_s)^2 + (y - y_s)^2 + 0{,}025,

    La petite constante ϵ=0,025\epsilon = 0{,}025 évite une singularité par division par zéro près du centre du vortex.

  2. Composantes horizontale et verticale de la vitesse :

    vx=0,8−0,22k(y−ys)r2,v_x = 0{,}8 - \frac{0{,}22 k (y - y_s)}{r^2}, vy=0,13sin⁡(9x+seed mod 19)+0,22k(x−xs)r2.v_y = 0{,}13 \sin(9x + \text{seed} \bmod 19) + \frac{0{,}22 k (x - x_s)}{r^2}.

    kk règle l’intensité du vortex et seed\text{seed} le décalage de phase de la sinusoïde.

  3. Normalisation du pas : le vecteur vitesse est normalisé, puis multiplié par un pas fixe de 0,0090{,}009 :

    Vstep=0,009⋅(vx,vy)∥(vx,vy)∥.\mathbf{V}_{\text{step}} = 0{,}009 \cdot \frac{(v_x, v_y)}{\|(v_x, v_y)\|}.
const field = (x, y) => {
const {source: s, strength: k, seed: currentSeed} = settings.current;
const dx = x - s.x;
const dy = y - s.y;
const radius = dx * dx + dy * dy + 0.025;
const vx = 0.8 - dy * 0.22 * k / radius;
const vy = 0.13 * Math.sin(x * 9 + currentSeed % 19) + dx * 0.22 * k / radius;
const length = Math.hypot(vx, vy) || 1;
return {x: vx / length * 0.009, y: vy / length * 0.009};
};

Optimisation par Tampon Graphique Hors-Écran​

Recalculer les 62 lignes de courant, avec au plus 250 pas d’intégration chacune, demanderait jusqu’à 15 500 évaluations du champ par trame. Une ligne s’arrête dès qu’elle sort des limites, ce qui peut réduire ce nombre. Le moteur vise 24 images par seconde et sépare les lignes mises en cache des particules mobiles :

  1. Prédessin hors écran (p5.Graphics) : les lignes sont recalculées dans un tampon p5.Graphics (paper) lorsque les paramètres ou les dimensions du canevas changent. Une intégration numérique dessine les lignes dans ce tampon et conserve leurs coordonnées dans des tableaux en mémoire.
  2. Déplacement des particules : à chaque trame, p.image(paper, 0, 0) copie toutes les lignes statiques en un seul appel. Un calcul modulo sur le compteur de trames sélectionne ensuite les points où dessiner les particules mobiles :
const trace = () => {
const css = getComputedStyle(node);
paper.background(css.getPropertyValue('--instrument-panel').trim());
ink = css.getPropertyValue('--instrument-red').trim();
const metal = css.getPropertyValue('--instrument-metal').trim();
paper.noFill();
paper.strokeWeight(0.8);
p.randomSeed(settings.current.seed);
paths = [];

for (let row = 0; row < 62; row++) {
let x = -0.1 + p.random(-0.025, 0.025);
let y = row / 51 - 0.1;
const points = [];
const color = p.color(row % 6 === 0 ? ink : metal);
color.setAlpha(row % 6 === 0 ? 145 : 78);
paper.stroke(color);
paper.beginShape();

for (let step = 0; step < 250; step++) {
points.push({x: x * p.width, y: y * p.height});
paper.vertex(x * p.width, y * p.height);
const v = field(x, y);
x += v.x; y += v.y;
if (x > 1.1 || y < -0.3 || y > 1.3) break;
}
paper.endShape();
paths.push(points);
}
dirty = false;
};

p.draw = () => {
if (!paper || disposed) return;
if (dirty) trace();
p.image(paper, 0, 0); // Un seul appel affiche toutes les lignes de courant statiques

p.noStroke(); p.fill(ink);
paths.forEach((points, index) => {
const point = points[(frame + index * 17) % points.length];
p.circle(point.x, point.y, index % 6 === 0 ? 3 : 1.6);
});
if (settings.current.playing) frame++;
};

Les particules animées avancent le long des tableaux de coordonnées précalculées.

Gestes et accessibilité​

  • Capture du pointeur : le glissement de la poignée active setPointerCapture pour conserver le geste lorsque le pointeur quitte le conteneur, sans sauts ni perte de l’interaction.
  • Accès au clavier : ArrowLeft, ArrowRight, ArrowUp et ArrowDown déplacent la poignée par incréments de 0,025.
  • Nettoyage : au démontage, le composant supprime les écouteurs d’événements et les observateurs, puis appelle explicitement paper.remove() et sketch.remove().
Explorer les liensOuvrir le réseau