Dorian Fevrier's blog - Mot-clé - développementJe m’appelle FEVRIER Dorian, je suis infographiste 3D passionné par mon métier, l’informatique en général, l’internet, la programmation et l’évolution de tout ce petit monde. Vous trouverez sur ce blog des tutoriaux, mes coups de cœurs, avis, etc.2024-01-02T23:48:05+01:00FEVRIER Dorianurn:md5:695d9c73474c33ce3dab043823509c4bDotclearProjeter un mesh sur un autre avec l'API Python de Mayaurn:md5:9653dcaa90d06317c1058f502c5c745c2011-02-20T23:56:00+01:002013-07-26T18:11:56+02:00NarannScript et code3dapiclosestIntersectioncodedéveloppementfrintersectionmayamelmeshnodepythonscripttuto<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_tn.png" alt="projection_mesh_api_tn.png" style="float:left; margin: 0 1em 1em 0;" title="projection_mesh_api_tn.png, fév. 2011" height="150" width="150" />Aujourd'hui je vous propose un tutorial qui vous permettra de projeter un mesh sur un autre.</p>
<p>C'est un truc qui peut être pratique mais surtout, c'est assez "fun" à faire (tout est relatif diront certains :seSentCon: ) et ça permet d'apprendre les fondamentales des changements de repères (les fameuses matrices) en ayant un exemple à la fois simple et concret.</p>
<p>Amateur de l'API, ce tuto est fait pour vous!</p> <center>:longBar:</center>
<p>Voici ce que nous souhaitons obtenir:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_015.png" alt="projection_mesh_api_015.png" style="display:block; margin:0 auto;" title="projection_mesh_api_015.png, fév. 2011" height="378" width="511" /></p>
<p>Un mesh projeté sur un autre.</p>
<p>Et la scène à partir de laquelle nous partons:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<center>:longBar:</center>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#theorie">Théorie</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#codeDeBase">Code de base</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#preparationDesAttributs">Préparation des attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#preparerSaScene">Préparer sa scene</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#laMethodeCompute">La méthode compute</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#lesMatricesExpliqueesAuxGraphistes">Les matrices expliquées aux graphistes</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#revenonsAuCode">Revenons au code!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#parcourir_et_modifier_chaque_vertex">Parcourir et modifier chaque vertex</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#construire_un_mesh">Construire un mesh</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#conclusion_et_code_source">Conclusion et code source</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#la_methode_sans_python">Mise à jour: La méthode sans Python</a></li>
</ul>
<center>:longBar:</center>
<h5>Théorie <a name="theorie"></a></h5>
<p>Une fois de plus on commence par de la théorie.</p>
<p>Dans les faits, vous allez voir que c'est assez simple car le plus dur (la partie intersection) sera géré via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">un appel à l'API Maya</a>:</p>
<pre class="python python"><span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span>.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span> ... <span style="color: black;">)</span></pre>
<p>Cette <a href="http://fr.wikipedia.org/wiki/Méthode_(informatique)">méthode</a> prend en charge l'intersection d'un rayon (point+direction) sur un mesh et renvoi quelques infos (dont la plus importante: La position du point projeté).</p>
<p>En gros, il nous faut trois choses:</p>
<ul>
<li>Un point d'origine (celui qu'on souhaite projeter).</li>
<li>Une direction (un vecteur).</li>
<li>Un mesh de destination.</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_002.png" title="projection_mesh_api_002.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_002_m.jpg" alt="projection_mesh_api_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_002.png, fév. 2011" height="410" width="560" /></a></p>
<p>Le point d'origine sera bien entendu chaque vertex du mesh à projeter (ici, les numéros des vertex étant en jaune).</p>
<p>La direction sera la normale du vertex à projeter (en vert sur l'image. D'accord on voit pas trop mais mettez y un peu de bonne volonté que diable! :cayMal: ).</p>
<p>Et le mesh de destination sera bien évidemment le mesh qui recevra le plan (dans notre cas, une sphere).</p>
<p>On récupère donc, à chaque fois, un point et une normale (les croix jaunes).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_003.png" title="projection_mesh_api_003.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_003_m.jpg" alt="projection_mesh_api_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_003.png, fév. 2011" height="423" width="560" /></a></p>
<center><i>Pardonnez mon shéma à dix-francs-six-sous-convertion-jpeg-moisie :pasClasse:</i></center>
<center>:longBar:</center>
<h5>Code de base <a name="codeDeBase"></a></h5>
<p>Voici les bases du code:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span>
<span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>Si vous avez déjà écrit un node Maya en Python, ce code ne doit pas vous faire trop peur.</p>
<p>J'explique vite fait pour les <del>boulets</del> autres :baffed: .</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span></pre>
<p>Import des principaux modules.</p>
<ul>
<li>Le module <em>sys</em> sert à créer les messages d'erreur lors du chargement/déchargement du plugin (voir plus loin).</li>
<li>Les deux autres modules servent à appeler les méthodes de l'API Maya.</li>
</ul>
<p>Rien de bien compliqué.</p>
<pre class="python python">kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span></pre>
<ul>
<li><em>kPluginNodeTypeName</em> est une simple variable appelé plus loin pour donner un nom à notre type de node.</li>
<li><em>kPluginNodeId</em> est une valeur qui sert d'identifiant pour le node quand il est écrit dans dans les fichiers binaires (mb). 0x80000 à 0xfffff sont utilisé pour les examples Maya. Voir <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">la documentation</a> pour plus d'informations.</li>
</ul>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Ici, la classe est une instance de l'objet <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html">MPxNode</a> qui est lui même une classe faite pour créer des nodes personnalisé (c'est une partie assez complexe que je n'aborderai pas dans ce tuto tant elle mérite un billet à part entière :jdicajdirien: ).</p>
<p>La méthode <em>__init__</em> est la première méthode lancée lors de la création de la classe. Elle initialise simplement la classe <em>MPxNode</em>.</p>
<p>La méthode <em>compute</em> est la méthode dans laquelle nous allons le plus travailler. C'est une méthode hérité de <em>MPxNode</em>. La partie du code "qui fait quelque chose". :sourit:</p>
<p>Si on ne connaît pas trop Python et les classes, cette partie du code peut sembler complexe mais ne vous inquiétez pas, c'est toujours la même qu'on utilise. La seule partie importante, c'est la méthode <em>compute</em>.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span></pre>
<p>Une fonction lancée au moment de l'initialisation du plugin qui renvoi un pointeur (si si) vers la classe (et donc le node) crée.</p>
<p>Python n'ayant pas de notions de pointeur et Maya en ayant besoin, notamment, pour initialiser ces plugins, Autodesk a créé la méthode OpenMayaMPx.asMPxPtr (rechercher "asMPxPtr" dans l'aide Maya et prendre le premier résultat pour une explication plus précise).</p>
<p>Une fois de plus, c'est quelque chose de basique, on le met, on réfléchie pas :bete: .</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span></pre>
<p>Cette méthode est elle aussi appelé lors de la création d'un node et permet (entre autres) d'initialiser les attributs du node. Ce sera la première que nous remplirons. Pour l'instant, elle en fait rien.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>La je vais vous dire, c'est vraiment un "copier-coller" que je fais depuis les exemples fournit. En gros ces fonctions sont appelé lors du chargement/déchargement des plugins. Elles servent à "enregistrer"/"radier" les plugins des sessions Maya.</p>
<p>Leur comportement est simple, je vous invite à analyser ce bout de code par vous même (ça fait pas de mal! :gniarkgniark: ).</p>
<p>A ce stade, vous devriez pouvoir créer votre node python en le chargeant dans Maya:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_004.png" alt="projection_mesh_api_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_004.png, fév. 2011" height="324" width="387" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_005.png" alt="projection_mesh_api_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_005.png, fév. 2011" height="465" width="476" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_006.png" alt="projection_mesh_api_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_006.png, fév. 2011" height="84" width="195" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_007.png" alt="projection_mesh_api_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_007.png, fév. 2011" height="75" width="433" /></p>
<p>Et en le créant comme suis:</p>
<pre class="mel mel">createNode projectMesh<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// Result: projectMesh1 //</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_008.png" alt="projection_mesh_api_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_008.png, fév. 2011" height="171" width="474" /></p>
<p>Dans la mesure ou il n'y a aucun attribut, ce node ne fait absolument rien! :sourit:</p>
<center>:longBar:</center>
<h5>Préparation des attributs <a name="preparationDesAttributs"></a></h5>
<p>Comme promis on va commencer par la méthode <em>nodeInitializer()</em> qui initialiser les attributs du node.</p>
<p>Nous allons avoir besoin de trois attributs:</p>
<ul>
<li>Deux en entrée (input): Le mesh qui projette ses vertex et celui qui les reçoit.</li>
<li>Un en sortie (output): Le mesh de sortie, le mesh projeté.</li>
</ul>
<p>C'est partie! :grenadelauncher:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>La première ligne:</p>
<pre class="python python">typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Crée un "objet" (appelé dans l'API Maya: "<a href="http://download.autodesk.com/us/maya/2009help/API/group___m_fn.html">Function Set</a>") qui va nous servir à manipuler les attributs (les créer surtout):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span></pre>
<p>On créé les attributs. Rien de bien compliqué (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_typed_attribute.html#fc105a88372e84910bc56c74c973bc26">voir doc</a>).</p>
<p>Quelques précisions sur les arguments utilisés:</p>
<ul>
<li>Le nom entier de l'attribut (long name).</li>
<li>Le nom court de l'attribut (short name).</li>
<li>Le "type" (au sens <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">API type</a>) de l'attribut.</li>
</ul>
<p>Les méthodes qui suivent chaque déclaration d'attribut (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#ced78c59e2a6b0395810705bb2896bfb">setReadable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#5b89d32b1d6beebfa7b21a58d1d8d6f2">setWritable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#b0fe1b4159b4f4bcefd7c0cef9a7b389">setStorable</a>) rajoutent des particularitées au dernier attribut créé (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html">voir la doc</a> pour plus de précisions).</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>Comme le commentaire l'indique, cette partie ajoute/connecte les attributs créés plus haut au node.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>Cette partie est très importante! :papi:</p>
<p>Elle permet de définir des "dépendances" entre les attributs.</p>
<p>Dans notre cas:</p>
<ul>
<li>Si l'attribut <em>inputMeshSrc</em> change, l'attribut <em>outputMesh</em> changera aussi.</li>
<li>Si l'attribut <em>inputMeshTarget</em> change, l'attribut <em>outputMesh</em> changera aussi.</li>
</ul>
<p>Si ces lignes ne sont pas mises, la méthode <em>compute</em> du node que nous sommes en train de créer ne sera jamais lancé. Le node ne sera donc jamais mis à jour.</p>
<p>Vous pouvez télécharger le node python en l'état actuel ici:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh001.7z">>> projectMesh001.7z <<</a></p>
<center>:longBar:</center>
<h5>Préparer sa scene <a name="preparerSaScene"></a></h5>
<p>Avant de réellement coder le comportement du node, il nous faut de la géométrie déjà présentes dans la scène à laquelle connecter notre futur node.</p>
<p>Créer une scène qui ressemble à ça:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<p>Créez aussi un troisième mesh, celui qui renverra la géométrie de notre futur node (qui sera le mesh projeté).</p>
<p>Dans mon cas: Une pSphere. Mais ça peut être n'importe quoi.</p>
<p>Tant que c'est un node de mesh. Vous pouvez même créer le node de mesh à la main.</p>
<p>Placez le au centre de la scène (0,0,0) pour qu'il n'y ai pas de décalage entre le mesh projeté et sa cible.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_009.png" alt="projection_mesh_api_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_009.png, fév. 2011" height="549" width="733" /></p>
<p>Pour éviter d'avoir à créer les connections à chaque fois, voici deux petits codes MEL qui permettent de tester votre node (il faut que vos nodes soit bien nommés).</p>
<p>Pour charger le tout:</p>
<pre class="mel mel">loadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"monRepertoire/projectMesh.py"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
createNode projectMesh<span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f projectMesh1.<span style="color: #202020;">outputMesh</span> pSphereShape2.<span style="color: #202020;">inMesh</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pPlaneShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshSrc</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pSphereShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshTarget</span><span style="color: #339933;">;</span></pre>
<p>Et pour tout décharger:</p>
<pre class="mel mel">delete projectMesh1<span style="color: #339933;">;</span>
flushUndo<span style="color: #339933;">;</span>
unloadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"projectMesh"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>Et voila le travail! Maintenant prenez une grosse inspiration, on saute!</p>
<center>:longBar:</center>
<h5>La méthode compute <a name="laMethodeCompute"></a></h5>
<p>La première chose à tester est la présence d'une connexion sur votre attribut outputMesh. En effet, si votre node n'est connecté à rien, il ne faut pas qu'il se calcule:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kUnknownParameter</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Une fois qu'on est sûr que les connections sont bonnes, on récupère les attributs d'entrés:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #808080; font-style: italic;"># get the inputMeshTarget (return MDataHandle)</span>
inMeshSrcHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
inMeshTargetHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span></pre>
<p>Ceci fait une "connection" vers le bloc de donnée des attributs. C'est la première étape pour récupérer la valeur (ici c'est un kMesh donc ce sera un peu différent) d'un attribut.</p>
<blockquote><p>Python étant un langage non typé (A la fois sa principale qualité mais aussi son principal défaut...), j'ai tendance à écrire en commentaire le type des données de l'API Maya que je récupère.</p>
<p>
Sinon, on a (très) vite fait de ne plus savoir du tout quelle variable correspond à quel type (surtout que des types dans l'API Maya, c'est pas ça qui manque :aupoil: ).</p>
<p>
Après, chacun sa méthode! Si vous avez un cortex sur développé, que vous voulez vous la jouer "Reunabranlé moi j'type queudal", qu'un code que vous êtes le seul à pouvoir lire vous met le kiki tout dur et que vous voulez justifier votre BAC+5 (par les temps qui courent ce n'est sûrement pas votre salaire qui doit s'en charger). N'hésitez pas, codez comme un <del>TD</del> porc: Pas de commentaires, des variables à une lettre et autres joyeusetés du genre... Vos collègues vous le rendront bien. :sourit:</p>
<p>
Mais si vous êtes plus modeste et souhaitez apprendre rapidement l'API Maya, je vous recommande vivement d'écrire les types de l'API Maya via des commentaires, directement dans votre code. En plus d'être plus clair, ça oblige à toujours savoir/chercher, quand on écrit des variables, à quel type elle correspond.</p></blockquote>
<p>Après ça, nous vérifions que nos deux attributs connectés sont bien des meshs:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>Et nous les récupèrons en tant que tel:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #808080; font-style: italic;"># return a MObject</span>
meshSrc = inMeshSrcHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshTarget = inMeshTargetHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>Je vous invite à regarder la doc de <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> histoire de voir ce qu'on peut récupérer d'un attribut.</p>
<p>Comme précisé dans le commentaire, on récupère un <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_object.html">MObject</a>. Ce type d'objet un peu "le type à tout faire" dans Maya.</p>
<p>Ce MObject n'est qu'un objet de transition. En effet, il est rarement utilisé directement.</p>
<p>Dans Maya, pour modifier/manipuler des objets, on passe souvent par des <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_base.html">"Function Set"</a>. Ils ont la forme: MFn*Type*.</p>
<p>Ici, pour manipuler les mesh, on va récupérer un function set de mesh: <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a></p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get the MFnMesh of the twice attr</span>
mFnMeshSrc = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshSrc <span style="color: black;">)</span>
mFnMeshTarget = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshTarget <span style="color: black;">)</span></pre>
<p>Ce qui nous intéresse maintenant c'est d'avoir une liste de tout les vertex du "mesh source" (celui qui va être projeté sur le "mesh cible") afin de créer un autre tableau de vertex qui contiendra leurs positions modifié:</p>
<pre class="python python">outMeshMPointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># create an array of vertex wich will contain the outputMesh vertex</span>
mFnMeshSrc.<span style="color: black;">getPoints</span><span style="color: black;">(</span>outMeshMPointArray<span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># get the point in the space</span></pre>
<p>La première ligne créée un tableau de type <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point_array.html">MPointArray</a>.</p>
<p>La seconde ligne le remplit avec les valeurs des vertex du "mesh source".</p>
<p>La façon de l'écrire est un peu déroutante ("à l'envers" diront certains :reflechi: ) mais c'est comme ça que fonctionne <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#54dc2a4415a365c7193936dad6643b47">getPoint</a> comme pas mal d'autres fonctions de l'API Maya.</p>
<p>Plutôt que de renvoyer le résultat, il est stocké dans la variable fournit en argument.</p>
<p>Nous avons maintenant un MPointArray remplit de vertex avec leurs positions.</p>
<p>L'idée est maintenant de modifier la position de ces vertex afin qu'elles correspondent à la position projetée sur le "mesh cible".</p>
<p>Mais voila... Les positions des vertex que vous avez récupéré dans votre MPointArray sont en "object space". C'est-à-dire, relatif au centre de l'objet.</p>
<p>Nous allons nous heurter à un vrai problème. The big one! The ultimate: The matrices! *Voix qui résonne* :enerve:</p>
<center>:longBar:</center>
<h5>Les matrices expliquées aux graphistes <a name="lesMatricesExpliqueesAuxGraphistes"></a></h5>
<p>Quand on est graphistes, on en entend des fois parler sans trop savoir ce que c'est :bete: .</p>
<p>Ajoutez à ça que ce qu'on trouve sur le net est très scolaire et "trop mathématique" (On montre comment multiplier une matrice sans expliquer pourquoi on est amené à le faire).</p>
<p>Tout ça au point qu'on ne voit pas forcément le lien avec notre boulot.</p>
<p>Je vais modestement tenter d'expliquer ça d'un point de vue "graphiste" :mayaProf: .</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/photoMatrix.jpg" title="photoMatrix.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.photoMatrix_m.jpg" alt="photoMatrix.jpg" style="display:block; margin:0 auto;" title="photoMatrix.jpg, fév. 2011" height="416" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/trinity-of-one/20562069/">Photo</a> par <a href="http://www.flickr.com/photos/trinity-of-one/">My Melting Brain</a> sous licence Créative <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/deed.fr">by-nc-sa</a>. Merci à lui! :sourit:</i></small></center>
<p>Créez un cube.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_010.png" alt="projection_mesh_api_010.png" style="display:block; margin:0 auto;" title="projection_mesh_api_010.png, fév. 2011" height="284" width="463" /></p>
<p>Vous avez sûrement remarqué, une fois votre cube créé, qu'il a un "point de pivot" avec des informations (position, rotation, échelle, etc...). Et bien ce point permet de faire un "lien mathématique" entre les points de vertex de votre cube et "le monde" (les coordonnées centrales du "monde" sont 0,0,0).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_011.png" alt="projection_mesh_api_011.png" style="display:block; margin:0 auto;" title="projection_mesh_api_011.png, fév. 2011" height="291" width="404" /></p>
<p>Dans l'idée, si ce node (le node de "transform") n'existait pas, votre objet serait au centre de la scène. Et pour le déplacer il faudrait déplacer les positions de tout les vertex du cube.</p>
<p>Le node de transform agit un peu comme un "parent" des vertex de votre cube. De cette façon, les positions des vertex du cube ne bouge pas. Par exemple, un vertex du cube placé à 1,1,1 (par rapport au pivot du cube donc) restera à 1,1,1 quel que soit la position du transform.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_012.png" alt="projection_mesh_api_012.png" style="display:block; margin:0 auto;" title="projection_mesh_api_012.png, fév. 2011" height="246" width="305" /></p>
<center><i>Ici, on déplace le pivot, pas les vertex du cube qui eux, ne change pas de place par rapport au pivot, c'est le pivot qui change de place par rapport au monde.</i></center>
<p>Mais pour pouvoir faire certaines opérations (dans notre cas, savoir si le vertex est dirigé vers un autre mesh), il faut que les coordonnées de toute les entités qui entrent en jeu soient sur un repère commun, le repère monde.</p>
<p>Démonstration en bédé:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_001.png" alt="projection_mesh_api_bd_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_001.png, fév. 2011" height="340" width="553" /></p>
<center><i>Deux vertex placés à différents endroits dans l'espace monde...</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_002.png" alt="projection_mesh_api_bd_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_002.png, fév. 2011" height="340" width="553" /></p>
<center><i>...mais au même endroit par rapport à leurs centres respectifs.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_003.png" alt="projection_mesh_api_bd_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_003.png, fév. 2011" height="340" width="553" /></p>
<center><i>Pas pratique pour faire des calcules.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_004.png" alt="projection_mesh_api_bd_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_004.png, fév. 2011" height="340" width="553" /></p>
<p>Alors que si on choisi, comme point de repère commun, le centre du monde.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_005.png" alt="projection_mesh_api_bd_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_005.png, fév. 2011" height="340" width="553" /></p>
<p>C'est beaucoup plus facile.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_006.png" alt="projection_mesh_api_bd_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_006.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_007.png" alt="projection_mesh_api_bd_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_007.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_008.png" alt="projection_mesh_api_bd_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_008.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_009.png" alt="projection_mesh_api_bd_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_009.png, fév. 2011" height="340" width="553" /></p>
<p>Bon, maintenant qu'on connaît les coordonnées des vertex dans leurs "espaces objets", il faut savoir comment les récupérer en "espace monde".</p>
<p>Le principe de base qui vient tout de suite à l'esprit est: On additionne les positions (relatives à l'objet) des vertex à la position (relative au monde) de son point de pivot.</p>
<blockquote><p>Exemple:</p>
<p>
Si pVertex la position d'un vertex et positionDuCube la position, dans le monde, du pivot du cube:</p>
<p>
positionDuCubeX + pVertexDansLeCubeX = pVertexDansLeMondeX</p></blockquote>
<p>Mais vous vous en doutez surement, c'est plus compliqué... :siffle:</p>
<p>En effet, dans le cas des rotations et de l'échelle, il ne suffit pas de quelques additions pour résoudre le problème.</p>
<blockquote><p>Note: Que ce soit une transformation, une rotation, ou une mise à l'échelle d'un mesh. Tout se résume à un déplacement des vertex dans l'espace.</p></blockquote>
<p>La vérité est que tout ses "paramètres" peuvent être mis dans un seul et même "objet" que l'on appel une matrice. Cette matrice, va nous servir à faire des calcules (que Maya nous épargne mais si ça vous intéresse, voici un exemple de <a href="http://jeux.developpez.com/faq/math/?page=bases#Q4">calcule d'une matrice de rotation</a>) pour récupérer les positions des vertex dans l'espace monde.</p>
<p>Comme je disait, Maya nous donne très facilement accès à cet objet grâce à l'<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> (Il y a plusieurs types de matrices, nous nous focaliserons que sur celle là).</p>
<p>Du coup, à ce stade, nous avons deux choses:</p>
<ul>
<li>Les positions des vertex relativement à l'objet (en "object space").</li>
<li>Une matrice de l'objet (qui est relative au monde, en "world space").</li>
</ul>
<p>Il faut donc "convertir" les positions des vertex de "object space" vers "world space". On Parle d'un changement de repère. Vous obtenez donc une position dite absolue ("world space").</p>
<p>Et pour obtenir la position d'un vertex dans le "world space", il "suffit" de multiplier la matrice de position d'un vertex {x,y,z} par l'inclusive matrix de l'objet. (J'ai mis "suffit" entre guillemet car multiplier une matrice c'est pas aussi simple que faire 2x2... :redface: )</p>
<blockquote><p>Note: Je ne ferais pas de démonstration sur "comment calculer une matrice", le net regorgeant d'exemples et d'explications.</p></blockquote>
<p>Et voici la formule:</p>
<pre>
positionGlobale = positionLocale * inclusiveMatrix
</pre>
<p>C'est un peu comme le théorème de Pythagore. On s'en fout de comment ça marche, tant qu'on sait quand l'utiliser! :baffed:</p>
<p>Voila! J'espère que cette petite explication vous aura éclairé un peu sur le pourquoi des matrices. :sourit:</p>
<center>:longBar:</center>
<h5>Revenons au code! <a name="revenonsAuCode"></a></h5>
<p>Pour récupérer l'inclusive matrice, rien de plus simple.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get MDagPath of the MMesh to get the matrix and multiply vertex to it. If I don't do that, all combined mesh will go to the origin</span>
inMeshSrcMDagPath = mFnMeshSrc.<span style="color: black;">dagPath</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MDagPath object</span>
inMeshSrcInclusiveMMatrix = inMeshSrcMDagPath.<span style="color: black;">inclusiveMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MMatrix</span></pre>
<p>La première ligne permet de récupérer le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html">dagPath</a> du mesh source. On peut considérer le dagPath comme étant l'équivalent du node de transform d'un objet dans Maya. Là ou toutes les informations sur les transformations (positions, rotations, échelles, etc...) sont stocké.</p>
<p>La seconde ligne récupère l'<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> du mesh source sous forme d'une <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_matrix.html">MMatrix</a>.</p>
<p>Maintenant, nous allons pouvoir parcourir chaque vertex:</p>
<ul>
<li>Le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point.html#1fdf65c85cf27f6ce4ddef0a72aac5d3">multiplier par la matrice</a> afin d'avoir sa position dans le world space.</li>
<li>Récupérer sa normale.</li>
<li><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html#d22edbae3de3e0742534acff6b4f6227">La mutiplier</a> elle aussi par la matrice.</li>
<li>Récupérer le point de collision, le stocker à la place du point en court.</li>
</ul>
<center>:longBar:</center>
<h5>Parcourir et modifier chaque vertex <a name="parcourir_et_modifier_chaque_vertex"></a></h5>
<p>Le début de la boucle principale ressemble à ça:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">(</span> outMeshMPointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span> :
inMeshMPointTmp = outMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span> <span style="color: #66cc66;">*</span> inMeshSrcInclusiveMMatrix <span style="color: #808080; font-style: italic;"># the MPoint of the meshSrc in the worldspace</span></pre>
<p>La boucle est simple: "i" sera incrémenté de 1 à chaque "tour" pour parcourir le tableau de vertex (MPointArray).</p>
<p>La première chose qu'on fait est une multiplication du point (<em>outMeshMPointArray[i]</em>) par la matrice (<em>inMeshSrcInclusiveMMatrix</em>) pour obtenir un vertex (<em>inMesgPointTmp</em>) en world space (avec des coordonnées relatives au "monde").</p>
<p>Maintenant que nous avons (enfin) le vertex positionné par rapport au monde, on va "l'intersectionner" (si les gars de l'académie française voyait ça :pasClasse: ) avec le mesh cible.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/intersection.jpg" title="intersection.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.intersection_m.jpg" alt="intersection.jpg" style="display:block; margin:0 auto;" title="intersection.jpg, fév. 2011" height="420" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/mynamemattersnot/2470352700/">Photo</a> par <a href="http://www.flickr.com/photos/mynamemattersnot/">MyNameMattersNot</a> sous licence Créative <a href="http://creativecommons.org/licenses/by-sa/2.0/deed.fr">by-sa</a>. Merci à lui!</i></small></center>
<p>Bon, allez regarder les arguments de la méthode <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">OpenMaya.MFnMesh.closestIntersection()</a> que <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#theorie">nous avont vu plus haut</a>.</p>
<p>Vous voyez qu'il y en a pas mal. :sourit:</p>
<p>Rassurez vous, nous pouvons en faire sauter la plupart. Ce qui nous intéresse c'est le vertex d'origine, sa direction (dans notre cas: la normale) et le point de "collision" (le hitPoint).</p>
<p>Mais plus subtile encore, regardez le type du premier argument attendu par la méthode (le raySource).</p>
<p>C'est un MFloatPoint!</p>
<p>Mais comment convertir <em>inMeshMPointTmp</em> (un MPoint) en MFloatPoint? En C++ c'est assez facile, il faut passer par des doubles. Après moult recherches, j'ai trouvé une solution. Je vous la donne de but en blanc:</p>
<pre class="python python">raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> inMeshMPointTmp.<span style="color: black;">x</span>, inMeshMPointTmp.<span style="color: black;">y</span>, inMeshMPointTmp.<span style="color: black;">z</span> <span style="color: black;">)</span></pre>
<p>C'est pas si compliqué mais si tu le sais pas...</p>
<p>Nous avons donc notre raySource.</p>
<p>Maintenant la direction:</p>
<pre class="python python">rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MVector</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertexNormal</span><span style="color: black;">(</span> i, <span style="color: #008000;">False</span>, rayDirection<span style="color: black;">)</span>
rayDirection <span style="color: #66cc66;">*</span>= inMeshSrcInclusiveMMatrix
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>rayDirection.<span style="color: black;">x</span>, rayDirection.<span style="color: black;">y</span>, rayDirection.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>Arrivé ici vous devriez comprendre:</p>
<ul>
<li>On créé le MVector.</li>
<li>On y stock la normale du vertex courant ("i") du "mesh source".</li>
<li>On multiplie par la matrice pour avoir ce vecteur relatif à l'espace monde.</li>
<li>On le "convertie" en MFloatVector.</li>
</ul>
<p>Le hitPoint quand à lui est un simple MFloatPoint:</p>
<pre class="python python">hitPoint = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Et le reste des arguments sont les suivants:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># rest of the args</span>
hitFacePtr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MScriptUtil</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">asIntPtr</span><span style="color: black;">(</span><span style="color: black;">)</span>
idsSorted = <span style="color: #008000;">False</span>
testBothDirections = <span style="color: #008000;">False</span>
faceIds = <span style="color: #008000;">None</span>
triIds = <span style="color: #008000;">None</span>
accelParams = <span style="color: #008000;">None</span>
hitRayParam = <span style="color: #008000;">None</span>
hitTriangle = <span style="color: #008000;">None</span>
hitBary1 = <span style="color: #008000;">None</span>
hitBary2 = <span style="color: #008000;">None</span>
maxParamPtr = <span style="color: #ff4500;">99999999</span>
<span style="color: #808080; font-style: italic;"># http://zoomy.net/2009/07/31/fastidious-python-shrub/</span>
hit = mFnMeshTarget.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>,
maxParamPtr,
testBothDirections,
accelParams,
hitPoint,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span></pre>
<p>Un grand merci à <a href="http://zoomy.net/about/">Peter J. Richardson</a>! Sans <a href="http://zoomy.net/2009/07/31/fastidious-python-shrub/">son billet</a>, je n'aurais jamais réussi à coder ce truc. C'est pour ça qu'il faut "partager ce qu'on sait" sur internet! ;)</p>
<p>Une fois le <em>closestIntersection</em> appelé, vous récupérez un hitPoint en MFloatPoint que vous reconvertissez en MPoint:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> hit :
inMeshMPointTmp = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPoint</span><span style="color: black;">(</span> hitPoint.<span style="color: black;">x</span>, hitPoint.<span style="color: black;">y</span>, hitPoint.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>On remplace le point courant par notre nouveau point:</p>
<pre class="python python">outMeshMPointArray.<span style="color: #008000;">set</span><span style="color: black;">(</span> inMeshMPointTmp, i <span style="color: black;">)</span></pre>
<p>Et c'est la fin de la boucle! :D</p>
<p>Arrivé ici vous avez un MPointArray <em>outMeshMPointArray</em> avec les valeurs des vertex projetés sur le mesh cible.</p>
<p>Il faut donc maintenant reconstruire le mesh.</p>
<center>:longBar:</center>
<h5>Construire un mesh <a name="construire_un_mesh"></a></h5>
<p>Je ne vais pas rentrer précisément dans les détails sur "comment créer et agencer les variables dans le cas de la création d'un mesh.</p>
<p>En gros il nous faut:</p>
<ul>
<li>Le nombre de vertex.</li>
<li>Le nombre de polygones (polygones + triangles si il y en a).</li>
<li>Un tableau de point (Tout les vertex à la queue leu leu avec leurs coordonnées XYZ).</li>
<li>Un tableau listant le nombre de vertex par polygones (Exemple: 4,4,4,4,3,4,3,4,3,4,4,etc...)</li>
<li>Un tableau d'index des vertex (Exemple: 1,2,3,4,3,4,5,6,5,6,7,8, etc...)</li>
</ul>
<p>Vous l'aurez compris, on déjà le tableau des points (le troisième point). Pour le reste, on le récupère bêtement sur le mesh d'origine.</p>
<p>Commençons! :hehe:</p>
<p>Dans un premier temps il faut créer un function set <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh_data.html">MFnMeshData</a>.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># create a mesh that we will feed!</span>
newDataCreator = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMeshData</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Ce function set va nous permettre de créer un MObject que nous allons pouvoir "remplir" des données du futur mesh.</p>
<pre class="python python">newOutputData = newDataCreator.<span style="color: black;">create</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Return MObject</span></pre>
<p>Comme je vous l'ai dit plus haut: Il faut qu'on récupère toute les informations (hormis le tableau de vertex) du mesh source:</p>
<pre class="python python">outMeshNumVtx = mFnMeshSrc.<span style="color: black;">numVertices</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># outputMesh will have the same number of vtx and polygons</span>
outMeshNumPolygons = mFnMeshSrc.<span style="color: black;">numPolygons</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create two array and feed them</span>
outMeshPolygonCountArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
outMeshVtxArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertices</span><span style="color: black;">(</span>outMeshPolygonCountArray, outMeshVtxArray<span style="color: black;">)</span></pre>
<p><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#5f014e7c511cf86978a765a932ce04d0">mFnMeshSrc.getVertices()</a> remplit les deux tableaux avec les informations nécessaires à la création du mesh voir <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#construire_un_mesh">plus haut</a></p>
<p>Une fois que nous avons tout ça, nous créons le mesh:</p>
<pre class="python python">meshFS = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshFS.<span style="color: black;">create</span><span style="color: black;">(</span>outMeshNumVtx, outMeshNumPolygons, outMeshMPointArray, outMeshPolygonCountArray, outMeshVtxArray, newOutputData<span style="color: black;">)</span></pre>
<p>Le principe est assez simple:</p>
<ul>
<li>On créer un function set <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a></li>
<li>On créer le mesh en donnant tout les arguments (récupéré plus haut) via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#6994886ddf6ea46baf399ff40d30bc32">meshFS.create()</a>.</li>
</ul>
<p>Une fois que le mesh est créé, on récupère le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> de la connexion "outputMesh " <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html#959379b4d93358519ccc99696031756c">pour y "mettre"</a> le MObject que l'on vient de remplir: Le mesh!</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Store them on the output plugs</span>
outputMeshHandle = data.<span style="color: black;">outputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
outputMeshHandle.<span style="color: black;">setMObject</span><span style="color: black;">(</span> newOutputData <span style="color: black;">)</span></pre>
<p>Une fois cela fait, on dit au dependency graph, via <a href="http://download.autodesk.com/us/maya/2010help/api/class_m_data_block.html#0d8faafb64e70cf0a579532bb033c98f">MDataBlock.setClean()</a>, que la connexion a été mise à jour.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># tell to the dependency graph the connection is clean</span>
data.<span style="color: black;">setClean</span><span style="color: black;">(</span> plug <span style="color: black;">)</span></pre>
<p>Et c'est fini! :youplaBoum:</p>
<p>Si vous avez bien suivi le tuto (et si je ne me suis pas planté ( :baffed: ), vous devriez avoir un node qui marche correctement (placez le plan de sorte qu'il "vise" la sphère):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_013.png" alt="projection_mesh_api_013.png" style="display:block; margin:0 auto;" title="projection_mesh_api_013.png, fév. 2011" height="390" width="635" /></p>
<p>Bien sur, si les vertex ne sont pas projeté sur la sphère, il retourne à leur position d'origine:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_014.png" alt="projection_mesh_api_014.png" style="display:block; margin:0 auto;" title="projection_mesh_api_014.png, fév. 2011" height="406" width="487" /></p>
<center>:longBar:</center>
<h5>Conclusion et code source <a name="conclusion_et_code_source"></a></h5>
<p>Arrivez ici, vous devriez avoir compris le principe des matrices (Si ce n'est pas le cas, n'hésitez pas à approfondir, vous verrez la 3D d'une autre façon! :sourit: ), être capable de récupérer les composants d'un mesh et de créer un mesh à partir de rien.</p>
<p>Voici le code source: <a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh.7z">projectMesh.7z</a></p>
<p>Et voilà! Je viens de finir un autre gros tuto. J'espère qu'il aura été instructif et que vous allez pouvoir commencer à faire des choses intéressantes avec l'API Maya. :banaeyouhou:</p>
<p>Ce genre d'informations manquent sur l'internet de l'infographie francophone. :franceHappy:</p>
<p>J'invite donc les seniors qui passeraient par là et qui tireraient quelque chose d'intéressant de ce qu'ils ont lu de ne pas hésiter à "rendre la pareille": Si vous êtes compétent dans un domaine, partagez! Je l'ai fait. :hihi:</p>
<p>N'hésitez pas à me dire si je me suis trompé quelque part, si un point ne vous semble pas clair ou si il y a une erreur. :pasClasse:</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
<center>:longBar:</center>
<h5>Mise à jour: La méthode sans Python <a name="la_methode_sans_python"></a></h5>
<p>Bon, ce n'est pas vraiment le but mais puisque Kel Solar en parle <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#c205">dans les commentaires</a> je vous donne un moyen de faire ça en utilisant la méthode intégré dans Maya. :seSentCon:</p>
<p>Sélectionner le mesh cible:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_016.png" alt="projection_mesh_api_016.png" style="display:block; margin:0 auto;" title="projection_mesh_api_016.png, fév. 2011" height="453" width="603" /></p>
<p>Sélectionner le mesh source:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_017.png" alt="projection_mesh_api_017.png" style="display:block; margin:0 auto;" title="projection_mesh_api_017.png, fév. 2011" height="409" width="554" /></p>
<p>Ouvrez les options du transfert d'attributs:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_018.png" alt="projection_mesh_api_018.png" style="display:block; margin:0 auto;" title="projection_mesh_api_018.png, fév. 2011" height="232" width="259" /></p>
<p>Settez les options comme suis:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_019.png" alt="projection_mesh_api_019.png" style="display:block; margin:0 auto;" title="projection_mesh_api_019.png, fév. 2011" height="359" width="551" /></p>
<p>En gros, on ne transfert que la position des vertex dans l'espace monde en les projetant le long de leur normale.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_020.png" alt="projection_mesh_api_020.png" style="display:block; margin:0 auto;" title="projection_mesh_api_020.png, fév. 2011" height="399" width="622" /></p>
<p>Et voilà le travail!</p>
<p>Vous pouvez tourner le mesh source et c'est actualisé directement! :laClasse:</p>
<p>Voilà! Comme ça, les personnes qui sont venu pour trouver une solution rapide ne seront pas frustré! :sourit:</p>Tkinter: Faire communiquer les variables de l'interfaceurn:md5:63aa6ec3946a173d21138e81febfcbea2009-03-15T23:04:00+01:002013-07-26T22:41:25+02:00NarannScript et codecppdéveloppementfrinterface graphiquepythonscripttkinter<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter009.png" alt="debutTkinter009.png" style="float:left; margin: 0 1em 1em 0;" title="debutTkinter009.png, mar. 2009" height="92" width="192" />Quand on fait une interface, on est souvent amené à récupérer le contenu des informations qui sont dans la dite interface (Est ce que la checkbox est activé? Qui y a t'il dans le widget Entry? etc...). Je vous propose de voir rapidement comment faire interagir des éléments d'une interface tkinter avec différentes variables. Nous allons voir qu'on passe par un objet qui est en fait... Une variable, ou plus précisément, la classe variable. :hehe: Nous aborderons brièvement le resizing des fenêtres dans la dernière partie.</p> <h5>Ze "minimal code" <a name="Ze_minimal_code"></a></h5>
<p>Avant de commencer il faut un code minimum. Le voici:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> tkinter <span style="color: #ff7700;font-weight:bold;">as</span> tk
<span style="color: #ff7700;font-weight:bold;">class</span> tkinterTuto<span style="color: black;">(</span>tk.<span style="color: black;">Frame</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
tk.<span style="color: black;">Frame</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ ==<span style="color: #483d8b;">'__main__'</span>:
tkinterTuto<span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">mainloop</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>L'initialisation de tkinter est subtile et ce serait vous mentir si je vous disais que je sais exactement ce que fait Python avec ce code. Je vais tout de même vous expliquer du mieux que je peut chacune des lignes de ce code minimum.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> tkinter <span style="color: #ff7700;font-weight:bold;">as</span> tk</pre>
<p>Ici, pas de soucis, c'est la ligne qui import le module tkinter. "as tk" est un moyen d'abréger le code. Au lieu de taper "tkinter" devant chaque fonction du module, nous pourront abréger par "tk".</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">class</span> tkinterTuto<span style="color: black;">(</span>tk.<span style="color: black;">Frame</span><span style="color: black;">)</span>:</pre>
<p>C'est la déclaration de la classe principale qui va contenir l'interface. Pour le "tk.Frame" il semblerait que ce soit obligatoire et que ça fasse partie du fonctionnement de tkinter. Il s'agit surement du frame principale qui va contenir tout les autres.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:</pre>
<p>Cette procédure (<strong>init</strong>()) est la procédure qui sera exécuté en premier, à l'initialisation de la classe (C'est le "constructeur").</p>
<pre class="python python">tk.<span style="color: black;">Frame</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span></pre>
<p><img src="http://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/parkingBouley.gif" alt="" style="float:right; margin: 0 0 1em 1em;" />
Alors celle la je pige rien! Je suppose qu'il initialise la frame principal. Mais je comprends pas car il reçois déjà une frame à la création de la classe. Bref, si quelqu'un est capable de m'expliquer exactement pourquoi, au final, il semble que nous ayons deux frames (celle en argument puis celle là) je suis preneurs! :seSentCon:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> __name__ ==<span style="color: #483d8b;">'__main__'</span>:
tkinterTuto<span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">mainloop</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Ces deux lignes permettent de vérifier qu'on a lancer ce fichier (et non pas importé par exemple), et "exécute" la classe.</p>
<p>Nous avons donc le programme minimum:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter001.png" alt="debutTkinter001.png" style="display:block; margin:0 auto;" title="debutTkinter001.png, mar. 2009" height="227" width="208" /></p>
<h5>Part en thèse <a name="Part_en_these"></a></h5>
<p>J'ajoute aussi des lignes qui permettent d'aligner la taille des widgets en fonction de la taille de la fenêtres. L'ajout de ses lignes n'est pas indispensable pour finir la première partie du tutorial mais elles le seront ensuite, lors ce que nous parlerons du "resizing". Vous pouvez essayer de faire sans si vous n'êtes intéressé par la seconde partie.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">class</span> tkinterTuto<span style="color: black;">(</span>tk.<span style="color: black;">Frame</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
tk.<span style="color: black;">Frame</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">rowconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">rowconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">grid</span><span style="color: black;">(</span>sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p>Quelques mots quand même: "columnconfigure" et "rowconfigure" permettent de configurer respectivement les "rows" (lignes) et les "columns" (collones). Le premier chiffre est l'index et le second, sont "poids". (Pour l'instant, ses lignes n'ont pas d'effet).
La dernière ligne indique à quoi le widget en question (ici, la fenêtre principale) va "s'accrocher". NSEW indique: Nord-Sud-Est-West. Vous indiquez donc les "poles" que vous voulez connecter au bordures (Vous n'êtes pas obligé de toute les mettre). Vous avez plusieurs façon de l'écrire suivant la façon dont est importé le module tkinter:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> tkinter
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #008000;">self</span>.<span style="color: black;">grid</span><span style="color: black;">(</span>sticky=tkinter.<span style="color: black;">N</span>+tkinter.<span style="color: black;">S</span>+tkinter.<span style="color: black;">E</span>+tkinter.<span style="color: black;">W</span><span style="color: black;">)</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> tkinter <span style="color: #ff7700;font-weight:bold;">as</span> tk
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #008000;">self</span>.<span style="color: black;">grid</span><span style="color: black;">(</span>sticky=tk.<span style="color: black;">N</span>+tk.<span style="color: black;">S</span>+tk.<span style="color: black;">E</span>+tk.<span style="color: black;">W</span><span style="color: black;">)</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> tkinter <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #66cc66;">*</span>
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #008000;">self</span>.<span style="color: black;">grid</span><span style="color: black;">(</span>sticky=N+S+E+W<span style="color: black;">)</span></pre>
<p>Mais la technique qui marche tout le temps est celle que j'ai fait, directement en string.</p>
<p>Bon, après ses explications <del>foirasses</del> approximatives, attaquons nous à la parti intéressante:</p>
<h5>Donner un titre <a name="Donner_un_titre"></a></h5>
<p>C'est le premier truc qui fait qu'on commence à s'approprier un programme (Ok les puristes, je sais qu'on parle de script...). Vous ne pouvez pas deviner la méthode, elle est donné dans la doc (qui, comme <a href="https://www.fevrierdorian.com/blog/index.php?post/2009/03/10/Tkinter%2C-vous-aussi%2C-faites-des-GUI-en-Python...-Ouai%2C-mes-fesses-ouai...#tkinter">je l'ai écrit avant</a>, est un peu moyenne).</p>
<pre class="python python"><span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">title</span><span style="color: black;">(</span><span style="color: #483d8b;">"tkinterTuto"</span><span style="color: black;">)</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">class</span> tkinterTuto<span style="color: black;">(</span>tk.<span style="color: black;">Frame</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
tk.<span style="color: black;">Frame</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">title</span><span style="color: black;">(</span><span style="color: #483d8b;">"tkinterTuto"</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Ça, c'est du nom!</span>
<span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">master</span>.<span style="color: black;">rowconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">rowconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">grid</span><span style="color: black;">(</span>sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter002.png" alt="debutTkinter002.png" style="display:block; margin:0 auto;" title="debutTkinter002.png, mar. 2009" height="227" width="208" /></p>
<h5>La procédure "qui-créé-les-choses"</h5>
<p>Maintenant que nous avons un titre, nous allons créé une seconde procédure (createWidgets) qui va nous servir à créer les éléments de l'interface. Une fois celle ci créer, nous la lançons à partir de la précédure <strong>init</strong>:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">class</span> tkinterTuto<span style="color: black;">(</span>tk.<span style="color: black;">Frame</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
tk.<span style="color: black;">Frame</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #008000;">self</span>.<span style="color: black;">createWidgets</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#lance la procédure qui créer les widgets</span>
<span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p>Les explication:</p>
<pre class="python python">mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span></pre>
<p>Créé un Frame, qui a comme parent "self" (le widget principal), qui a deux pixels de bordure et un relief de type groove (Vous pouvez regarder les différents <a href="http://infohost.nmt.edu/tcc/help/pubs/tkinter/relief.html" hreflang="en">types de relief</a>).</p>
<pre class="python python">mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p>cette ligne est importante... En effet, nous avons créer le widget mais ne lui avons donné aucun emplacement... Cette ligne s'en charge. "column" et "row" définisse les index de la grille dans laquelle nous "incrustons" les widgets. "sticky" est, comme tout à l'heure, pour accrocher le widget sur les bords de sa grille.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter003.png" alt="debutTkinter003.png" style="display:block; margin:0 auto;" title="debutTkinter003.png, mar. 2009" height="139" width="155" /></p>
<p>Nous avons un joli petit effet de bordure. Vous pouvez tester, la fenêtre est resizeable (C'est nos petites lignes de tout à l'heure qui font cet effet).</p>
<h5>Dévidons le vide... <a name="Devidons_le_vide"></a></h5>
<p>C'est pas le tout mais notre fenêtre reste bien vide... :hehe:</p>
<p>Nous allons la remplir avec le widget le plus simple (Je crois...), le "Label".</p>
<p>Le "Label" c'est quoi? Et bien c'est tout simplement un texte qui se loge dans l'interface...</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;">#creation des widgets</span>
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
valeurOneLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurOne"</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#position des widgets</span>
mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span>
valeurOneLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span></pre>
<pre class="python python">valeurOneLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurOne"</span><span style="color: black;">)</span></pre>
<p>Le premier argument est, comme toujours, le parent du widget (mainFrame), pour le second je vous épargne les explications...</p>
<pre class="python python">valeurOneLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span></pre>
<p>Comme pour tout à l'heure, nous plaçons le widget dans la grille. Vous aurez remarqué qu'à priori "mainFrame" et "valeurOneLabel" sont placé au même endroit mais si vous regardez leurs parents respectif, vous verrez qu'il n'ont pas les mêmes... Et oui! Chaque frame à sa propre grille.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter004.png" alt="debutTkinter004.png" style="display:block; margin:0 auto;" title="debutTkinter004.png, mar. 2009" height="50" width="112" /></p>
<h5>Toc! Toc!... Entry?<a name="Toc_Toc_Entry"></a></h5>
<p>Nous allons maintenant ajouter notre second widget: Entry!</p>
<p>Entry est un champ de texte dans lequel l'utilisateur peut taper... Du texte... Mais c'est surtout un widget qui utilise la classe variable! :hehe:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;">#creation des objets variables</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span> = tk.<span style="color: black;">StringVar</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span>.<span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: #483d8b;">"Ecrivez ici"</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#creation des widgets</span>
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
valeurOneLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurOne"</span><span style="color: black;">)</span>
valeurOneEntry = tk.<span style="color: black;">Entry</span><span style="color: black;">(</span>mainFrame, textvariable=<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#position des widgets</span>
mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span>
valeurOneLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span>
valeurOneEntry.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">1</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span></pre>
<p>Les explications:</p>
<pre class="python python"><span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span> = tk.<span style="color: black;">StringVar</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>La première ligne créé un objet de type StringVar qui va stocker une variable de type... String!</p>
<pre class="python python"><span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span>.<span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: #483d8b;">"Ecrivez ici"</span><span style="color: black;">)</span></pre>
<p>Cette seconde ligne montre comment nous stockons les informations dans cet objet. Il faut imaginer que nous avons affaire à un objet qui contient une variable et non à une variable "classique". On y stocke avec ".set" et récupère avec ".get".</p>
<pre class="python python">valeurOneEntry = tk.<span style="color: black;">Entry</span><span style="color: black;">(</span>mainFrame, textvariable=<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span><span style="color: black;">)</span></pre>
<p>Vous avez remarquez? Nous avons ajouter l'argument "textvariable" et lui avons donné notre objet-variable. Ainsi quand nous modifierons "valeurOneEntry", nous modifierons directement la variable! Simple non? :sourit:</p>
<p>Vous remarquerez le "self" devant "valeurOneEntry". En si nous ne l'avions pas fait, "valeurOneEntry" aurait été supprimé dès la fin de la procédure "createWidgets". En ajoutant le nom du parent devant une variable (Ici, self renvois au parent de la procédure qui est la classe tout simplement), nous disons à l'interpréteur de créer cette variable au niveau de ce parent (Ainsi, notre variable sera présente dans toute la classe). Cela évite (comme en C par exemple :siffle: ) de devoir initialiser une variable (que l'on souhaite conserver) avant d'entrer dans une procédure.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter005.png" alt="debutTkinter005.png" style="display:block; margin:0 auto;" title="debutTkinter005.png, mar. 2009" height="50" width="192" /></p>
<h5>Les python, ça me donne des boutons... <a name="Les_python_ca_me_donne_des_boutons"></a></h5>
<p>Maintenant nous allons ajouter un bouton qui réagit au clique. C'est toujours le même principe:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;">#creation des variables</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span> = tk.<span style="color: black;">StringVar</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span>.<span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: #483d8b;">"Ecrivez ici"</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#creation des widgets</span>
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
valeurOneLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurOne"</span><span style="color: black;">)</span>
valeurOneEntry = tk.<span style="color: black;">Entry</span><span style="color: black;">(</span>mainFrame, textvariable=<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span><span style="color: black;">)</span>
button = tk.<span style="color: black;">Button</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"Click Me!"</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#position des widgets</span>
mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span>
valeurOneLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span>
valeurOneEntry.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">1</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span>
button.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, columnspan=<span style="color: #ff4500;">2</span>, row=<span style="color: #ff4500;">1</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p>Explications:</p>
<pre class="python python">button = tk.<span style="color: black;">Button</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"Click Me!"</span><span style="color: black;">)</span></pre>
<p>Notre bouton, son parent est "mainFrame", et son texte est "Click Me!"...</p>
<pre class="python python">button.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, columnspan=<span style="color: #ff4500;">2</span>, row=<span style="color: #ff4500;">1</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p>Et on le place dans l'interface! Vous remarquerez l'argument "columnspan". Celui ci permet de "manger" un certain nombre de colonne (comme en html pour ceux qui ont eu "la chance" de faire des sites en tableau :aupoil: )</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter006.png" alt="debutTkinter006.png" style="display:block; margin:0 auto;" title="debutTkinter006.png, mar. 2009" height="73" width="192" /></p>
<p>On vois que malgré que le bouton soit placé dans la colonne zéro, il utilise deux colonnes.</p>
<p>Hop hop hop! Passons à l'étape suivante! :marioCours:</p>
<h5>Faut que ça réagisse! <a name="Faut_que_ca_reagisse"></a></h5>
<p>Bien! Maintenant que notre interface est prête, nous allons commencer à la faire "réagire"...</p>
<p>Pour cela, il faut lui indiquer quoi executer... En l'occurrence, une procédure. Créons dans un premier temps une procédure toute simple:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> printSomething<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">(</span><span style="color: #483d8b;">"something"</span><span style="color: black;">)</span></pre>
<p>Cette procédure ne fait qu'écrire "something" dans la console...</p>
<p>Il faut maintenant dire au bouton de l'exécuter quand on clique dessus:</p>
<pre class="python python">button = tk.<span style="color: black;">Button</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"Click Me!"</span>, command=<span style="color: #008000;">self</span>.<span style="color: black;">printSomething</span><span style="color: black;">)</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter007.png" alt="debutTkinter007.png" style="display:block; margin:0 auto;" title="debutTkinter007.png, mar. 2009" height="91" width="301" /></p>
<p>L'objectif maintenant est de pouvoir récupérer, dans notre procédure "printSomething" le contenu de la variable, rien de plus simple! Ça s fait grace au .get:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> printSomething<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">(</span><span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span>.<span style="color: black;">get</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter008.png" alt="debutTkinter008.png" style="display:block; margin:0 auto;" title="debutTkinter008.png, mar. 2009" height="88" width="282" /></p>
<p>Ça avance, ça avance!</p>
<p>Ce qui peut être interessant, c'est de faire intervenir cette variable dans l'interface.</p>
<p>La première fois où j'ai eu à faire un changement de valeur de texte dans l'interface, je ne me suis pas servi de la classe variable mais je fesai tout avec des events... Pour chaque clique je fesai une modification... :baffed:</p>
<p>Grace aux class variable c'est super simple! (On ne se moque pas les développeurs, ça peut sembler évident mais je n'ai pas encore une approche object-oriented très avancé et je découvre tkinter en fesant des relations entre la <a href="http://www.tcl.tk/man/tcl8.5/TkCmd/contents.htm" hreflang="en">doc officiel des commandes tk</a>, et la syntaxe Python)</p>
<p>Donc voici la méthode:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;">#creation des variables</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span> = tk.<span style="color: black;">StringVar</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span>.<span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: #483d8b;">"Ecrivez ici"</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#creation des widgets</span>
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
valeurOneLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurOne"</span><span style="color: black;">)</span>
valeurOneEntry = tk.<span style="color: black;">Entry</span><span style="color: black;">(</span>mainFrame, textvariable=<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span><span style="color: black;">)</span>
button = tk.<span style="color: black;">Button</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"Click Me!"</span>, command=<span style="color: #008000;">self</span>.<span style="color: black;">printSomething</span><span style="color: black;">)</span>
valeurTwoLabel = tk.<span style="color: black;">Label</span><span style="color: black;">(</span>mainFrame, text=<span style="color: #483d8b;">"ValeurTwo"</span>, textvariable=<span style="color: #008000;">self</span>.<span style="color: black;">valeurOneVar</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#position des widgets</span>
mainFrame.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span>
valeurOneLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span>
valeurOneEntry.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">1</span>, row=<span style="color: #ff4500;">0</span>, sticky=<span style="color: #483d8b;">"EW"</span><span style="color: black;">)</span>
button.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, columnspan=<span style="color: #ff4500;">2</span>, row=<span style="color: #ff4500;">1</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span>
valeurTwoLabel.<span style="color: black;">grid</span><span style="color: black;">(</span>column=<span style="color: #ff4500;">0</span>, columnspan=<span style="color: #ff4500;">2</span>, row=<span style="color: #ff4500;">2</span>, sticky=<span style="color: #483d8b;">"NSEW"</span><span style="color: black;">)</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter009.png" alt="debutTkinter009.png" style="display:block; margin:0 auto;" title="debutTkinter009.png, mar. 2009" height="92" width="192" /></p>
<p>Et en temps réel!</p>
<p>C'est la fin de la première partie. Avec ça on peu faire pas mal de choses (comme un générateur de batch par exemple... :sauteJoie:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter010.png" alt="debutTkinter010.png" style="display:block; margin:0 auto;" title="debutTkinter010.png, mar. 2009" height="301" width="676" /></p>
<h5>Varier la taille de la fenêtre<a name="Varier_la_taille_de_la_fenetre"></a></h5>
<p>Si vous avez essayé de resize la fenêtre, vous verrez que les widgets ne s'adaptent pas à sa taille:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter011.png" alt="debutTkinter011.png" style="display:block; margin:0 auto;" title="debutTkinter011.png, mar. 2009" height="142" width="269" /></p>
<p>Le problème vient du "mainFrame" qui ne s'adapte pas à la taille de la fenetre...
Vu que nous avions déjà placé les informations de "poids" de la grille <a href="https://www.fevrierdorian.com/blog/post/2009/03/16/Tkinter%3A-Faire-communiquer-les-variables-de-l-interface.#Part_en_these">plus haut</a>, le reste est assez simple pour le coup:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> createWidgets<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;">#creation des variables</span>
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #808080; font-style: italic;">#creation des widgets</span>
mainFrame = tk.<span style="color: black;">Frame</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, borderwidth=<span style="color: #ff4500;">2</span>, relief=<span style="color: #483d8b;">"groove"</span><span style="color: black;">)</span>
mainFrame.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">0</span><span style="color: black;">)</span>
mainFrame.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span>
<span style="color: black;">[</span>...<span style="color: black;">]</span>
<span style="color: #808080; font-style: italic;">#position des widgets</span>
<span style="color: black;">[</span>...<span style="color: black;">]</span></pre>
<p>Explications:</p>
<pre class="python python">mainFrame.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, weight=<span style="color: #ff4500;">0</span><span style="color: black;">)</span>
mainFrame.<span style="color: black;">columnconfigure</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, weight=<span style="color: #ff4500;">1</span><span style="color: black;">)</span></pre>
<p>Le premier argument est l'index de la colonne. weight, est le "poids" du widget.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/tkinter/faireCommuniquerVariables/debutTkinter012.png" alt="debutTkinter012.png" style="display:block; margin:0 auto;" title="debutTkinter012.png, mar. 2009" height="135" width="269" /></p>
<p>Et voila! :youplaBoum:</p>
<p>Vous pouvez vous amuser à modifier les valeurs des poids pour voir comment le programme réagit.</p>
<p>En espérant que ce petit tutorial vous sera utile, passez une bonne journée.</p>
<p>Dorian</p>