15 votes

Point aléatoire sur une sphère donnée

Je veux sélectionner des points aléatoires sur une sphère donnée. Cette page l'explique assez bien :

http://mathworld.wolfram.com/SpherePointPicking.html ("Obtenir des points tels que toute petite zone sur la sphère...")

Mais je ne suis pas entièrement sûr de l'implémenter correctement en JavaScript, car j'ai peu de moyens de le tester correctement :

var u = random();
var v = random();
var angle1 = 2 * Math.PI * u;
var angle2 = Math.pow(Math.cos (2 * v - 1), -1);
X = X0 + (radius * Math.sin(angle1) * Math.cos(angle2));
Y = Y0 + (radius * Math.sin(angle1) * Math.sin(angle1));
Z = Z0 + (radius * Math.cos(angle1));

Je ne suis surtout pas sûr d'avoir bien compris le cos(-1), que j'ai implémenté comme "Le cosinus à la puissance de -1".

36voto

Neil Lamoureux Points 336

L'algorithme du cube ne donnera pas une distribution uniforme sur la sphère - en particulier les zones proches des projections des coins auront la distribution la plus dense de points et près des centres des faces des cubes seront les plus basses.

Vous pouvez comprendre cela intuitivement puisque le volume du cube projeté sur la sphère sous-jacente est plus grand près des coins que près des centres des faces du cube. En fait, le volume d'un petit morceau (qui se projette sur un petit cercle sur la sphère) est proportionnel au cube de la taille du vecteur partant de l'origine et passant par le centre du petit cercle jusqu'au point de la sphère qu'il intersecte.

Ainsi, le volume relatif sur le centre d'une face de cube (comme (1,0,0)) est de 1, mais pour un coin (par exemple, (1,1,1)), c'est le cube de sqrt(3) ou 1,73 cubé, environ 5,2, donc presque 5 fois plus dense !

La fonction spreadPoints() pourrait faire un meilleur travail, mais je n'en suis pas sûr.

Il y a quelques erreurs dans votre JavaScript - l'utilisation de la fonction pow(..,-1) au lieu de acos(), des confusions sur les angles et l'absence de l'objet Math pour l'appel random()..,

Voici un JavaScript similaire mais correct pour faire ce que dit le lien Wolfram :

/*
Returns a random point of a sphere, evenly distributed over the sphere.
The sphere is centered at (x0,y0,z0) with the passed in radius.
The returned point is returned as a three element array [x,y,z]. 
*/
function randomSpherePoint(x0,y0,z0,radius){
   var u = Math.random();
   var v = Math.random();
   var theta = 2 * Math.PI * u;
   var phi = Math.acos(2 * v - 1);
   var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
   var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
   var z = z0 + (radius * Math.cos(phi));
   return [x,y,z];
}

24voto

6502 Points 42700

Je pense qu'un algorithme plus simple est

  1. Choisissez un point aléatoire à l'intérieur du [-1,1]x[-1,1]x[-1,1] cube
  2. Si x*x + y*y + z*z > 1 répéter à partir de 1
  3. Normaliser la division x , y y z por Math.sqrt(x*x + y*y + z*z)

en d'autres termes, il suffit de choisir un point aléatoire à l'intérieur de la sphère et de le projeter sur la sphère. Ne vous inquiétez pas trop de la "boucle", car la probabilité qu'un point se trouve à l'extérieur de la sphère est d'environ 0,4764 et, en moyenne, la boucle nécessitera moins de deux itérations.

Vous pouvez voir cet algorithme en action sur ce lien . Notez que si vous utilisez chrome, il y aura une certaine bande autour d'un équateur qui, à mon avis, est un bug dans Math.random ou juste un générateur aléatoire de mauvaise qualité (fonctionne bien sur Firefox ou Safari, mais le même problème est visible aussi sur le navigateur Android). La bande est beaucoup plus visible avec un nombre plus élevé de points (par exemple 10000 au lieu des 1000 points que j'utilise maintenant pour garder l'animation lisse). EDIT : Ce bug a maintenant été corrigé sur chrome et Android.

Notez que si vous cherchez une méthode pour distribuer des points de manière égale sur une sphère, vous pouvez faire quelque chose de plus joli en choisissant dix points aléatoires comme décrit ci-dessus, puis en acceptant seulement celui qui a la plus grande distance 3D par rapport à l'ensemble des points déjà choisis. Ceci est toujours globalement aléatoire (c'est-à-dire que la probabilité qu'un disque avec un rayon donné reçoive un point est la même pour tous les disques de la sphère) mais distribuera mieux les points si vous avez besoin de faire un "échantillonnage" de la sphère. Cette fonction est codée comme suit spreadPoints() dans le fichier html pointé par le lien.

Vous pouvez voir la différence entre les deux approches ici :

enter image description here

Les deux sphères ont 1000 points aléatoires dessinés sur elles : la sphère de gauche a utilisé des points aléatoires uniformes, la sphère de droite a plutôt fait le choix en choisissant chaque point parmi dix candidats aléatoires pour maximiser la distance des points déjà choisis.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X