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 combinant un écoulement laminaire horizontal, une composante sinusoïdale et un vortex centré en :
-
Noyau de lissage de la distance :
La petite constante évite une singularité par division par zéro près du centre du vortex.
-
Composantes horizontale et verticale de la vitesse :
règle l’intensité du vortex et le décalage de phase de la sinusoïde.
-
Normalisation du pas : le vecteur vitesse est normalisé, puis multiplié par un pas fixe de :
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 :
- Prédessin hors écran (
p5.Graphics) : les lignes sont recalculées dans un tamponp5.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. - 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
setPointerCapturepour conserver le geste lorsque le pointeur quitte le conteneur, sans sauts ni perte de l’interaction. - Accès au clavier :
ArrowLeft,ArrowRight,ArrowUpetArrowDowndé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()etsketch.remove().