Projet : étape 4 (semaine 5 du semestre)

Buts

Nous allons cette semaine ajouter des obstacles et des sources de particules à notre projet.

Préliminaires :

Je vous rappelle également tous les autres conseils donnés en semaine 3.


[2+] Exercice P6 : Les obstacles

On s'intéresse ici aux obstacles, c'est-à-dire des objets du monde ne pouvant pas bouger et dont la seule « activité » est d'exercer sur les particules une force double de la force usuelle (cf le descriptif général ou les compléments mathématiques du projet).

Ces obstacles sont donc de simples « objets » dont nous aurons uniquement besoin de connaître la position et de pouvoir calculer le point le plus proche (du centre de gravité) d'une particule sur l'obstacle (cf compléments mathématiques).

A noter que pour les particules aussi nous avons besoin de connaître la position.

[Question P6.1] Comment proposez-vous de représenter les obstacles dans votre projet ? Cela modifie-t-il la classe « Particule » ?

Explicitez et justifiez votre choix dans votre fichier REPONSES.

Plus concrètement, en utilisant bien sûr votre conception détaillée ci-dessus, réalisez une classe Plan permettant de représenter un obstacle plan tel que décrit dans le complément mathématique du projet.

Cette classe devra au moins avoir :

Ceux qui souhaitent agrémenter leur projet pourront également ajouter une classe Dalle représentant une portion rectangulaire de plan en ajoutant simplement à la classe précédente (par héritage) une largeur, une longueur et un vecteur unitaire (indiquant la longueur, le vecteur indiquant la largeur étant le produit vectoriel de la normale et la longueur).

Fournissez pour finir un programme testObstacle.cc qui devrait au minimum (et commencer par cela) :

Ce programme devra donc commencer strictement par l'affichage suivant :

Plan d'origine (0 0 0) et de normale (0 0 1) : 
distance à (0 7 0) : (0 7 0)
distance à (0 0 10) : (0 0 0)
distance à (2 0 0) : (2 0 0)
distance à (5 -4 6) : (5 -4 0)
Plan d'origine (0 0 0) et de normale (0 1 0) : 
distance à (0 7 0) : (0 0 0)
distance à (0 0 10) : (0 0 10)
distance à (2 0 0) : (2 0 0)
distance à (5 -4 6) : (5 0 6)
Plan d'origine (0 0 0) et de normale (1 0 0) : 
distance à (0 7 0) : (0 7 0)
distance à (0 0 10) : (0 0 10)
distance à (2 0 0) : (0 0 0)
distance à (5 -4 6) : (0 -4 6)
Plan d'origine (0 0 0) et de normale (0.57735 0.57735 0.57735) : 
distance à (0 7 0) : (-2.33333 4.66667 -2.33333)
distance à (0 0 10) : (-3.33333 -3.33333 6.66667)
distance à (2 0 0) : (1.33333 -0.666667 -0.666667)
distance à (5 -4 6) : (2.66667 -6.33333 3.66667)
Plan d'origine (1 2 3) et de normale (0.267261 0.801784 0.534522) : 
distance à (0 7 0) : (-0.571429 5.28571 -1.14286)
distance à (0 0 10) : (-0.5 -1.5 9)
distance à (2 0 0) : (2.78571 2.35714 1.57143)
distance à (5 -4 6) : (5.57143 -2.28571 7.14286)

Pour finir, ajoutez à la classe « Particule » une autre méthode ajouteForce() prenant un obstacle comme argument et qui ajoute à l'attribut force de la particule courante, la force qu'exerce l'obstacle sur celle-ci.

Voir le complément mathématique du projet pour l'implémentation de cette méthode.


[2-] Exercice P7 : Sources de particules

Contexte

On souhaite ici faciliter la création et l'ajout de particules dans le système. On va pour cela créer des « sources » de particules. Pour simplifier, on ne parle ici que de sources ponctuelles, mais vous pouvez bien sûr étendre le concept. Si vous le faites, expliquez bien votre conception dans votre fichier CONCEPTION.

De telles sources auront pour but d'ajouter périodiquement des particules au système suivant un modèle (de particule) donné.

Les vitesses initiales des particules produits par la source seront tirés au hasard suivant une loi gaussienne (pas de panique, on vous explique ci-dessous ce que c'est et comment faire).

Bases

Développez une classe Source possédant :

Finalement, pour éviter que la source ne produise trop de particules (et fasse « exploser » votre simulation), ajoutez un attribut etat qui sera « allumé » ou « éteint », permettant d'activer ou d'arrêter la source. Par défaut, une source est active (elle produit).

Ajoutez à cette classe le(s) constructeur(s), destructeur, accesseur(s) et manipulateur(s) que vous estimez nécessaire(s). (Il ne devrait pas y en avoir beaucoup.)

Ajoutez également deux méthodes on() et off() pour allumer/éteindre la source.

La classe Source aura de plus une méthode creation() permettant de générer des particules. Cette méthode aura comme paramètres un tableau dynamique de pointeurs sur des particules (à remplir) et un pas de temps (nombre réel).

Avant de vous expliquer comment cette méthode fonctionne, il faut que je vous explique comment générer des nombres aléatoires et en particulier des nombres suivant une loi gaussienne.

Nombres au hasard

La génération de nombres aléatoire s'étant depuis C++11 largement améliorée (en qualité) mais largement complexifiée, nous vous fournissons ici le code d'une classe Aleatoire qui suffira à nos besoins dans ce projet : Aleatoire.h, Aleatoire.cc.

La première chose à bien comprendre avec les nombres « aléatoires » sur ordinateur c'est que ce sont en fait des séries de nombres ayant les propriétés statistiques de nombres aléatoires, mais dont on peut, si nécessaire contrôler l'ordre au moyen d'une valeur initiale appelée « graine ». On parle de nombre « pseudo-aléatoires ». On peut donc, si on le souhaite, initialiser (une fois pour toute, par exemple dans le main ou dans le constructeur de votre simulateur) la séquence de nombres aléatoires voulue.

Cette notion de graine est très utile pour « debugger » car si l'on initialise le générateur de nombres aléatoires avec la même graine, on aura alors exactement la même suite de nombres pseudo-aléatoires (et on pourra donc reproduire exactement le même bug si nécessaire).

Si l'on souhaite par contre du « vrai » aléatoire, au sens où la séquence de nombres tirés ne soit pas la même à chaque fois, on peut alors choisir la graine elle-même au hasard.

Pour tout cela, nous avons donc muni notre classe Aleatoire de deux constructeurs :

Si donc vous voulez générer des « vrais » nombres aléatoires, vous ferez :

  Aleatoire generateur;

(utilisation du constructeur par défaut) ; mais si par contre vous voulez imposer la graine, par exemple 1234, pour débugger, alors vous ferez :

  Aleatoire generateur(1234);

Ensuite, notre générateur de nombres aléatoires peut générer suivant deux distributions de probabilités : soit la loi uniforme (entre deux valeurs), soit la loi normale (aussi appellée « gaussienne »), laquelle a deux paramètres : une moyenne et un écart-type.

Commençons par des nombres au hasard répartis de façon uniforme. Utilisez pour cela la méthode uniforme(). Par exemple :

Aleatoire generateur;
... generateur.uniforme(1.0, 2.5) ...

pour un tirage uniforme d'un double entre 1 et 2.5.

Passons maintenant à une loi gaussienne de moyenne m et d'écart-type s :

dP(x) = 1 / ((2π)½s) exp( - (x-m)2 / (2s2) ) dx.

Pour réaliser un tirage suivant une loi gaussienne, utilisez la méthode gaussienne() de la classe Aleatoire par exemple comme ceci :

Aleatoire generateur;
... generateur.gaussienne(1.5, 0.5) ...

pour un tirage de moyenne 1.5 et d'écart-type 0.5.

Au niveau de l'intégration : un seul générateur aléatoire sera créé par simulation (c.-à-d. dans le main() pour les programme de tests, puis dans « la bonne classe » pour votre projet complet (à voir plus tard) et sera passé (par référence) à la source.

Génération de particules

L'algorithme pour générer des particules sera le suivant :

  1. Avec debit le débit de la source et dt le pas de temps reçu en argument, déterminer le nombre de particule à générer via :

      double fraction(debit*dt); // fraction = debit "vrai", mais a priori non entier
      int nombre(fraction);      // partie entière
      fraction -= nombre;        // partie fractionnaire
      // on ajoute 1 au hasard, proportionnellement à la partie fractionnaire :
      if ( generateur.uniforme(0.0, 1.0) < fraction ) ++nombre; 
    

    (Explication pour celles et ceux qui veulent comprendre : cela a pour effet d'avoir en moyenne le bon débit : pendant un temps dt, on doit tirer debit*dt particules ; mais ce nombre n'est pas forcément entier (il peut même être compris entre 0 et 1, par exemple pour des dt petits). On prend donc la partie entière de ce nombre, à laquelle on ajoute 1 de temps en temps, au hasard proportionnellement à la partie fractionnaire de ce nombre.)

Ensuite, pour chaque particule tirée :

  1. copier la particule modèle ;
  2. modifier la vitesse de la nouvelle particule en tirant chacune de ses composantes suivant une une loi gaussienne de moyenne la coordonnée correspondante du vecteur « vitesse initiale moyenne » stocké en attribut dans la classe Source et d'écart-type la valeur également stockée comme attribut de cette classe (cf la description initiale).

    (Remarque : il se trouve que tirer les coordonnées cartésiennes chacune suivant une loi gaussienne conduit à un tirage gaussien du vecteur).

  3. Modifiez également le rayon de cette nouvelle particule en tirant à nouveau suivant une loi gaussienne, de moyenne le rayon de la particule modèle et comme écart-type la valeur correspondante stockée comme attribut dans la classe Source (cf la description initiale).

Amélioration

Dès la semaine prochaine : pour éviter que les particules générées ne soient trop proches surtout pour des débits trop élevés, déplacez de suite chaque nouvelle particule créée d'un pas de temps suivant les lois du mouvement.

Test

Créez un programme testSource.cc pour tester vos sources.


Dernière mise à jour le 4 mars 2026
Last modified: Wed Mar 4, 2026