IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Template d'un constructeur d'une classe et pointeur


Sujet :

Langage C++

  1. #1
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut Template d'un constructeur d'une classe et pointeur
    Bonjour,

    J'ai défini ma classe vertex de cette manière (je reprends uniquement le constructeur, le reste étant hors sujet) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class vertex
    {
    public:
    	/*
    	[Constructeur]
    	@param	dim				la dimension du point
    			coordinates		les coordonnées du point
    	*/
    	template<size_t dim>
    	vertex(double coordinates[dim])
    	{
    		this->dim = dim;
    		this->coordinates = new double[dim];
    		for(int i = 0; i < dim; ++i)
    			this->coordinates[i] = coordinates[i];
    	}
    private:
    	size_t dim;
            double * coordinates;
    }
    Je souhaiterais ensuite pouvoir écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    vertex *v = new vertex<3>(coordinates);
    Malheureusement, cette ligne ne compile pas. Le compilateur et l'IntelliSense de Visual Studio me disent qu'aucun constructeur par défaut n'existe pour la classe vertex. J'en déduis que l'expression ci-dessus est invalide.

    J'ai dû mal à voir où se situe l'erreur, pour autant que le constructeur soit lui-même valide.

    En vous remerciant d'avance,

    Nicolas.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    syntaxe originale, mais on dirait qu'elle n'est pas possible !

    Tel quel, ton appel construit une classe template, hors c'est juste ton constructeur qui est template. (ce qui donne déjà des noeuds au cerveau amha).
    Le plus simple serait alors de mettre un constructeur par défaut, et de créer une autre méthode template (init, create, ...).
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ou alors, procéder comme avec le singleton: un "constructeur statique"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class vertex:
    private:
    vertex(double coordinates[], int size);
    public:
    template<int n>
    static vertex make(double coordinates[]);
    };
    Mais comme toujours, le tableau est rarement un bon choix. Je préfère de loin la paire d'itérateur.

    Ou, dans ce cas précis, templater la classe elle-même, quitte à créer une classe de base non template
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    La question à se poser est sans doute :
    Est-ce que tu peux (comprend: est-il sensé de)mettre dans une seule et même collection, des vertex qui ont un nombre de coordonnées différents
    En toute honnêteté, je répondrais que, de toute évidence, le nombre de coordonnées que les vertex manipulent va dépendre du référentiel utilisé par le contexte global de ton application:

    Soit tu travailles en "deux dimensions", et tous tes vertex seront composés de deux coordonnées, soit tu travailles en "trois dimensions", et tous tes vertex seront composés... de trois coordonnées.

    A priori, il n'y a pas plus de sens à placer un vertex composé de trois coordonnées dans un contexte "deux dimensions" qu'à placer un vertex composé de deux coordonnées dans un contexte... 3D !

    Ce n'est donc pas ton constructeur qui doit être template, mais carrément toute la classe

    Après, il y a plusieurs possibilités pour y arriver, l'une d'elles étant simplement de passer un paramètre de type entier pour indiquer le nombre de dimensions, par exemple sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    template <int d>
    class vertex
    {
        public:
            enum{dimension = d;}
            vertex():coordinates(new double[dimension]){}
            ~vertex(){delete coordinates;}
           /* ne pas oublier le constructeur par copie et l'opérateur d'affectation
            * que je passe ici ;)
            */
            double get(int num)const {return coordinates[num];}
            void set(int num, double value){coordinates[num] = value;}
        private:
            double * coordinates;
    };
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    Bonjour, et merci pour vos réponses.

    En réalité, nous travaillons sur un programme de triangulation. Les coordonnées des points sont en trois dimensions puisque les triangles sont affichés en 3D via OpenGL. Cependant, la triangulation s'effectue dans le plan et ne requiert donc que les coordonnées (x,y); c'est pour cela que nous avons codé une série de fonctions template qui calculent la norme d'un vecteur, la distance entre deux vecteurs, etc. selon un certain nombre de coordonnées.

    Nous avons volontairement décidé d'écarter l'utilisation d'une classe template pour nous éviter d'avoir à nous soucier de la dimension complète d'un point. Ce que l'on veut, c'est pouvoir considérer un point 3D comme un point 2D en laissant "tomber" la dernière coordonnée qui correspond à la hauteur du point.

    Voici le code complet de la classe vertex réadaptée avec l'idée de Bousk.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    class vertex
    {
    public:
    	/*
    	[Constructeur]
    	Il se contente d'attribuer le pointeur nul au pointeur des coordonnées.
    	*/
    	vertex() : dim(0), coordinates(nullptr)
    	{
    	}
     
    	/*
    	[Destructeur]
    	*/
    	~vertex(void)
    	{
    		/* libère la mémoire allouée au tableau des coordonnées du point */
    		if(coordinates != nullptr)
    			delete coordinates;
    	}
     
    	/*
    	Initialise l'objet vertex.
    	@param:		coordinates		les coordonnées du point
    	*/
    	template<size_t dim>
    	void init(double coordinates[dim])
    	{
    		if(dim < 1)
    			throw exception("vertex::init<size_t dim> : le paramètre dimensionnel est invalide.");
    		if(this->coordinates != nullptr)
    			delete coordinates;
     
    		this->dim = dim;
    		this->coordinates = new double[dim];
    		for(size_t i = 0; i < dim; ++i)
    			this->coordinates[i] = coordinates[i];
    	}
     
    	/*
    	Retourne les coordonnées du point dans un tableau pré-alloué.
    	@param:		coordinates		les coordonnées du point
    	*/
    	template<size_t dim>
    	void get_coordinates(double coordinates[dim]) const
    	{
    		if(this->coordinates == nullptr)
    			throw exception("vertex::get_coordinates<size_t dim> : l'objet n'a pas été initialisé (vertex::init<size_t dim>).");
    		if(dim > this->dim || dim < 0)
    			throw exception("vertex::get_coordinates<size_t dim> : le paramètre dimensionnel est invalide.");
    		for(size_t i = 0; i < dim; ++i)
    			coordinates[i] = this->coordinates[i];
    	}
     
    	/*
    	Modifie les coordonnées du point.
    	@param:		new_coordinates		les nouvelles coordonnées du point
    	*/
    	template<size_t dim>
    	void set_coordinates(double new_coordinates[dim])
    	{
    		if(this->coordinates == nullptr)
    			throw exception("vertex::set_coordinates<size_t dim> : l'objet n'a pas été initialisé (vertex::init<size_t dim>).");
    		if(dim > this->dim || dim < 0)
    			throw exception("vertex::set_coordinates<size_t dim> : le paramètre dimensionnel est invalide.");
    		for(size_t i = 0; i < dim; ++i)
    			this->coordinates[i] = coordinates[i];
    	}
    private:
    	size_t dim;
    	double * coordinates;
    };
    J'imagine que les utilisations de la classe exception et du keyword nullptr sont également discutables. :p

    Mais comme toujours, le tableau est rarement un bon choix. Je préfère de loin la paire d'itérateur.
    Pouvez-vous m'en dire davantage ?

    Bien à vous,

    Nicolas.

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Le gros problème d'un tableau, c'est qu'on ignore sa taille.

    C'est mal adapté à cette situation, mais en gros, ma proposition était de reprendre l'interface générale de la STL.
    Avec une paire d'itérateurs, directement tirés de <iterator>, vous aurez une indépendance vis à vis du conteneur utilisé par l'appeleur de votre constructeur.

    En gros ca permet d'avoir un appel en vertex v(truc.begin(), truc.end());.

    Cela dit, pour votre problème, il suffirait d'avoir des points 3D, et de ne lire que les deux premières dimensions…
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Je ne vois aucun avantage à ce choix par rapport à une simple structure Point avec 3 membres x,y,z, quitte à ne pas utiliser z dans certains algorithmes qui traitent le point en 2D.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    tout à fait. mais sans contexte, ca pouvait passer
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  9. #9
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    En fait, j'avais en tête de créer quelque chose de générique qui puisse fonctionner aussi bien en dimension deux qu'en dimension N > 2, même si le cas N > 3 n'est absolument pas pertinent pour le problème qui nous intéresse.

    Puisque l'algorithme de triangulation ne requiert que les deux premières coordonnées, je voulais épargner au programme de devoir récupérer une coordonnée qui ne nous intéresse pas. Le programme doit tourner très rapidement sur un nombre très grand de points. Je me doute que l'opération d'affectation d'une valeur à une entrée d'un tableau ne coûte rien du tout, mais cela me semblait tout de même être un effort inutile dont nous pourrions nous passer.

    Après, nous pourrions effectivement nous contenter d'une simple structure avec trois fonctions accesseurs de type get_x(), get_y() et get_z().

    Cela dit, ça ne change rien au fait qu'il nous faut des fonctions qui calculent la distance entre deux points ou la norme d'un vecteur en dimension 2 et en dimension 3. L'utilisation de templates dans ce cas là permettrait de n'avoir qu'une seule fonction norm qui fonctionne en 2D ou en 3D plutôt que deux fonctions séparées.

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Nadd Voir le message
    Bonjour, et merci pour vos réponses.

    En réalité, nous travaillons sur un programme de triangulation. Les coordonnées des points sont en trois dimensions puisque les triangles sont affichés en 3D via OpenGL. Cependant, la triangulation s'effectue dans le plan et ne requiert donc que les coordonnées (x,y); c'est pour cela que nous avons codé une série de fonctions template qui calculent la norme d'un vecteur, la distance entre deux vecteurs, etc. selon un certain nombre de coordonnées.

    Nous avons volontairement décidé d'écarter l'utilisation d'une classe template pour nous éviter d'avoir à nous soucier de la dimension complète d'un point. Ce que l'on veut, c'est pouvoir considérer un point 3D comme un point 2D en laissant "tomber" la dernière coordonnée qui correspond à la hauteur du point.
    Cela n'a pas de sens...

    Tu peux, très facilement, envisager d'avoir une conversion de point 3D en point2D si tu n'as pas besoin d'une des coordonnées, mais le principe est strictement le même que lorsque tu travailles en système métrique et que tu veux avoir des informations en pouces, pieds, et miles : tu change de référentiel, et tu dois donc convertir tes valeurs

    La conversion peut, effectivement, se "contenter" d'ignorer la composante "Y", ou de la considérer comme ayant une valeur nulle, mais elle doit se faire

    Avec la classe template sous la forme que j'ai présentée, tu pourrais avoir deux fonctions proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    vertex<2> to2D(vertex<3> const & origin)
    {
        vertex<2> ret;
        ret.set(0,origin.get(0));
        ret.set(1,origin.get(2));
        return ret;
    }
    vertex<3> to3D(vertex<2> const & origin)
    {
     
        vertex<3> ret;
        ret.set(0,origin.get(0));
        ret.set(2,origin.get(1));
        ret.set(1,origin.get(0.0));
        return ret;
    }
    Quand tu dois trianguler, tu convertis de la sorte tous tes plans et toutes tes droites, puis tu les manipule en "deux dimensions", pour terminer en convertissant le résultat en 3D en vue de l'affichage

    Ou bien, tu te bases sur le fait que, quoi qu'il arrive, tu travailles toujours avec des points "3D", mais que, quoi qu'il arrive, le résultat d'une triangulation aura toujours une valeur arbitraire pour la coordonnée Y, qui peut etre, au choix nulle ou équivalente à la moyenne de la coordonnée des coordonnées Y des points de référence(ou toute autre valeur valide mais arbitraire de ton choix).

    Dans ce cas, la classe vertex pourrait d'ailleurs ne plus être template
    Mais, quoi qu'il en soit, il n'y a strictement aucun sens à vouloir faire tenir des points 2D et des points 3D dans une seule et même collection
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    La conversion peut, effectivement, se "contenter" d'ignorer la composante "Y", ou de la considérer comme ayant une valeur nulle, mais elle doit se faire.
    Imaginons que j'ai un point en 3D : vertex<3> *v. Si je le convertis en 2D pour effectuer la triangulation puis que je le reconvertis en 3D, je ne risque pas de perdre une composante ?

    Sinon, j'ai saisi l'incohérence d'avoir des points 2D et 3D dans une même collection.

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Nadd Voir le message
    Imaginons que j'ai un point en 3D : vertex<3> *v. Si je le convertis en 2D pour effectuer la triangulation puis que je le reconvertis en 3D, je ne risque pas de perdre une composante ?
    De toutes façons, a priori, ta triangulation ne donnera aucune information de hauteur...

    Tu "perds" donc d'office cette information au sujet de l'objet triangulé

    C'est la raison pour laquelle je te donne différentes pistes à suivre afin d'arriver à déterminer une valeur "cohérente" pour cette composante

    Tu pourrais tout aussi parfaitement, si tu sais que les objets triangulés sont d'office au sol, envisager de chercher la hauteur du point connu le plus proche du résultat de ta triangulation, mais bon, cela ne marchera pas avec des avions, par exemple
    Sinon, j'ai saisi l'incohérence d'avoir des points 2D et 3D dans une même collection.
    Là, tu me rassures
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    En réalité, le programme reçoit un nuage de points en 3D à partir d'un fichier de données. C'est la première étape du programme que de charger ces données et de créer des objets vertex. La triangulation s'effectue ensuite dans le plan et les triangles sont ensuite affichés en 3D grâce à OpenGL. L'objectif est donc de trianguler une surface dans l'espace. La hauteur des points est inutile pour la triangulation mais elle sert pour le rendu final. Il faut donc pouvoir conserver les données de hauteur.

    EDIT: L'intérêt majeur dans notre construction était de pouvoir appliquer la triangulation aussi bien sur des objets vertex 2D que 3D. Nous pourrions considérer une classe vertex<size_t dim> template comme proposé et rendre notre fonction de triangulation également template en se limitant au cas où dim = 2 et dim = 3. Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<size_t dim>
    class vertex { ... }
     
    template<size_t dim>
    void triangulation(vector<vertex<dim>*> vertices, ...);
    Notre collection ne contiendrait alors que des points de même dimension et la fonction de triangulation pourrait admettre des points 2D ou 3D, selon que l'utilisateur décide de trianguler uniquement dans le plan ou bien avec un rendu dans l'espace. Puisque la fonction de triangulation n'a besoin que des coordonnées 2D, il faudrait éventuellement conserver les méthodes et fonctions templates afin de pouvoir (par exemple) calculer la norme 2D d'un vecteur 3D. Ce que je voulais surtout éviter c'était de me retrouver avec des fonctions doubles du genre : norm2D, norm3D, dist2D, dist3D, dot2D, dot3D, etc.

  14. #14
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Alors effectivement, tu dois tout templater: la classe de point, la triangulation, la norme, et ainsi de suite.

    Surtout que pour trianguler dans un plan, tu dois d'abord changer de repère, opération qui considère les 3 coordonnées.

    Cependant, avec une bonne classe de point et de vecteur, la plupart des operations sont implémentables comme des operator libres.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  15. #15
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    Bonjour, et merci encore pour vos réponses.

    J'ai suivi vos conseils et décidé de simplifier drastiquement la structure du programme. Puisque le problème qui nous intéresse ne fait intervenir que la 2D (pour la triangulation) et la 3D (pour le rendu), j'ai décidé de nous y tenir pour la classe vertex.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class vertex
    {
    public:
    	vertex(double x, double y, double z);
    	~vertex(void);
    	inline double x(void) const { return _x; }
    	inline double y(void) const { return _y; }
    	inline double z(void) const { return _z; }
    	inline void get_coordinates(point<dimension::dim2> &p) const { p[0] = _x; p[1] = _y; }
    	inline void get_coordinates(point<dimension::dim3> &p) const { p[0] = _x; p[1] = _y; p[2] = _z; }
    	inline double &x(void) { return _x; }
    	inline double &y(void) { return _y; }
    	inline double &z(void) { return _z; }
    	inline void set_coordinates(double x, double y, double z) { _x = x; _y = y; _z = z; }
    private:
    	double _x, _y, _z;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    enum dimension : size_t
    {
    	dim2 = 2,
    	dim3 = 3
    };
     
    template<dimension dim>
    class point
    {
    public:
    	point() { ... }
    	point(double coordinates[dim]) { ... }
    	inline void get_coordinates(double coordinates[dim]) const { ... }
    	inline double const & operator[](size_t index) const { ... }
    	inline double & operator[](size_t index) { ... }
    private:
    	double coordinates[dim];
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template<dimension dim>
    inline double norm(vertex *u)
    {
    	point<dim> p;
    	u->get_coordinates(p);
     
    	double sum = 0.;
    	for(int i = 0; i < dim; ++i)
    		sum += pow(p[i], 2);
     
    	return sqrt(sum);
    };
    J'espère que l'usage du keywork inline est justifié et que la structure général est suffisamment claire.

    Merci encore pour votre aide,

    Nicolas.

  16. #16
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Il y a quand meme une question qui me turlupine dans ta solution : quelle est la différence entre un vertex et un point, surtout s'il est 3D

    A priori le phénomène d'alignement aidant, il n'y a aucune différence en terme d'utilisation de la mémoire entre le fait d'avoir une structure qui manipules trois double sous la forme d'un tableau proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct Point
    {
        double coordinate[3];
    };
    et la même structure qui les manipule sous la forme de trois variables séparées et ressemblant à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct Point
    {
        double x;
        double y;
        double z;
    };
    si ce n'est que la première forme impose une convention supplémentaire dans le sens où tout le monde doit savoir à quel axe de coordonnée correspond chaque valeur de coordinate.


    Or, c'est finalement à peu près ce à quoi correspondent tes actuelles classes Point <3> et vertex

    D'après ce que tu expliquais au début, ta classe vertex était, sommes toutes, fort proche de ton actuelle classe point.

    Je t'ai convaincu du fait qu'il n'avait aucun sens d'avoir des points deux dimensions et des points trois dimensions "mélangés" dans une seule et même collection, et je maintiens ce que j'ai dit hier.

    Mais c'est peut etre se "faire du mal pour rien" que d'avoir deux représentations sommes toutes identiques de la même donnée, et de "s'amuser" à passer de l'une à l'autre, non

    Comme je l'ai dit plus tot, rien ne t'empêche d'avoir une classe Point (car il est vrai que ca semble mieux coller à l'idée que le nom vertex) qui représente manipule d'office des points 3D, mais de "laisser purement et simplement tomber" (ou du moins de ne pas la prendre en compte ou de lui donner une valeur arbitraire ) la coordonnée Y quand elle ne sert à rien.

    Ceci dit:
    1. Quand tu fournis l'implémentation d'une fonction directement dans la définition d'une classe, la fonction est implicitement déclarée inline. L'utilisation du mot clé inline est donc inutile pour les fonctions de ta classe vertex (par contre, le mot clé est obligatoire pour la fonction norm )
    2. Il n'est jamais bon de renvoyer une référence non constante sur un membre de classe, car cela brise purement et simplement l'encapsulation : Fournis des accesseurs constants et des mutateurs si besoin en est, mais pas une fonction non constante qui renvoie une référence non constante sur un membre
    En outre, on pourrait sans doute arguer du fait qu'un objet de type vertex (ou point) a sémantique de valeur, et que tout objet de ce type est donc d'office constant (si tu change une des coordonnée d'un point, tu obtiens, purement et simplement un autre point ) pour justifier le fait que seuls les accesseurs constants sont réellement utiles et intéressants

    Je persiste encore une fois et je signe le fait que, à mon sens, tu pourais très bien te contenter d'une seule et unique classe (nommons la Point, si tu veux) qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Point
    {
        public:
            Point(double x, double y, double z):x_(x),y_(y),z_(z){}
            double x() const{return x_;}
            double y() const{return y_;}
            double z() const{return z_;}
        private:
            double x_;
            double y_;
            double z_;
    };
    qui serait utilisée partout en considérant que, dans certaines circonstances ( lors de la triangulation, essentiellement), il faut simplement envisager l'utilisation d'une valeur arbitraire pour la coordonnée Y.

    La seule question restant à trancher étant la manière de déterminer cette valeur arbitraire, et j'ai ouvert suffisamment de voies pour te permettre de faire preuve d'imagination

    Ta fonction norm prendrait alors la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    double norm(const Point & origin)
    {
        double result = (origin.x()*origin.x() +
                         origin.y()*origin.y() +
                         origin.z()*origin.z());
        return sqrt(result);
    }
    Et, quand il s'agit de faire la triangulation, tu travailles uniquement sur les coordonnées X et Z, avant d'évaluer la valeur arbitraire pour y (au pire, en te basant sur le fait que y vaut 0.0 lors des premiers calculs)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  17. #17
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    Bonjour, et merci pour votre réponse.

    La classe point avait pour objectif de permettre la conversion de coordonnées 3D en coordonnées 2D en laissant simplement tomber la coordonnée en Z. Il s'agissait simplement de planquer un tableau de coordonnées dans un objet.

    En géométrie numérique, on travaille sur des maillages. La terminologie à appliquer est celle de triangle et de vertex, d'où l'utilisation de ces noms pour les casses. Même si un vertex possède la même structure qu'un point, il ne désigne a priori pas la même chose.

    Comme je l'ai dit plus tot, rien ne t'empêche d'avoir une classe Point (car il est vrai que ca semble mieux coller à l'idée que le nom vertex) qui représente manipule d'office des points 3D, mais de "laisser purement et simplement tomber" (ou du moins de ne pas la prendre en compte ou de lui donner une valeur arbitraire ) la coordonnée Y quand elle ne sert à rien.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    double norm(const Point & origin)
    {
        double result = (origin.x()*origin.x() +
                         origin.y()*origin.y() +
                         origin.z()*origin.z());
        return sqrt(result);
    }
    Lorsque le programme démarre, il charge toutes les points à partir d'un fichier de données. Ces points sont en trois dimensions et toutes leurs composantes ont de l'importance. Pour la triangulation, seules les deux premières m'intéressent : la troisième ne doit donc pas être prise en compte dans la norme, d'où l'idée d'un template. Pour être honnête, je ne saisis pas très bien votre idée. Comment la classe point pourrait-elle savoir qu'elle doit attribuer une valeur spécifique à l'une de ses composants pour "simuler" le cas 2D, à moins que l'idée n'était de faire une copie du point et d'attribuer la valeur nulle à l'une de ses composantes ?

    Ceci dit:
    1. Quand tu fournis l'implémentation d'une fonction directement dans la définition d'une classe, la fonction est implicitement déclarée inline. L'utilisation du mot clé inline est donc inutile pour les fonctions de ta classe vertex (par contre, le mot clé est obligatoire pour la fonction norm )
    2. Il n'est jamais bon de renvoyer une référence non constante sur un membre de classe, car cela brise purement et simplement l'encapsulation : Fournis des accesseurs constants et des mutateurs si besoin en est, mais pas une fonction non constante qui renvoie une référence non constante sur un membre
    J'en prends très bonne note. Honte sur moi !

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Nadd Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    double norm(const Point & origin)
    {
        double result = (origin.x()*origin.x() +
                         origin.y()*origin.y() +
                         origin.z()*origin.z());
        return sqrt(result);
    }
    Lorsque le programme démarre, il charge toutes les points à partir d'un fichier de données. Ces points sont en trois dimensions et toutes leurs composantes ont de l'importance. Pour la triangulation, seules les deux premières m'intéressent : la troisième ne doit donc pas être prise en compte dans la norme, d'où l'idée d'un template. Pour être honnête, je ne saisis pas très bien votre idée. Comment la classe point pourrait-elle savoir qu'elle doit attribuer une valeur spécifique à l'une de ses composants pour "simuler" le cas 2D, à moins que l'idée n'était de faire une copie du point et d'attribuer la valeur nulle à l'une de ses composantes ?
    Oui, tout à fait...


    Quand tu fais ta triangulation, tu pars d'un certain nombre de points connus qui participent au relèvement, qui vont fournir chacun un azimut vers l'objet triangulé qui va représenter un "point inconnu".

    Dans 99% des cas, deux points de relèvements et les deux azimuts correspondant pourraient suffire, sauf, bien sur, si l'objet triangulé se trouve dans l'alignement des deux points de relèvement.

    C'est pour cela que l'on va travailler généralement avec minimum trois points de relèvements, mais chaque point de relèvement supplémentaire a une chance d'apporter une précision supérieure

    Quand tu vas faire ta triangulation, tu vas prendre les points de relèvement deux à deux , pour obtenir un triangle dont deux des angles sont les points de relèvement et dont le troisième est l'objet triangulé, dont tu ignores, a priori, la position.

    Les cotés de ce triangles sont représentés par :
    1. l'azimut reliant les deux points de relèvement pour le premier coté
    2. l'azimut relevé à partir du premier point de relèvement pour le deuxième coté
    3. l'azimut relevé à partir du deuxième point de relèvement pour le troisième coté
    et ce sont ces informations qui te permettront de déterminer le point où se trouve... l'objet triangulé.

    Ta fonction de triangulation va donc... renvoyer un point qui ne fait, au début du moins, pas partie de l'ensemble des points déjà connus, et sur une hauteur inconnue, et qui pourrait ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Point triangulation(std::vector<Point> const &, std::vector<Azimuts> const &) //un azimut par point 
    {
         /* calcul des position X et Z en utilisant les points 2 à 2 et les azimuts
          * correspondant
          * (règle des sinus ??? )
          */
         return Point(X, 0.0,Z)
    }
    et tu vas tracer une verticale passant par le point renvoyé pour indiquer que ton objet triangulé se trouve "à une altitude inconnue" "quelque part à la verticale de ce point".

    Si tu sais que ton objet triangulé est d'office "au sol", tu pourras chercher le point connu le plus proche du point calculé afin d'en récupérer l'altitude

    Tu pourrais, bien sur, en plus d'avoir un azimut, envisager d'avoir un angle d'élévation, vu que tu connais l'altitude de tes points de relèvements, et tu pourrais appliquer le même principe, mais sur base des élévations pour déterminer l'altitude de ton objet triangulé

    Mais cela implique que, pour calculer l'altitude, chaque point de relèvement doit te donner un azimut et une élévation, donc deux angles au total

    Et comme il n'est pas impossible que le point triangulé se trouve exactement à l'emplacement d'un de tes points de maillage, tu peux, bien entendu, avoir deux fois le même point en mémoire : une fois pour indiquer le point de maillage et une autre fois pour indiquer l'objet triangulé, ce dernier pouvant être "retiré de la liste" une fois qu'il n'a plus de raison d'être
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  19. #19
    Membre régulier Avatar de Nadd
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    160
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 160
    Points : 95
    Points
    95
    Par défaut
    Je pense qu'il doit y avoir un mal entendu. Lorsque je parle de triangulation, je fais référence à la triangulation de Delaunay, qui consiste, à partir d'un nuage de points connus dans le plan, à former des triangles de Delaunay de manière à obtenir un maillage sur lequel nous pouvons appliquer une méthode d'éléments finis. Il ne s'agit pas de la technique permettant de calculer la position d'un point à partir d'autres points. Je suis navré de n'avoir pas été clair là-dessus...

    Le nuage de points est connu et il n'y aucun point mystère à calculer. L'objectif du programme est simplement de former des triangles de Delaunay avec ces points, ce qui s'effectue dans le plan, et ensuite afficher via OpenGL ces triangles dans l'espace 3D en utilisant les données de hauteur.

  20. #20
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Ah, ok... au temps pour moi

    Ceci dit, même si la triangulation ne prend pas l'altitude du point en compte, tu te retrouveras quand même au final avec un triangle donc chaque point est la copie d'un point de ton maillage d'origine, et le calcul de la normale prendra toujours la composante y en compte aussi

    La seule chose, c'est que la composante y des points ne sera pas prise en compte lors de la triangulation.

    Mais cela n'implique absolument pas le fait... qu'elle n'existe pas
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 11
    Dernier message: 26/02/2015, 01h20
  2. Prototype du constructeur d'une classe template
    Par Meseira dans le forum Langage
    Réponses: 11
    Dernier message: 05/01/2011, 08h47
  3. Réponses: 2
    Dernier message: 04/12/2005, 21h10
  4. Réponses: 5
    Dernier message: 20/11/2005, 11h15
  5. Réponses: 3
    Dernier message: 06/11/2005, 18h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo