Bonjour,
Je cherche à implémenter un graphe de scène en Javascript mais j'ai du mal à trouver une bonne définition, savoir ce qu'il y a dans geode, node ...
Je ne sais pas non plus si c'est possible en Javascript !
Pourriez vous m'aider ? Merci
Bonjour,
Je cherche à implémenter un graphe de scène en Javascript mais j'ai du mal à trouver une bonne définition, savoir ce qu'il y a dans geode, node ...
Je ne sais pas non plus si c'est possible en Javascript !
Pourriez vous m'aider ? Merci
Salut,
Créer un arbre en JavaScript est tout à fait possible, le plus simple est d’ajouter une propriétée Childs et Parent a un objet pour l’insérer dans un arbre
un truc du genre :
Si tu cherche à étudier l’architecture d’un excellent moteur 3D en Javascript, je te conseil de jeter un œil a O3D de google
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 function Attach(parent,child) { if(parent.Childs==undefined) parent.Childs = [ child ]; else parent.Childs.push(child); child.Parent = parent; } var earth = { Name : "Earth", Matrix : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], }; var moon = { Name : "Moon", Matrix : [ 1, 0, 0, 384467, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], }; Attach(earth,moon); alert(earth.Childs[0].Name); //"Moon" alert(moon.Parent.Name); // "Earth"
En effet, O3D a l'air d'être pas mal foutu. Je vais essayer de comprendre un peu les détails et "mettre les mains dans le cambouis" lol pour développer mon propre moteur.
Faire un moteur de type scénographe est évidemment tout à fait possible en Javascript ; je me suis d'ailleurs amusé à en faire un pour développer des jeux sur la freebox (cf. ma signature).
Les quelques idées de base:
- si tu est un minimum familier avec d'autres langages orientés objet (genre C++, Java), je te conseille de passer par une librairie additionnelle pour te donner un moyen d'écrire du code 'simple et limpide' (pour qui a déjà touché aux langages sus-cités) avec les principales fonctionnalités 'objet' à portée de main: constructeurs, héritage, ... Perso je me suis tourné vers Base.js.
- l'architecture des classes est assez simple au premier abord. Exemple:
* un singleton qui va s'occuper de tout ce qui concerne le contexte (chargement, canvas, gestion du temps, ...). Appelons le 'Stage'.
* un singleton pour gérer tout ce qui touche aux inputs (clavier, souris, ...).
* une classe 'Scene' qui sera la classe de base qui contient le graphe de scène et les animations en cours dans la scène. Le Stage exécute et affiche une et une seule scène à la fois.
* une classe générique représentant un élément dans le graphe (noeud ou feuille) avec les caractéristiques qui y sont rattachées, genre: position, taille, visibilité, alpha. Appelons là 'Node'.
* une classe dérivant de 'Node' qui représente un noeud dans le graphe, et qui est donc capable de contenir des enfants (typiquement avec une liste chainées). Appelons la 'GroupNode'.
* des classes dérivant de 'Node' qui représentent les feuilles de ton graphe et qui -elles- sont capables de se dessiner: une BitmapNode, TextNode, RectangleNode, LineNode, ... ; chacune a en plus des propriétés communes (position, taille, ...) des paramètres spécifiques (url de l'image, couleur du rectangle, ...).
Voilà pour la partie 'graphe de scène' ; tu poruras ensuite raffiner:
- avec des objets graphiques plus complexes, par exemple une classe 'Button' qui dérive de GroupNode et qui est composé d'une image de fond (le bouton), et d'une texte par au dessus (le label).
- avec la gestion des animations et des événements dans le temps.
- etc...
Mais comme le dit p3ga5e, il est plus qu'opportun de s'inspirer des libs existantes, de s'en faire deux ou trois pour comprendre leur fonctionnement et leur architecture interne. Ca permet non seulement de voir ce qui est commun à toutes les libs et ce qui peut être amené à varier en fonction des sensibilités propres et des besoins de leurs auteurs.
Perso si tu connais un peu Java, je te conseille de jeter un oeil du côté de la façon dont est organisée la lib PulpCore que j'ai un peu pratiqué et dont je me suis directement inspiré pour ma propre lib JS. Pour moi, elle a une approche extrêmement intéressante pour abstraire la notion de 'propriété' associées aux sprites (couleur, alpha, position, visibilité, ...) pour ensuite pouvoir les réutiliser de façon très élégante et générique dans les animations.
My 2 cents.
Merci pour toutes ces informations.
Pour l'instant, j'ai créé 3 classes :
- WebGL_Node qui va plutôt gérer tout ce qui est arbre. J'ai pensé que dans un noeud, un a un objet et une transformation( 1 matrice). Chaque noeud possède 1 identifiant.
Au début, je pensais à grouper Node et GroupNode mais je ne sais pas si c'est une bonne idée. De plus, est ce que seul les feuilles sont capable de dessiner ? Si c'est le cas ma structure ne va pas.
- WebGL_Context qui serait la classe singleton dont tu parles. Elle spécifie la taille du canvas et le navigateur a utilisé ainsi que le contexte selon le navigateur
- WebGL_Object qui implémenterait les fonctionnalités de base. Construire un cube, une sphère ...
Je ne comprend pas trop l'utilisation de Base.js. En effet,dans les classes par prototypage, toutes les méthodes sont séparées et forment plusieurs blocks alors que dans l'autre méthode tout est réunit dans un seul block.
Du coup, cela serait plus lourd dans des appels de fonction de toujours appeler toute la classe alors que avec prototype on appele juste la méthode de la classe qui nous intéresse non ?
C'est sujet à débat ; pour moi le groupe et les feuilles sont des fonctionalités séparées. Mais ça peut se discuter.
C'est juste que les blocs distincts sont passés à Base.js au moment de la définition de la classe en les regroupant dans une collection de blocs pour ne faire qu'un appel à 'extend' pour que le code soit plus joli. Ca n'empêche que le résultat est le même.toutes les méthodes sont séparées et forment plusieurs blocks alors que dans l'autre méthode tout est réunit dans un seul block
Non, c'est juste une couche syntaxique ; au niveau perfs pour les accès aux méthodes / fonctions c'est totalement équivalent.Du coup, cela serait plus lourd dans des appels de fonction de toujours appeler toute la classe alors que avec prototype on appele juste la méthode de la classe qui nous intéresse non ?
Il y a (peut-être) juste une légère perte pour deux opérations beaucoup moins récurrentes: l'instanciation (new) et la conso mémoire ; à vérifier mais pour moi c'est plutôt négligeable,alors que je suis pourtant dans un environnement extrêmement contraint (un MIPS à 200MHz et quelques dizaines de Mo de RAM pour faire tourner l'ensemble du système).
Après, comme dit précédemment je partage juste cette lib qui m'a plu parce qu'elle correspondait bien à mon approche du JS: une grosse expérience en C++ et Java et très peu en JS. Mais c'est pas pour ça que tu dois te sentir obligé de l'utiliser (et ce n'est sûrement pas la seule qui existe non plus).
J'ai fait la classe GroupNode. J'aimerai te demander ton avis, si c'est un "truc" comme ca qu'il faut que je fasse :
tu en penses quoi ?
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 /** * @author sylvain */ var WebGL_GroupNode = Base.extend({ //////////////////////////////////////////////////////////////////// // Constructeur de la classe Node // // // //////////////////////////////////////////////////////////////////// constructor : function() { this.children = []; }, //////////////////////////////////////////////////////////////////// // Méthode pour ajouter un objet à la liste chainée GroupNode // du node correspondant // Entrée : l'enfant // Sortie : le GroupNode mis à jour //////////////////////////////////////////////////////////////////// addChild : function(child) { var c = this.children.push(child); return c; }, //////////////////////////////////////////////////////////////////// // Méthode pour supprimer un élément d'enfant // // // //////////////////////////////////////////////////////////////////// removeChildren : function() { var children = this.children; if(children.length !=0) { this.children.length = 0; } }, removeChild : function(child) { for(var i = 0 ; i < this.children.length ; i++) { if(this.children[i]=== child) { this.children.splice(i,1); } } }, //////////////////////////////////////////////////////////////////// // Méthode pour supprimer un enfant // // // //////////////////////////////////////////////////////////////////// indexOfChild : function(id) { this.children.split(id,1); } });
Je ne suis pas tout a fait d’accord avec l’approche POO quant on travaille sur une platform JavaScript, car tous ces héritages et polymorphisme a un cout, sur la maintenance du code, la flexibilité et peut être aussi sur les performances.
De ce que j’ai constaté, en Javascript la reflexivité ne coute rien contrairement a des langages comme C#.n'est pas plus rapide que
Code : Sélectionner tout - Visualiser dans une fenêtre à part var v= obj.ValueIl existe une autre approche, en utilisant le prototypage pour construire un abre:
Code : Sélectionner tout - Visualiser dans une fenêtre à part var v= obj["Value"]
Cela a l’avantage d’ajouter n’importe quel type d’objet en tant que nœud ou feuille d’un arbre, même des objets WebGL :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 Object.prototype.AddAsChild = function(child) { if(this.Children==undefined) this.Children = [ child ]; else this.Children.push(child); child.Parent = this; };
Ensuite il est intéressant de pouvoir déterminer si un objet implémente une interface, pour cela pas besoin de d’une mécanique d’héritage lourde, la réflexivité est largement suffisante :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 var root = "Hello !"; var child = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; root.AddAsChild(child); alert(child.Parent) // "Hello !"
Voila c’été juste pour dire que l’on se passe tres bien d’une approche POO en JavaScript.
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 /*** * return true if this object implements the interface */ Object.prototype.Implements = function(interface) { for(var property in interface) { if( typeof interface[property] != "string") continue; if(this[property]==undefined || typeof this[property] != interface[property] ) return false; } return true; }; /** * Interface IRender */ var IRender = { Matrix : "object", Render : "function" }; var Shape = { Name : "House", Matrix : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], }; alert(Shape.Implements(IRender));// false : il manque la methode Render! Shape.Render = function() { alert('Rendering ' + this.Name); }; alert(Shape.Implements(IRender)); // true
Nouknouk: pourquoi deux singletons?
Je suis embêté là.
Quelle est la meilleure méthode d'implémentation en Javascript ?
Voici une autre idée d'implémentation en utilisant Prototype.js
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 /** * @author sylvain */ //////////////////////////////////////////////////////////////////// // Constructeur de la classe Node // // // //////////////////////////////////////////////////////////////////// var WebGL_GroupNode = Class.create( WebGL_Node, { //////////////////////////////////////////////////////////////////// // Méthode pour ajouter un objet à la liste chainée GroupNode // du node correspondant // Entrée : l'enfant // Sortie : le GroupNode mis à jour //////////////////////////////////////////////////////////////////// addChild : function(child) { var c = this.children.push(child); return c; }, //////////////////////////////////////////////////////////////////// // Méthode pour supprimer un élément d'enfant // // // //////////////////////////////////////////////////////////////////// removeChildren : function() { var children = this.children; if(children.length != 0) { this.children.length = 0; } }, removeChild : function(child) { for(var i = 0 ; i < this.children.length ; i++) { if(this.children[i]=== child) { this.children.splice(i,1); } } }, //////////////////////////////////////////////////////////////////// // Méthode pour supprimer un enfant // // // //////////////////////////////////////////////////////////////////// indexOfChild : function(id) { this.children.split(id,1); } });
Tout comme devoir prendre du temps pour apprendre une nouvelle syntaxe, approche, façon de faire, etc...tous ces héritages et polymorphisme a un cout, sur la maintenance du code, la flexibilité et peut être aussi sur les performances.
Question perfs, ça n'a pas réellement d'incidence car les appels aux fonctions une fois l'objet créé sont totalement identiques en interne. A nouveau, la seule différence concerne la surcharge uniquement au moment de l'instanciation. Donc quand tu fais ton 'new MonObjet(params)'. Rien d'autre. Base ne fait que fournir une surcouche pour transformer le code écrit sous 'sa syntaxe' vers le Javascript 'normal'.
Quant à l'approche POO non flexible et maintenable, je te laisse la responsabilité de ces allégations.
On dévie du débat initial et j'ai peur qu'on pourrisse le topic de ce pauvre Sylvain.
Pour conclure là-dessus en un mot comme en 100: je n'ai jamais dit que c'était la solution idéale et encore moins universelle. Juste qu'elle pouvait éventuellement convenir pour qui est habitué à une approche POO (par exemple du fait d'une pratique antérieure de Java ou C++). Rien de plus.
Pourtant l'objet Array de Javascript possède une méthode slice avec en paramètre l'identifiant et le nombre de cases à supprimer pour éviter les trous.
En fait depuis tout à l'heure je cherche à faire une liste chainée en Javascript mais comme il n'y a pas de pointeurs je ne vois pas trop comment faire. Je cherchais une alternative.
La librairie osg de Cédric Pinson et o3d n'utilisent pas de liste chainée mais après je ne sais pas si les algorithmes sont efficace.
C'est pas parce qu'une méthode toute faite existe qu'elle rend l'utilisation des tableaux la solution la plus efficace.
Un code brut de mon crû ; probablement très loin d'être parfait, mais ça te donne une idée:En fait depuis tout à l'heure je cherche à faire une liste chainée en Javascript mais comme il n'y a pas de pointeurs je ne vois pas trop comment faire. Je cherchais une alternative.
Et son emploi:
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 ListImpl = { constructor: function(itPrevNextPrefix) { this.prefix = itPrevNextPrefix; this.prefixNext = this.prefix+"_next"; this.prefixPrev = this.prefix+"_prev"; this.first = null; this.last = null; }, append: function(obj) { if (obj[this.prefixNext] || obj[this.prefixPrev]) { return null; } obj[this.prefixNext] = null; obj[this.prefixPrev] = this.last; if (this.last != null) { this.last[this.prefixNext] = obj; obj[this.prefixPrev] = this.last; } if (this.first == null) { this.first = obj; } this.last = obj; return obj; }, prepend: function(obj) { if (obj[this.prefixNext] || obj[this.prefixPrev]) { return null; } obj[this.prefixPrev] = null; obj[this.prefixNext] = this.first; if (this.first != null) { this.first[this.prefixPrev] = obj; } if (this.last == null) { this.last = obj; } this.first = obj; return obj; }, remove: function(obj) { if (obj[this.prefixNext] != null) { obj[this.prefixNext][this.prefixPrev] = obj[this.prefixPrev]; } else { this.last = obj[this.prefixPrev]; } if (obj[this.prefixPrev] != null) { obj[this.prefixPrev][this.prefixNext] = obj[this.prefixNext]; } else { this.first = obj[this.prefixNext]; } obj[this.prefixPrev] = null; obj[this.prefixNext] = null; return obj; }, clear: function() { while ( ! this.isEmpty()) { this.takeFirst(); } }, takeFirst: function() { if (this.first == null) { return null; } else if (this.first[this.prefixNext] == null) { var obj = this.first; this.first = null; this.last = null; return obj; } else { var obj = this.first; obj[this.prefixNext][this.prefixPrev] = null; this.first = obj[this.prefixNext]; obj[this.prefixNext] = null; return obj; } }, takeLast: function() { if (this.last == null) { return null; } else if (this.last[this.prefixPrev] == null) { var obj = this.last; this.first = null; this.last = null; return obj; } else { var obj = this.last; obj[this.prefixPrev][this.prefixNext] = null; this.last = obj[this.prefixPrev]; obj[this.prefixPrev] = null; return obj; } }, size: function(obj) { var i = 0; var it = this.first; while (it != null) { i++; it = it[this.prefixNext]; } return i; }, contains: function(obj) { var it = this.first; while (it != null) { if (it == obj) { return true; } it = it[this.prefixNext]; } return false; }, isEmpty: function(obj) { return (this.first == null); }, previous: function(obj) { return obj[this.prefixPrev]; }, next: function(obj) { return (obj) ? obj[this.prefixNext] : this.first; }, }; List = Base.extend(ListImpl);
"nomListe" permet d'avoir des objets qui appartiennent à plusieurs listes en même temps. Chaque liste doit avoir un nom unique.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 var l = new List("nomListe"); l.append("monObjet"); l.prepend("monAutreObjet"); for (var it = l.first ; it != null ; it = l.next(it)) { alert(it); }
oui il est bien ton algo je comprend le principe.
Il y a un truc qui me gène.
Que veut dire cette ligne ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part obj[this.prefixNext][this.prefixPrev] = obj[this.prefixPrev];
si l'ordre n'est pas important un array est beaucoup plus efficace qu'une liste chainée.
Pour effacer au milieu, il suffit dans ce cas d'echanger l'objet a enlever avec celui a la fin, puis de retirer la fin.
sinon, nouknouk, pourquoi des singletons?
- "this.prefixNext" est une chaine de caractère qui représente le nom de l'attribut de obj qui contient une référence vers l'objet suivant.
- même chose pour "this.prefixPrev" sauf qu'il est le nom de l'attribut qui pointe sur l'élément précédent.
Est donc équivalent à quelque chose comme:
Code : Sélectionner tout - Visualiser dans une fenêtre à part obj[this.prefixNext][this.prefixPrev] = obj[this.prefixPrev];
Sauf que dans mon implémentation les noms des attributs 'suivant' et 'precedent' sont pas fixes, mais dynmaiques et choisi de façon unique pour chaque liste (le fameux "nomListe" du post précédent) ; cela permet que deux listes qui contiennent un même objet aillent pas trifouiller dans les 'suivant' et 'precedent' de l'autre.
Code : Sélectionner tout - Visualiser dans une fenêtre à part obj.suivant.precedent = objet.precedent
Mais l'ordre est important ici.
On ne peut pas afficher un objet dans la scène avant d'avoir fait les bonnes transformations.
C'est une implémentation d'une liste chaînée ; l'ordre est respecté (et heureusement: je me sers de ces itérateurs pour des listes d'objets 2D dans les GroupNodes pour lesquels l'ordre d'affichage est tout aussi important).
Concrètement c'est une partie de l'opération de suppression d'un élément d'une liste (méthode remove): (voir image en pj)
La suppression consiste à mettre à jour ce qui est en rouge dans le schéma. Il y a deux pointeurs à mettre à jour et la ligne de code que tu as donnée correspond à la moitié du boulot, ie. la flêche rouge qui part de 8 et va vers 3.
Concrètement, cette ligne dit: obj étant 15, il dit à 8 que son nouveau précédent n'est plus obj (15), mais le précédent de obj, à savoir 3.
Désolé d'avance pour le hors sujet ^^
pour moi prendre le temps d'apprendre les spécificités d'un language n'est pas une perte de temps mais un investissement !Tout comme devoir prendre du temps pour apprendre une nouvelle syntaxe, approche, façon de faire, etc...
C'est juste que les developpeurs semble sous-estimer la liberté qu'offre le faite de s'affranchir de la declaration des meta-données (je parle de notion de class).
je rencontre ce probleme avec tous mes stagiaires Développeurs, ils semblent formatés pour appliquer des design-paterns apprit par coeurs de maniere mecanique ... c'est juste que cela me désole ...
Pour en revenir au sujet de la discutions , je tient a préciser que le type tableau en javascript est un gros FAKE !
en realité un tableau est un objet javascript comme un autre !
pour preuveen realité un type Array est une specialisation d'object :
Code : Sélectionner tout - Visualiser dans une fenêtre à part typeof [] == "object" // true
il n'existe aucune difference entre les indices d'un tableau et les proprietés d'un objet !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 var tab = [ 0 ,1, 2, 3]; for(var property in tab) { alert(property == tab[property]); }
C'est pourquoi j'insiste sur le fait d'apprendre les notions d'un langage, un tableau en javascript ne permet aucune optimisation !
ne croyez pas alligner vos données en memoire en utilisant un tableau en Javascript.
tous fois l'approche liste chainées sur les fils d'un noeud peut apporter un gain de performance,car un parcour itératif semble plus performant qu'un parcours recursif en javasprtipt ( cela est difficelement mesurable ) !
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager