Bienvenue dans ce troisième article consacré aux axes virtuels basés sur les rotations. Dans le deuxième article, nous avons posé les fondations du projet consacré aux rotations. Je vous propose aujourd'hui d'aller plus loin afin d'ajouter quelques briques supplémentaires à notre édifice.
Introduction
Concernant les fonctions EEP, vous pouvez toujours vous référez à la documentation présente sur le site concernant l'utilisation des différentes fonctions Lua pour EEP. Pour les autres fonctions Lua, comme à l'accoutumée, je vous donnerai les explications nécessaires au moment opportun. Vous pouvez également consulter cette page afin d'apporter des réponses à vos questions et les explications nécessaires aux fonctions et mots-clés propres au langage Lua.
Dans cet article, nous allons également découvrir quelques fonctions de trigonométrie mais rien d'insurmontable rassurez-vous.
Si vous voulez écrire vous-même votre script, je vous conseille d'utiliser l'éditeur notepad++ pour profiter de toutes les fonctions d'édition. Cet éditeur est également disponible en français. Ensuite au gré des modifications, un simple copié-collé dans la fenêtre Lua sera parfait même si la possibilité de l'intégrer directement dans la fenêtre Lua sous EEP est possible.
On y va ? Alors c'est parti pour approfondir notre étude sur les rotations !
Réponse à une question
Dans le premier article, nous avons découvert la rotation Z basique avec notre "Mur-porte" dont sa rotation de base est située à gauche.
A gauche... mouais c'est bien, mais maintenant j'aimerai réaliser une rotation à partir du côté droit pour ce mur sans tourner la caméra de 180° ! 😉
Alors comment faire ? Comme nous l'avons déjà vu, a priori c'est impossible car l'utilisation de la fonction EEPStructureSetRotation(...) dans une boucle numérique appliquera immuablement la rotation à partir du côté gauche vu la conception même du modèle à l'origine. Comparativement, si la rotation d'origine était au centre, celle-ci s’effectuerait... au centre sans espoir de changer quoi que ce soit.
Après réflexion, nous pourrions nous dire ceci : "l'idée serait de pouvoir déplacer le point de rotation d'origine du côté gauche vers le côté droit comme ceci" :
Si une boucle numérique ne convient pas, il faut trouver une autre solution et la solution s'appelle le... cercle trigonométrique combiné avec un mouvement de translation !
Le cercle trigonométrique
En quoi consiste le cercle trigonométrique ? regardons l'image ci-dessous :
Vous n'y comprenez rien ? c'est normal !!! ne partez pas ! Je vais résumer pour vous :
Pour faire simple : nous pouvons désormais "déplacer" le point de rotation d'origine vers un nouveau point et c'est exactement ce dont nous avons besoin pour arriver à nos fins ! C'est ce qu'on appelle un mouvement de translation.
Un peu plus compliqué ! Il s'agit d'un cercle de rayon 1 centré à l'origine d'un système de coordonnées cartésiennes. Les angles sont mesurés à partir de l'axe horizontal et peuvent être exprimés en degrés ou en radians (dans notre article, les angles seront mesurés en degrés). Chaque point sur le cercle correspond à un angle donné et peut être représenté par ses coordonnées (cosinus, sinus), où le cosinus de l'angle est la coordonnée X et le sinus de l'angle est la coordonnée Y. Cela permet de visualiser les relations entre les angles et les valeurs des fonctions trigonométriques et Il est également utile pour comprendre les propriétés des fonctions périodiques et pour résoudre des équations trigonométriques.
Si on se réfère au dessin de notre mur, l'angle désiré va donc devenir réalité ! Regardons comment mettre en place ce mécanisme.
Déplacer le point de rotation de gauche à droite
Avant de rentrer dans le vif du sujet, je ne vais pas revenir en intégralité sur l’interaction entre les fonctions, les variables, etc... Nous allons plutôt nous concentrer sur les nouvelles fonctions et les nouveaux paramètres pour mener à bien notre travail. Voici en vidéo ce que nous désirons obtenir :
Une p'tite ouverture sur la droite bien sympa !
Nous allons prendre comme exemple, le mur ID101 pour nous accompagner pendant toute la durée de l'exercice.
Préparons le terrain et commençons par examiner les fonctions appelées à partir des contacts posés sur les voies :
Comme d'habitude, les noms des fonctions sont renseignés dans les propriétés des contacts et comme pour la rotation Z de base, sont situées dans le fichier Projet_principal.lua aux lignes n° 729 et 735 :
-- Ouverture du mur ID101
function fn_Contact_Open_ID101()
Mod.Ouvre_ID101("#101")
end
-- Fermeture du mur ID101
function fn_Contact_Close_ID101()
Mod.Ferme_ID101("#101")
end
A la ligne n° 730 est appelée la fonction Mod.Ouvre_ID101("#101") située dans le fichier ID_Modeles.lua :
-- Ouverture du mur ID101
function M.Ouvre_ID101(Ar_ID_Lua)
-- Appelle la fonction M.ImmoBetonRE1 pour récupérer les paramètres communs du mur en béton
M.ImmoBetonRE1(Ar_ID_Lua)
-- Incrémentation de 0.2 degré à chaque itération de la boucle
gTb_Rot_Increment[Ar_ID_Lua] = 0.2
-- Valeur maximale à atteindre en degrés pour le mur ID101
gTb_Rot_Max[Ar_ID_Lua] = -88
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_MILIEU_DROIT, C.OUVERTURE)
end
A la ligne n° 1124, nous retrouvons notre fonction M.ImmoBetonRE1(Ar_ID_Lua) qui va gentiment nous retourner les paramètres communs du modèle :
Fonction M.ImmoBetonRE1(Ar_ID_Lua)
-- Paramètres communs pour le mur en béton
function M.ImmoBetonRE1(Ar_ID_Lua)
-- La longueur du mur Ar_ID_Lua à l'échelle 1 est = à 10.5 mètres
gTb_Long_X_Ech1[Ar_ID_Lua] = 10.5
-- La largeur du mur Ar_ID_Lua à l'échelle 1 est = à 6 mètres
gTb_Larg_Y_Ech1[Ar_ID_Lua] = 6
-- La hauteur du mur Ar_ID_Lua à l'échelle 1 est = à 8 mètres
gTb_Haut_Z_Ech1[Ar_ID_Lua] = 8
--[[ Variable pour stocker la valeur calculée des rotations
à chaque itération de la boucle while ]]
gTb_Rot_Calcul[Ar_ID_Lua] = 0
gTb_Rot_Origine[Ar_ID_Lua] = C.ROTATION_ORIGINE_GAUCHE
end
Ensuite aux lignes n° 1126 et 1128, le programme récupère respectivement les valeurs des tables gTb_Rot_Increment[Ar_ID_Lua] et gTb_Rot_Max[Ar_ID_Lua].
Pour terminer à la ligne n° 1130, la fonction fn_Operation(.....) est appelée avec les arguments suivants :
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_MILIEU_DROIT, C.OUVERTURE)
- Le premier argument correspond au n° ID du modèle,
- Le deuxième argument implique une rotation Z,
- Le troisième argument demande une ouverture vers l'arrière,
- Le quatrième argument implique une rotation au milieu du côté droit,
- Le cinquième argument active l'opération pour l'ouverture.
Arrêtons-nous une minute sur le quatrième argument. Une rotation sur le côté droit, d'accord... mais pourquoi au milieu ?
Et bien si nous sommes capables de "déplacer" le point de rotation le long de l'axe X (de gauche à droite), nous pouvons aussi le faire sur l'axe Y (de bas en haut) et ainsi cumuler les deux ! Explication avec un petit dessin :
L' axe Y est vertical, ce qui n'empêche en rien de placer notre nouveau point de rotation où bon nous semble sur cet axe et les trois points qui font sens sont :
- Soit le coin avant à droite,
- Soit le milieu à droite,
- Soit le coin arrière à droite.
Pour résumer, reprenons les arguments entre parenthèses pour la fonction fn_Operation(.....) :
- Le n° ID du modèle,
- Initier une rotation Z,
- Avec une ouverture en arrière,
- A partir du côté droit au milieu,
- Avec l'opération programmée pour l'ouverture
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_MILIEU_DROIT, C.OUVERTURE)
Les bases sont désormais posées, il nous reste plus qu'à découvrir les nouveaux paramètres.
La fonction fn_Operation() et l'opération pour l'ouverture
Les nouveaux paramètres à découvrir
Comme la rotation Z classique découverte dans le premier article, l'en-tête de la fonction fn_Operation(.....) et les quelques lignes du début ne change pas :
-- *************************************************************************************************
-- * *
-- * La fonction fn_Operation se charge des trois rotations X, Y, Z pour pivoter les structures *
-- * vers l'avant et vers l'arrière. *
-- * *
-- * Liste des arguments : *
-- * *
-- * Ar_ID_Lua = N° ID Lua de l'objet *
-- * Ar_Rot_Souhait = Rotation souhaitée (x, y, z ou de base) *
-- * Ar_Sens_Mouvement = Sens du mouvement avant ou arrière des modèles *
-- * concerne uniquement les rotation X et Z *
-- * Ar_Pos_Pt_Rot* = Rotation Z (détermine si la rotation s'effectue au milieu, en *
-- * arrière ou en avant sur l'axe Y du modèle) *
-- * Rotation Y (détermine si la rotation s'effectue à gauche ou à droite) *
-- * -> C.ROT_Z_COIN_AVANT_GAUCHE = 20 *
-- * -> C.ROT_Z_MILIEU_GAUCHE = 21 *
-- * -> C.ROT_Z_COIN_ARRIERE_GAUCHE = 22 *
-- * -> C.ROT_Z_COIN_AVANT_DROIT = 25 *
-- * -> C.ROT_Z_MILIEU_DROIT = 26 *
-- * -> C.ROT_Z_COIN_ARRIERE_DROIT = 27 *
-- * -> C.ROT_X = 30 *
-- * -> C.ROT_Y = 31 *
-- * -> C.ROT_Z = 32 *
-- * -> C.ROT_Y_A_GAUCHE = 12 *
-- * -> C.ROT_Y_A_DROITE = 13 *
-- * *
-- * * Argument ignoré pour les rotations X et de base *
-- * *
-- * Ar_Operation = Type d'opération (ouverture ou fermeture) *
-- * *
-- *************************************************************************************************
function fn_Operation(Ar_ID_Lua, Ar_Rot_Souhait, Ar_Sens_Mouvement, Ar_Pos_Pt_Rot, Ar_Operation)
-- /** Variables et tables locales **/
-- Table pour recevoir les valeurs calculées des positions X, Y et Z à chaque itération de la boucle while
local Tb_Calcul_Pos = {}
-- Variables pour recevoir les valeurs cibles X, Y et Z calculées selon le type de rotation demandée
local Nb_Pos_X_Cible, Nb_Pos_Y_Cible, Nb_Pos_Z_Cible = 0, 0, 0
-- Variable pour stocker la fonction anonyme correspondante au type de rotation demandée
local Rotation_Locale = nil
-- Variable pour recevoir la valeur maximale ou minimale selon l'ouverture ou la fermeture
local Nb_Rotation_Min_Max = 0
Nous retrouvons la déclaration de nos variables et table.
Comme d'habitude, avant de démarrer les calculs, nous devons récupérer les valeurs des positions et des rotations du modèle exactement comme la rotation Z de base dans le deuxième article. Pour rappel ces valeurs sont stockées dans les tables globales gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua], gTb_Pos_Z[Ar_ID_Lua] et gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Z[Ar_ID_Lua] reconnaissables avec le préfixe g_ et associées avec le numéro Ar_ID_Lua du modèle (ici le n° 101) :
-- Les calculs s'effectuent uniquement pendant l'opération de l'ouverture
if Ar_Operation == C.OUVERTURE then
-- Récupèrer les valeurs des positions de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua], gTb_Pos_Z[Ar_ID_Lua] = EEPStructureGetPosition( Ar_ID_Lua )
-- Récupèrer les valeurs des rotations de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Z[Ar_ID_Lua] = EEPStructureGetRotation( Ar_ID_Lua )
Comme nous sommes sur une rotation Z, les différents tests nous amènent à celui de la ligne n° 1583 :
-- /** Sinon si la rotation souhaitée concerne la rotation Z **/
elseif Ar_Rot_Souhait == C.ROTATION_AXE_Z then
Nous testons l'égalité à savoir si le deuxième argument Ar_Rot_Souhait est bien = à C.ROTATION_AXE_Z. Un petit rappel des arguments passés à la fonction fn_Operation(.....) dans la fonction M.Ouvre_ID101(Ar_ID_Lua) pour se rafraichir la mémoire :
M.Ouvre_ID101(Ar_ID_Lua)
-- Ouverture du mur ID101
function M.Ouvre_ID101(Ar_ID_Lua)
-- Appelle la fonction M.ImmoBetonRE1 pour récupérer les paramètres communs du mur en béton
M.ImmoBetonRE1(Ar_ID_Lua)
-- Incrémentation de 0.2 degré à chaque itération de la boucle
gTb_Rot_Increment[Ar_ID_Lua] = 0.2
-- Valeur maximale à atteindre en degrés pour le mur ID101
gTb_Rot_Max[Ar_ID_Lua] = -88
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_MILIEU_DROIT, C.OUVERTURE)
end
Le 2ème argument est paramétré avec la constante C.ROTATION_AXE_Z
Le 2ème argument à la ligne n° 1130 est bien paramétré avec la constante C.ROTATION_AXE_Z ainsi la condition est vérifiée et vraie.
Je vais maintenant procéder à une synthèse des arguments passés à la fonction fn_Operation(.....) qui nous servira de support pour la suite et sera également disponible sous forme d'infobulle contextuelle dans le but de faciliter la lecture plus bas dans l'article. Je vais également ajouter quelques paramètres communs du modèle utilisé ainsi nous aurons toutes les informations sous la main et nous évitera de jongler entre les différentes fonctions :
Analyse du test conditionnel - 1ère partie
Arrivée à ce stade, notre fonction fn_Operation(.....) a déjà intégré notre demande pour une rotation Z. C'est bien ! mais elle ne sait pas encore quel traitement précis appliquer car il lui manque les tests des autres arguments concernant les positions X et Y par exemple. L'axe de position Z ne bouge pas en rotation Z ce qui est logique vu que que la rotation s'effectue autour de l'axe du même nom. Ainsi pour sélectionner les bons calculs plusieurs conditions doivent être testées et nous allons commencer par l'axe de position Y.
Voici le premier groupe des tests conditionnels pour renseigner les variables locales Nb_Pos_Y_Cible et Rotation_Locale :
Au début de la partie consacrée à la rotation Z, deux fonctions locales sont implémentées et seront utilisées en fonction du résultat des tests :
- fn_WhileInferieurOuEgal_Z() : cette fonction gère la boucle avec la condition inférieure ou égale ( <= ),
- fn_WhileSuperieurOuEgal_Z() : cette fonction gère la boucle avec la condition supérieure ou égale ( >= ).
Fonctions fn_WhileInferieurOuEgal_Z() et fn_WhileSuperieurOuEgal_Z()
local function fn_WhileInferieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est inférieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] <= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est incrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] + gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo( C.TEMPO )
end
end
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
N'oublions pas que nous sommes sur la rotation Z et par conséquent le point de rotation initial sur l'axe de position Y peut se trouver aussi bien à gauche ou à droite comme nous le montre le petit dessin ci-dessous :
Comme le souligne clairement la première partie de l'ordinogramme, trois tests sont nécessaires pour déterminer quel côté et quelle position ont été demandés dans le quatrième argument lors de l'appel de la fonction fn_Operation(.....) :
- Rotation Z au milieu gauche ou milieu droit ?
- Rotation Z au coin arrière gauche ou au coin arrière droit ?
- Rotation Z au coin avant gauche ou au coin avant droit ?
Dans tous les cas selon le test validé, la variable Nb_Pos_Y_Cible se verra attribuer la valeur correcte pour la position Y et la variable Rotation_Locale héritera la référence de la fonction de rotation adéquate. Nous reviendrons plus tard sur le choix des valeurs et références assignées aux deux variables.
Ci-dessous, la partie du code avec les trois tests. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
La partie du code avec les trois tests
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
Dans le cas de notre modèle, il suffit de regarder dans la synthèse ID101 pour connaitre la valeur du 4ème argument Ar_Pos_Pt_Rot. Dans notre exemple il s'agit de la constante C.ROT_Z_MILIEU_DROIT. Ainsi le point de rotation est fixé sur le côté droit et au milieu de celui-ci. Si on se réfère à la partie du code avec les trois tests, celui de la ligne n° 1626 est vérifié et la condition est vraie. Le programme renseigne les deux variables Nb_Pos_Y_Cible, Rotation_Locale, ignore les deux autres tests et passe directement après la ligne n° 1653.
Nous venons de franchir la première étape car la fonction vient d'intégrer ces nouveaux éléments. Un petit récapitulatif rapide :
- Nous sommes sur une rotation Z,
- La variable Nb_Pos_Y_Cible est paramétrée avec la valeur correcte pour la position Y. Dans notre exemple Nb_Pos_Y_Cible est égal à gTb_Pos_Y[Ar_ID_Lua] à la ligne n° 1629,
- La variable Rotation_Locale a hérité la référence de la fonction de rotation adéquate. Dans notre exemple Rotation_Locale est égale à fn_Rotation_Z à la ligne n° 1631.
Il nous reste maintenant à déterminer la valeur correcte pour la position X, la valeur négative ou positive pour l'angle maximale de l'ouverture et appeler la fonction correspondante pour exécuter la boucle.
Analyse du test conditionnel - 2ème partie
La deuxième partie se décompose en deux. Un premier volet (partie n° 2a) pour analyser séparément le côté droit et un autre pour le côté gauche. Dans cet exercice je présenterai uniquement le côté droit. Nous verrons le côté gauche dans un autre article.
Regardons le premier volet concernant le côté droit afin de renseigner les variables locales Nb_Pos_X_Cible, Nb_Rotation_Min_Max et déterminer la fonction fn_WhileInferieurOuEgal_Z() ou fn_WhileSuperieurOuEgal_Z() afin d'exécuter la boucle de l'ouverture :
Ci-dessous, voici le code correspondant à partir de la ligne 1656. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Le calcul ci-dessous concernent les trois origines :
-- C.ROTATION_ORIGINE_GAUCHE et C.ROTATION_ORIGINE_CENTRE et C.ROTATION_ORIGINE_DROITE
-- Un test conditionnel n'est pas nécessaire pour tester ces trois valeurs afin
-- de déterminer le point de translation car gTb_Corr_Pos_X[Ar_ID_Lua] est utilisée pour cela.
-- Si la rotation est demandée sur le côté gauche du modèle, quelque soit les trois rotations d'origine du modèle
-- Additionner à partir de la position X dans les propriétés du modèle la correction X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
if Ar_Sens_Mouvement == C.VERS_LARRIERE then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture arrière en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose la valeur négative par défaut enregistrée dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = gTb_Rot_Max[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
end
elseif Ar_Sens_Mouvement == C.VERS_LAVANT then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture avant en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose une valeur positive obtenue avec math.abs et la valeur par défaut enregistrée
-- dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = math.abs( gTb_Rot_Max[Ar_ID_Lua] )
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
end
end
Le premier test débute à la ligne n° 1656. Il est étalé sur 3 lignes pour une question de lisibilité. L'opérateur conditionnel utilisé est or qui se traduit par ou.
Explication : si Ar_Pos_Pt_Rot est égal à C.ROT_Z_COIN_AVANT_DROIT ou Ar_Pos_Pt_Rot est égal à C.ROT_Z_MILIEU_DROIT ou Ar_Pos_Pt_Rot est égal à C.ROT_Z_COIN_ARRIERE_DROIT alors... le programme continue à la ligne n° 1661 pour tester l'origine de base de la rotation. Pour le savoir, il suffit de regarder dans la synthèse ID101.
Dans l'exemple de notre modèle, la constante C.ROTATION_ORIGINE_GAUCHE nous informe qu'il s'agit d'une rotation d'origine de base située sur le côté gauche.
La ligne n° 1666 se charge de renseigner la variable Nb_Pos_X_Cible.
Ensuite le programme passe à la ligne n° 1668. Ce test concerne le type de mouvement demandé, soit vers l'arrière ou soit vers l'avant. Dans les deux cas, cette information est disponible dans le 3ème argument Ar_Sens_Mouvement comme nous pouvons le vérifier dans la synthèse ID101 avec la constante C.VERS_LARRIERE. Le test est ainsi validé et la condition est vraie.
Arrivé à ce stade il ne reste plus qu'à déterminer le type d'opération demandée, c'est-à-dire l'ouverture ou la fermeture de notre modèle. Cette information est disponible dans le 5ème argument Ar_Operation comme nous pouvons aussi le vérifier dans la synthèse ID101. Ici est concernée l'opération pour l'ouverture. A la ligne n° 1670 le test étant validé, les lignes n° 1674 et 1676 vont être exécutées.
La ligne n° 1674 se charge de renseigner la variable Nb_Rotation_Min_Max avec la valeur de la table gTb_Rot_Max[Ar_ID_Lua] située dans la fonction Mod.Ouvre_ID101("#101") indiquée dans la synthèse ID101 et fixée à -88°. Ici nous conserverons la valeur négative.
Et pour terminer la ligne n° 1676 appelle la fonction fn_WhileSuperieurOuEgal_Z() pour démarrer le processus d'ouverture de notre modèle.
Fonction locale fn_WhileSuperieurOuEgal_Z()
Je vais répondre à une question qui pourrait légitimement se poser. Pourquoi une fonction locale dans la fonction fn_Operation(.....) ?
Réponse : les deux fonctions locales fn_WhileInferieurOuEgal_Z() et fn_WhileSuperieurOuEgal_Z() concernent les boucles pour les ouvertures mais aussi pour les fermetures. Il serait contreproductif d'écrire autant de lignes de code identiques pour répéter ces boucles vu le nombre de combinaisons différentes. Il est bien plus judicieux d'assigner en amont les bonnes valeurs aux variables et ensuite laisser ces deux fonctions travailler sur la base de ces variables.
Et un des avantages vient du fait même de leurs emplacements particuliers. Vu qu'elles sont imbriquées dans la fonction fn_Operation(.....), les variables et tables locales à la fonction fn_Operation(.....) deviennent... globales pour ces deux fonctions et il est inutile de passer les valeurs des variables et tables sous forme d'arguments dans les en-têtes des deux fonctions.
Je vais maintenant détailler la fonction fn_WhileSuperieurOuEgal_Z(). Nous verrons pour l'opération de la fermeture sa jumelle fn_WhileInferieurOuEgal_Z().
Tout d'abord, voici le code de cette fonction :
Fonction fn_WhileSuperieurOuEgal_Z()
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
Cette fonction gère aussi bien l'ouverture ou la fermeture avec une boucle conditionnelle et l'opérateur relationnel supérieur ou égal ( >= ) d'où son nom.
Cette boucle démarre à la ligne n° 1607. La condition de notre boucle while est la suivante : Tant que gTb_Rot_Calcul[Ar_ID_Lua] est supérieur ou égal à Nb_Rotation_Min_Max, ce qui se traduit par : tant que 0 est supérieur ou égal à -88°, la boucle continue encore et encore.
Si nous regardons dans la synthèse ID101, la valeur contenue dans la table gTb_Rot_Calcul[Ar_ID_Lua] va être mise à jour après la soustraction (à la ligne n° 1610) de la valeur gTb_Rot_Increment[Ar_ID_Lua] qui est égale à 0.2 degré.
Une fois l'opération effectuée, la ligne n° 1612 applique tout simplement la rotation Z classique.
Et à partir de maintenant la magie va opérer ! Pour y parvenir, la variable Rotation_Locale avait hérité de la fonction fn_Rotation_Z à la ligne n° 1630 :
L'héritage de la fonction fn_Rotation_Z
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
Ce mécanisme d'héritage est très intéressant car il permet dans une seule et même variable d'assigner selon le cas une fonction différente par rapport au contexte. Dans notre cas, il s'agit de la fonction fn_Rotation_Z. Mais selon les critères demandés comme par exemple une rotation à partir du coin arrière gauche, la variable Rotation_Locale hériterait d'une autre fonction intitulée fn_Rotation_Coin (aux lignes n° 1640 ou 1650). La variable Rotation_Locale devient à cet instant une fonction anonyme dans le jargon informatique.
Les calculs de la fonction anonyme sont retournés dans la table Tb_Calcul_Pos déclarée au début de la fonction fn_Operation(.....). Le fait de recourir à l'usage d'une table nous apporte déjà un élément de réponse. Il est fort probable que la fonction Rotation_Locale retourne au minimum deux valeurs mais lesquelles ?
Rappelez-vous... l'axe de position Z ne bouge pas si vous manipulez la rotation Z ce qui est logique vu que la rotation s'effectue autour de l'axe du même nom. Je la connais par cœur cette phrase ! 🙂
Donc par déduction, que nous reste-t'il ? si l'axe de position Z ne bouge pas, il nous reste... les axes de positions X et Y ! En effet, ces deux axes vont être calculés à chaque itération de la boucle et c'est la modification des positions X et Y combinée à la rotation Z qui va nous permettre d'ouvrir vers l'arrière notre modèle à partir du côté droit au milieu même si de base, la rotation d'origine ne peut s'effectuer qu'à partir du milieu du côté gauche.
Si vous n'arrivez pas à visualiser mentalement dans l'espace ce principe de géométrie, rassurez-vous ! tout va s'éclairer !
Nous allons déjà découvrir les trois arguments passés à la fonction mais avant, je vais afficher à nouveau le petit dessin déjà vu en haut de cet article et cette fois-ci avec les coordonnées X, Y du point de rotation souhaité :
Rappel de la ligne 1614 avec la fonction Rotation_Locale :
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
Dans un soucis de simplicité, cette fonction comprend trois arguments et peut être décomposée en trois parties :
1ère partie
Argument { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }. Il s'agit des axes de positions d'origine X et Y du modèle enregistrées dans les tables gTb_Pos_X et gTb_Pos_Y. Dans notre exemple X = 250 m et Y = 200 m. Ces deux valeurs sont entourées par des accolades et envoyées à la fonction formatées en tant que table.
2ème partie
Argument { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }. Ces deux valeurs ont été calculées dans les tests plus haut. Rappel du calcul de la variable Nb_Pos_X_Cible à la ligne n° 1666 :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Le calcul ci-dessous concernent les trois origines :
-- C.ROTATION_ORIGINE_GAUCHE et C.ROTATION_ORIGINE_CENTRE et C.ROTATION_ORIGINE_DROITE
-- Un test conditionnel n'est pas nécessaire pour tester ces trois valeurs afin
-- de déterminer le point de translation car gTb_Corr_Pos_X[Ar_ID_Lua] est utilisée pour cela.
-- Si la rotation est demandée sur le côté droit du modèle, quelque soit les trois rotations
-- d'origine du modèle, additionner la position X + la correction X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
Cette variable est le résultat de la position X initiale gTb_Pos_X égale à 250 m auquel on additionne la valeur de la table gTb_Corr_Pos_X. Dans notre exemple, cette valeur est strictement égale à la valeur de la table gTb_Long_X_Ech1 (ligne n° 2032) dans les paramètres communs du modèle. Je reviendrai plus tard sur la table gTb_Corr_Pos_X. Pour le moment, la longueur du modèle est égale à 10.5 m. Si nous faisons le calcul, la variable Nb_Pos_X_Cible est égale à 250 + 10.5 = 260.5 m ce qui correspond exactement à l'axe de position X désiré à droite dans le dessin.
Rappel de la fonction M.ImmoBetonRE1(Ar_ID_Lua)
-- Paramètres communs pour le mur en béton
function M.ImmoBetonRE1(Ar_ID_Lua)
-- La longueur du mur Ar_ID_Lua à l'échelle 1 est = à 10.5 mètres
gTb_Long_X_Ech1[Ar_ID_Lua] = 10.5
-- La largeur du mur Ar_ID_Lua à l'échelle 1 est = à 6 mètres
gTb_Larg_Y_Ech1[Ar_ID_Lua] = 6
-- La hauteur du mur Ar_ID_Lua à l'échelle 1 est = à 8 mètres
gTb_Haut_Z_Ech1[Ar_ID_Lua] = 8
--[[ Variable pour stocker la valeur calculée des rotations
à chaque itération de la boucle while ]]
gTb_Rot_Calcul[Ar_ID_Lua] = 0
gTb_Rot_Origine[Ar_ID_Lua] = C.ROTATION_ORIGINE_GAUCHE
end
Le fait de faire "glisser" un point de rotation vers un autre s'appelle un mouvement de translation. Allez, on passe maintenant à la variable Nb_Pos_Y_Cible. Si nous regardons bien le dessin, la position Y est exactement la même à gauche ou à droite. Le calcul à la ligne n° 1628 nous le prouve :
Extrait de la fonction fn_Operation(....)
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
Rien de plus simple : comme la position Y ne bouge pas, Nb_Pos_Y_Cible est égale à la valeur gTb_Pos_Y qui correspond à la position Y initiale.
Ces deux valeurs sont entourées par des accolades et envoyées à la fonction formatées en tant que table.
3ème partie
Argument gTb_Rot_Calcul[Ar_ID_Lua]. Il s'agit tout naturellement du calcul de l'angle à la ligne n° 1610 dans la boucle.
Passons maintenant à la fonction fn_Rotation_Z !
Analyse de la fonction trigonométrique fn_Rotation_Z
Je sais que la géométrie peut paraitre compliquée. Même si je ne vais pas trop m'attarder sur le sujet, il faut quand même expliquer quelques aspects de cette fonction.
-- **********************************************************************************
-- * *
-- * Cette fonction retourne les positions X et Y dans le cas d'une rotation Z *
-- * avec un point de rotation souhaité au milieu de l'axe Y *
-- * *
-- * M = table avec 2 arguments (Voir l'appel de la fonction pour les arguments) *
-- * O = table avec 2 arguments (Voir l'appel de la fonction pour les arguments) *
-- * Angle = angle de rotation Z recalculé à chaque itération de la boucle while *
-- * *
-- **********************************************************************************
fn_Rotation_Z = function(Ar_M, Ar_O, Ar_Angle)
local x, y, x1, y1
Ar_Angle = Ar_Angle * C.PI180
-- Translation du point pour que le point de rotation soit placé à la position voulue
x1 = Ar_M[1] - Ar_O[1]
y1 = Ar_M[2] - Ar_O[2]
-- Appliquer la rotation
x = x1 * math.cos( Ar_Angle ) - y1 * math.sin( Ar_Angle ) + Ar_O[1]
y = x1 * math.sin( Ar_Angle ) - y1 * math.cos( Ar_Angle ) + Ar_O[2]
-- Retourne les positions X et Y
return { x, y }
end
Commençons par la liste des arguments dans l'en-tête de la fonction à la ligne n° 1956 :
- Ar_M = argument table qui comprend les positions initiales X et Y du modèle,
- Ar_O = argument table qui comprend les positions initiales X et Y. (La position X comprend la position X initiale + la longueur total du modèle sur l'axe X),
- Ar_Angle = correspond à l'angle actuellement en cours de calcul,
- La ligne n° 1966 déclare 4 variables locales x, y, x1 et y1,
- La ligne n° 1968 multiplie l'angle par la valeur de la constante C_.PI180. Cette constante correspond au résultat de pi/180. Ce qui donne une valeur fractionnaire de pi ramenée à 1 degré. Il ne reste plus qu'à multiplier cette valeur par celle de l'angle indiquée dans l'argument Ar_Angle. Cette valeur est nécessaire pour calculer les nouvelles positions X et Y à l'aide des fonctions math.sin et math.cos.
- Ensuite x1 = position X - la position (X + longueur du modèle),
- y1 = position Y - position Y (c'est à dire ici dans notre cas = à 0)
- A la ligne 1975, la nouvelle position X est calculée en tenant compte des valeurs x1 pour le cosinus et de l'angle y1 pour le sinus de l'angle,
- A la ligne 1976, la nouvelle position Y est calculée en tenant compte des valeurs x1 pour le sinus et de l'angle y1 pour le cosinus de l'angle,
- Et pour finir, la ligne n° 1979 retourne les deux nouvelles positions X et Y sous la forme d'une table. Voilà pourquoi nous avons déclaré la table locale Tb_Calcul_Pos dans la fn_Operation(.....).
La fonction est maintenant terminée, le programme retourne dans la fonction fn_WhileSuperieurOuEgal_Z() à la ligne n° 1616 :
Fonction fn_WhileSuperieurOuEgal_Z()
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
Tout ce qu'il nous reste à faire est d'appliquer les nouvelles positions au modèle avec la fonction EEPStructureSetPosition() :
- Le premier argument correspond au n° ID du modèle répertorié dans l'argument Ar_ID_Lua,
- Le deuxième argument se rapporte au premier indice de la table Tb_Calcul_Pos[1] et représente la nouvelle position X,
- Le troisième argument se rapporte au deuxième indice de la table Tb_Calcul_Pos[2] et représente la nouvelle position Y,
- Et le quatrième argument correspond à la position Z enregistrée dans la table gTb_PosZ[Ar_ID_Lua] qui reste inchangée car l'axe de position Z ne bouge pas si vous manipulez la rotation Z.
Ainsi les nouvelles positions sont recalculées autant de fois que la valeur de l'angle est modifiée à chaque itération de la boucle.
A la ligne n° 1618, la fonction fn_Tempo(C_.TEMPO) entre en action et la boucle recommence tant que sa condition est vérifiée.
Une fois celle-ci achevée, la fonction fn_Operation(.....) se termine, les variables et table locales sont détruites et le processus d'ouverture est terminé.
Nous allons maintenant passer à l'opération pour la fermeture !
La fonction fn_Operation() et l'opération pour la fermeture
Le nouveau paramètre à découvrir
L'opération pour la fermeture ressemble beaucoup à celle de l'ouverture et pour cause ! nous allons faire appel à la même fonction fn_Operation(.....). Néanmoins, il y a deux changements à prendre en considération :
Le première changement se situe au niveau du test conditionnel à la ligne n° 1291 :
Fonction fn_Operation()
-- Les calculs s'effectuent uniquement pendant l'opération de l'ouverture
if Ar_Operation == C.OUVERTURE then
-- Récupèrer les valeurs des positions de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua], gTb_Pos_Z[Ar_ID_Lua] = EEPStructureGetPosition( Ar_ID_Lua )
-- Récupèrer les valeurs des rotations de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Z[Ar_ID_Lua] = EEPStructureGetRotation( Ar_ID_Lua )
-- Récupère la longueur en mètres sur l'axe X du modèle après application éventuelle de la nouvelle échelle
gTb_Ech_Dimension_X[Ar_ID_Lua] = fn_EEPStructureGetScale( Ar_ID_Lua, C.ROTATION_AXE_X, gTb_Long_X_Ech1[Ar_ID_Lua] )
-- Récupère la largeur en mètres sur l'axe Y du modèle après application éventuelle de la nouvelle échelle
gTb_Ech_Dimension_Y[Ar_ID_Lua] = fn_EEPStructureGetScale( Ar_ID_Lua, C.ROTATION_AXE_Y, gTb_Larg_Y_Ech1[Ar_ID_Lua] )
-- Récupère la hauteur en mètres sur l'axe Z du modèle après application éventuelle de la nouvelle échelle
gTb_Ech_Dimension_Z[Ar_ID_Lua] = fn_EEPStructureGetScale( Ar_ID_Lua, C.ROTATION_AXE_Z, gTb_Haut_Z_Ech1[Ar_ID_Lua] )
print("\ngTb_Ech_Dimension_X : ", gTb_Ech_Dimension_X[Ar_ID_Lua])
print("gTb_Ech_Dimension_Y : ", gTb_Ech_Dimension_Y[Ar_ID_Lua])
print("gTb_Ech_Dimension_Z : ", gTb_Ech_Dimension_Z[Ar_ID_Lua], "\n")
if gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_CENTRE then
gTb_Corr_Pos_X[Ar_ID_Lua] = ( gTb_Long_X_Ech1[Ar_ID_Lua] / 2 ) + ( ( gTb_Ech_Dimension_X[Ar_ID_Lua] - gTb_Long_X_Ech1[Ar_ID_Lua] ) / 2 )
gTb_Corr_Pos_Y[Ar_ID_Lua] = ( gTb_Larg_Y_Ech1[Ar_ID_Lua] / 2 ) + ( ( gTb_Ech_Dimension_Y[Ar_ID_Lua] - gTb_Larg_Y_Ech1[Ar_ID_Lua] ) / 2 )
elseif gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_GAUCHE then
if Ar_Pos_Pt_Rot == C.ROT_Y_A_DROITE or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
gTb_Corr_Pos_X[Ar_ID_Lua] = gTb_Ech_Dimension_X[Ar_ID_Lua]
else
-- Par déduction, ici Ar_Pos_Pt_Rot = toutes les positions de rotations vers la gauche
gTb_Corr_Pos_X[Ar_ID_Lua] = 0
end
gTb_Corr_Pos_Y[Ar_ID_Lua] = gTb_Ech_Dimension_Y[Ar_ID_Lua] / 2
elseif gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_DROITE then
if Ar_Pos_Pt_Rot == C.ROT_Y_A_GAUCHE or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE then
gTb_Corr_Pos_X[Ar_ID_Lua] = gTb_Ech_Dimension_X[Ar_ID_Lua]
else
-- Par déduction, ici Ar_Pos_Pt_Rot = toutes les positions de rotations vers la droite
gTb_Corr_Pos_X[Ar_ID_Lua] = 0
end
gTb_Corr_Pos_Y[Ar_ID_Lua] = gTb_Ech_Dimension_Y[Ar_ID_Lua] / 2
end
-- gTb_Corr_Pos_Z[Ar_ID_Lua] ne concerne que la rotation Y à partir du bord supérieur gauche ou droit
gTb_Corr_Pos_Z[Ar_ID_Lua] = gTb_Ech_Dimension_Z[Ar_ID_Lua]
end -- Fin du test pour l'argument si l'opération demandée concerne l'ouverture
Lorsque la fonction fn_Operation(.....) est appelée pour l'opération de la fermeture, les valeurs des positions et rotations déjà enregistrées lors de l'ouverture dans les tables globales n'ont pas besoin d'être à nouveau récupérées. Ceci est aussi valable pour les calculs des éventuelles modifications d'échelles. Le bloc complet du code situé entre les lignes n° 1290 et 1358 n'est pas exécuté pour l'opération de la fermeture.
Sinon le deuxième et dernier changement concerne la seule information manquante qui correspond à la valeur angulaire minimale pour la fermeture, comme nous l'avions déjà vu dans l'article précédent pour la rotation de base.
Je ne vais pas revenir sur l'analyse de la première partie de l'ordinogramme qui est strictement identique à l'opération consacrée à l'ouverture. Avant d'aller plus loin, nous avons juste besoin de connaitre la valeur minimale de l'angle pour la fermeture. Cette information est consignée dans la fonction M.Ferme_ID101(Ar_ID_Lua) à la ligne n° 1137 :
Fonction M.Ferme_ID101(Ar_ID_Lua)
-- Fermeture du mur ID101
function M.Ferme_ID101(Ar_ID_Lua)
-- Valeur minimale à atteindre en degrés pour le mur ID101
gTb_Rot_Min[Ar_ID_Lua] = 0
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_MILIEU_DROIT, C.FERMETURE)
end
La table gTb_Rot_Min[Ar_ID_Lua] contient la valeur minimal de l'angle fixée ici à 0°. Naturellement nous retrouvons exactement les mêmes arguments à la ligne n° 1139 comme dans l'opération pour l'ouverture excepté le cinquième argument avec la constante C.FERMETURE.
Reprenons la deuxième partie de notre ordinogramme. Comme il s'agit du même modèle avec les mêmes paramètres, nous pouvons passer directement aux tests pour l'opération de la fermeture.
Reprenons l'extrait de la fonction fn_Operation(.....) et regardons les tests correspondants. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si le modèle possède son origine de base à gauche ou au centre
if gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_GAUCHE or
gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_CENTRE then
-- Additionner la position X dans les propriétés du modèle et la correction X
-- La correction X est égale à la largeur de l'axe X à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
elseif gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_DROITE then
-- Si le modèle possède son origine de base à droite
-- La position X cible est par défaut la position X dans les propriétés du modèle
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua]
end
if Ar_Sens_Mouvement == C.VERS_LARRIERE then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture arrière en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose la valeur négative par défaut enregistrée dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = gTb_Rot_Max[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
end
elseif Ar_Sens_Mouvement == C.VERS_LAVANT then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture avant en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose une valeur positive obtenue avec math.abs et la valeur par défaut enregistrée
-- dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = math.abs( gTb_Rot_Max[Ar_ID_Lua] )
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
end
end
Comme le cinquième argument de la fonction fn_Operation(.....) correspond à la constante C.FERMETURE, la seule différence par rapport à l'ouverture se situe à partir de la ligne n° 1686. Le test va être validé car la condition est vérifiée et vraie. A la ligne n° 1688, la valeur angulaire minimale va être transférer dans la variable Nb_Rotation_Min_Max. A la ligne n° 1690, il ne reste plus qu'à appeler la fonction locale fn_WhileInferieurOuEgal_Z().
Fonction locale fn_WhileInferieurOuEgal_Z()
Comme je l'ai déjà expliqué à l'ouverture, cette fonction est locale à la fonction fn_Operation(.....). Ainsi les tables et variables utilisées dans fn_Operation(.....) sont accessibles dans fn_WhileInferieurOuEgal_Z().
Voici le code de cette fonction :
Fonction fn_WhileInferieurOuEgal_Z
local function fn_WhileInferieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est inférieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] <= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est incrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] + gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo( C.TEMPO )
end
end
Celle-ci gère aussi bien l'ouverture ou la fermeture selon le cas avec une boucle conditionnelle et l'opérateur relationnel inférieur ou égal ( <= ) d'où son nom.
Cette boucle démarre à la ligne n° 1587. La condition de notre boucle while est la suivante : Tant que gTb_Rot_Calcul[Ar_ID_Lua] est inférieur ou égal à Nb_Rotation_Min_Max, ce qui se traduit par : tant que -88 est inférieur ou égal à 0°, la boucle continue encore et encore.
Rappelez-vous à l'ouverture, la valeur calculée dans la table gTb_Rot_Calcul[Ar_ID_Lua] était égale à la valeur angulaire maximale fixée à -88. Deux avantages justifient l'utilisation d'une table globale pour conserver cette valeur :
- Cette valeur reste accessible à n'importe quel endroit du script,
- Inutile d'utiliser la fonction EEPStructureGetRotation() pour récupérer à nouveau la valeur angulaire Z si celle-ci n'avait pas été enregistrée pendant l'opération de l'ouverture.
Ainsi la valeur contenue dans la table gTb_Rot_Calcul[Ar_ID_Lua] va être mise à jour après l'addition (à la ligne n° 1590) de la valeur gTb_Rot_Increment[Ar_ID_Lua] qui est égale à 0.2 degré.
Une fois l'opération effectuée, la ligne n° 1592 applique tout simplement la rotation Z classique.
Ensuite comme à l'ouverture, nous sommes toujours sur une rotation Z au milieu du coté droit. La variable Rotation_Locale avait hérité de la fonction fn_Rotation_Z à la ligne n° 1630 :
L'héritage de la fonction fn_Rotation_Z
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
A la ligne n° 1594, les calculs retournés par la fonction anonyme Rotation_Locale sont transférés dans la table Tb_Calcul_Pos.
Tout ce qu'il nous reste à faire à la ligne n° 1596 est d'appliquer la nouvelle position au modèle avec la fonction EEPStructureSetPosition() :
- Le premier argument correspond au n° ID du modèle répertorié dans l'argument Ar_ID_Lua,
- Le deuxième argument se rapporte au premier indice de la table Tb_Calcul_Pos[1] et représente la nouvelle position X,
- Le troisième argument se rapporte au deuxième indice de la table Tb_Calcul_Pos[2] et représente la nouvelle position Y,
- Et le quatrième argument correspond à la position Z enregistrée dans la table gTb_PosZ[Ar_ID_Lua] qui reste inchangée car l'axe de position Z ne bouge pas si vous manipulez la rotation Z.
Ainsi les nouvelles positions sont recalculées autant de fois que la valeur de l'angle est modifiée à chaque itération de la boucle.
A la ligne n° 1598, la fonction fn_Tempo(C_.TEMPO) entre en action et la boucle recommence tant que sa condition est vérifiée.
Une fois celle-ci achevée, la fonction fn_Operation(.....) se termine, les variables et table locales sont détruites et le processus d'ouverture est terminé.
Maintenant, notre "mur-porte" devrait être revenu à sa position initiale ! Nous allons le vérifier en vidéo :
Ce qu'il faut retenir : L' utilisation du cercle trigonométrique permet de "déplacer" un point de rotation vers une position ( X, Y ) quelconque sur un plan. On appelle cela un mouvement de translation. Comme le point de rotation n'est plus celui d'origine (de la gauche, il est passé à droite), les positions doivent être recalculées pour "maintenir" le modèle à sa place tout en appliquant ladite rotation.
Dans cet exemple, nous avons choisi le milieu du côté droit mais nous pouvons positionner librement le point de rotation où bon nous semble. Il suffit pour cela de connaitre les nouvelles coordonnées X et Y du nouveau point. Par exemple, on pourrait très bien déplacer le point de rotation du milieu droit vers le coin arrière droit. Vous ne me croyez pas ? 😉
Ci-dessous une petite vidéo pour vous montrer que c'est tout à fait possible :
Le poteau est uniquement présent pour symboliser le point de rotation.
Sur la base des exercices précédents, nous allons continuer tranquillement à nous familiariser avec la rotation Z à partir du coin arrière droit comme dans la vidéo ci-dessus et rassurez-vous, le prochain exemple n'est pas plus difficile. Il s'inscrit dans la continuité de notre article.
Ouverture à partir du coin arrière droit
Dans cet exercice, nous garderons toujours l'ouverture à partir du côté droit avec le même modèle et les mêmes paramètres. La seule modification à apporter est de déplacer le point de rotation d'origine placé au milieu du côté gauche vers le côté droit au coin à l'arrière.
Introduction
Commençons par un petit dessin pour illustrer notre exercice :
Comme nous conservons le même modèle que l'exercice précédent, j'ai ajouté les dimensions pour la longueur sur l'axe X et la largeur sur l'axe Y.
Attention: ici l'axe Y n'est pas la hauteur car nous sommes en 2D et non pas en 3D. La hauteur est attribuée dans EEP à l'axe Z.
Le point jaune détermine notre nouveau point de rotation. Pour le placer correctement, nous avons besoin de connaitre les dimensions X et Y du modèle à l'échelle 1. Ces informations sont répertoriées dans la fonction des paramètres communs du modèle M.ImmoBetonRE1(Ar_ID_Lua) dans le fichier ID_Modeles.lua :
-- Paramètres communs pour le mur en béton
function M.ImmoBetonRE1(Ar_ID_Lua)
-- La longueur du mur Ar_ID_Lua à l'échelle 1 est = à 10.5 mètres
gTb_Long_X_Ech1[Ar_ID_Lua] = 10.5
-- La largeur du mur Ar_ID_Lua à l'échelle 1 est = à 6 mètres
gTb_Larg_Y_Ech1[Ar_ID_Lua] = 6
-- La hauteur du mur Ar_ID_Lua à l'échelle 1 est = à 8 mètres
gTb_Haut_Z_Ech1[Ar_ID_Lua] = 8
--[[ Variable pour stocker la valeur calculée des rotations
à chaque itération de la boucle while ]]
gTb_Rot_Calcul[Ar_ID_Lua] = 0
gTb_Rot_Origine[Ar_ID_Lua] = C.ROTATION_ORIGINE_GAUCHE
end
Pour le moment, nous avons besoin de deux informations, la longueur et la largeur :
- Pour la longueur, nous allons appliquer exactement le même calcul que l'exercice précédent : position X initiale + la longueur du modèle,
- Pour la largeur, nous allons récupérer la valeur dans la table gTb_Larg_Y_Ech1[Ar_ID_Lua] qui est ici égale à 6 mètres et aussi la valeur de la position Y initiale du modèle à laquelle nous allons ajouter (ou soutraire)... 3 ou 6 mètres ?
Pour le savoir c'est très facile. Il faut toujours regarder l'emplacement du point de rotation de base du modèle et peu importe qu'il soit à gauche, au milieu ou à droite, car dans notre cas, nous recherchons la position de la rotation sur l'axe Y ! et non pas sur l'axe X :
Ici le point de rotation de base de notre modèle est situé au milieu de l'axe Y. Donc si notre modèle fait 6 mètres de largueur, il suffit de faire 6 / 2 = 3 mètres.
Comment savoir si nous devons additionner ou soustraire les 3 mètres ? Un petit dessin pour expliquer :
Résumons : pour déplacer le point de rotation de base situé à gauche, il faut :
- Prendre la position X du modèle dans le plan à laquelle on additionne la longueur du modèle égale à 10.5 mètres. Par exemple, si le modèle est placé à 250 mètres sur l'axe X, le nouveau point de rotation pour l'axe désiré sera fixé à 250 + 10.5 = 260.5 mètres,
- Prendre la position Y du modèle dans le plan à laquelle on additionne 3 mètres. Par exemple, si le modèle est placé à 200 mètres sur l'axe Y, le nouveau point de rotation sera fixé à 203 mètres.
Notre nouveau point de rotation final X, Y (qui était situé au départ aux positions X = 250, Y= 200) est donc déplacé ou translaté aux nouvelles positions X = 260.5, Y = 203.0. En effet, en géométrie déplacer un point de rotation s'appelle une translation car un point de rotation reste avant tout un point de rotation et sa forme ne change pas.
Traitement de la rotation Z, vers l'arrière, à partir du coin arrière droit
Nous allons utiliser le modèle avec l'ID 102 qui est le même que l'ID 101. Seule change la position Y pour le nouveau point de rotation.
Pour ce nouvel exercice, je vais détailler une fois encore les fonctions. Dans les articles suivants, je m’arrêterai uniquement sur les nouvelles fonctionnalités.
Comme d'habitude au début de la séquence, les noms des fonctions sont renseignées dans les propriétés des contacts. Les fonctions concernées sont situées dans le fichier Projet_principal.lua aux lignes n° 742 et 748 :
-- Ouverture du mur ID102
function fn_Contact_Open_ID102()
Mod.Ouvre_ID102("#102")
end
-- Fermeture du mur ID102
function fn_Contact_Close_ID102()
Mod.Ferme_ID102("#102")
end
A la ligne n° 744 est appelée la fonction Mod.Ouvre_ID102("#102") située dans le fichier ID_Modeles.lua :
-- Ouverture du mur ID102
function M.Ouvre_ID102(Ar_ID_Lua)
-- Appelle la fonction M.ImmoBetonRE1 pour récupérer les paramètres communs du mur en béton
M.ImmoBetonRE1(Ar_ID_Lua)
-- Incrémentation de 0.2 degré à chaque itération de la boucle
gTb_Rot_Increment[Ar_ID_Lua] = 0.2
-- Valeur maximale à atteindre en degrés pour le mur ID102
gTb_Rot_Max[Ar_ID_Lua] = -88
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_COIN_ARRIERE_DROIT, C.OUVERTURE)
end
A la ligne n° 1148, nous retrouvons notre fonction M.ImmoBetonRE1(Ar_ID_Lua) qui va nous retourner les paramètres communs du modèle :
Fonction M.ImmoBetonRE1(Ar_ID_Lua)
-- Paramètres communs pour le mur en béton
function M.ImmoBetonRE1(Ar_ID_Lua)
-- La longueur du mur Ar_ID_Lua à l'échelle 1 est = à 10.5 mètres
gTb_Long_X_Ech1[Ar_ID_Lua] = 10.5
-- La largeur du mur Ar_ID_Lua à l'échelle 1 est = à 6 mètres
gTb_Larg_Y_Ech1[Ar_ID_Lua] = 6
-- La hauteur du mur Ar_ID_Lua à l'échelle 1 est = à 8 mètres
gTb_Haut_Z_Ech1[Ar_ID_Lua] = 8
--[[ Variable pour stocker la valeur calculée des rotations
à chaque itération de la boucle while ]]
gTb_Rot_Calcul[Ar_ID_Lua] = 0
gTb_Rot_Origine[Ar_ID_Lua] = C.ROTATION_ORIGINE_GAUCHE
end
Ensuite aux lignes n° 1150 et 1152, le programme récupère respectivement les valeurs des tables gTb_Rot_Increment[Ar_ID_Lua] et gTb_Rot_Max[Ar_ID_Lua].
Pour terminer à la ligne n° 1130, la fonction fn_Operation(.....) est appelée avec les arguments suivants :
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_COIN_ARRIERE_DROIT, C.OUVERTURE)
- Le premier argument correspond au n° ID du modèle,
- Le deuxième argument implique une rotation Z,
- Le troisième argument demande une ouverture vers l'arrière,
- Le quatrième argument implique une rotation sur le côté droit au coin à l'arrière.
- Le cinquième argument active l'opération pour l'ouverture.
Le seul changement par rapport à l'exercice précédent est situé dans le quatrième argument. La rotation s'effectue toujours sur le côté droit, mais cette fois-ci au coin à l'arrière et non plus au milieu.
Comme nous sommes capables de "déplacer" le point de rotation le long de l'axe X (de gauche à droite), nous pouvons aussi le faire sur l'axe Y (de bas en haut) et ainsi cumuler les deux. Rappel du petit dessin :
Je réitère une nouvelle fois cette phrase pour bien figer la situation : notre nouveau point de rotation final X, Y (qui était situé au départ aux positions X = 250, Y= 200) est donc déplacé ou translaté aux nouvelles positions X = 260.5, Y = 203.0.
Nous avons désormais en notre possession tous les éléments en main pour mener à bien l'opération.
La fonction fn_Operation() et l'opération pour l'ouverture
Révision des paramètres
A l'instar de l'exercice précédent, j'ai créé une synthèse des arguments passés à la fonction fn_Operation(.....) qui nous servira de support pour la suite et sera également disponible sous forme d'infobulle contextuelle dans le but de faciliter la lecture plus bas dans l'article. Je vais également ajouter quelques paramètres communs du modèle utilisé ainsi nous aurons toutes les informations sous la main et nous évitera de jongler entre les différentes fonctions :
Nous retrouvons l'en-tête de notre fonction fn_Operation(.....) :
function fn_Operation(Ar_ID_Lua, Ar_Rot_Souhait, Ar_Sens_Mouvement, Ar_Pos_Pt_Rot, Ar_Operation)
-- /** Variables et tables locales **/
-- Table pour recevoir les valeurs calculées des positions X, Y et Z à chaque itération de la boucle while
local Tb_Calcul_Pos = {}
-- Variables pour recevoir les valeurs cibles X, Y et Z calculées selon le type de rotation demandée
local Nb_Pos_X_Cible, Nb_Pos_Y_Cible, Nb_Pos_Z_Cible = 0, 0, 0
-- Variable pour stocker la fonction anonyme correspondante au type de rotation demandée
local Rotation_Locale = nil
-- Variable pour recevoir la valeur maximale ou minimale selon l'ouverture ou la fermeture
local Nb_Rotation_Min_Max = 0
-- Les calculs s'effectuent uniquement pendant l'opération de l'ouverture
if Ar_Operation == C.OUVERTURE then
-- Récupèrer les valeurs des positions de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua], gTb_Pos_Z[Ar_ID_Lua] = EEPStructureGetPosition( Ar_ID_Lua )
-- Récupèrer les valeurs des rotations de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Z[Ar_ID_Lua] = EEPStructureGetRotation( Ar_ID_Lua )
Jusqu'ici rien ne change par rapport à l'ouverture. Nous retrouvons la déclaration de nos variables et table locales. Les lignes n° 1294 et 1296 récupèrent les valeurs des positions et rotations du modèle identifié avec l'argument Ar_ID_Lua.
Analyse du test conditionnel - 1ère partie
Comme dans l'exercice précédent, nous sommes toujours dans une ouverture vers l'arrière avec une rotation Z, mais cette fois-ci avec un point de rotation situé du côté droit au coin à l'arrière pour un objet dont la rotation d'origine de base est toujours située à gauche.
Reprenons notre ordinogramme pour avoir une vue d'ensemble au sujet du test à effectuer :
Nous allons passer directement au test pour la rotation Z situé à la ligne n° 1583 :
-- /** Sinon si la rotation souhaitée concerne la rotation Z **/
elseif Ar_Rot_Souhait == C.ROTATION_AXE_Z then
Au début de la partie consacrée à la rotation Z, deux fonctions locales sont implémentées et seront utilisées en fonction du résultat des tests :
- fn_WhileInferieurOuEgal_Z() : cette fonction gère la boucle avec la condition inférieure ou égale ( <= ),
- fn_WhileSuperieurOuEgal_Z() : cette fonction gère la boucle avec la condition supérieure ou égale ( >= ).
Fonctions fn_WhileInferieurOuEgal_Z() et fn_WhileSuperieurOuEgal_Z()
local function fn_WhileInferieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est inférieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] <= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est incrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] + gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo( C.TEMPO )
end
end
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
Passons directement aux tests pour déterminer les paramètres retenus par rapport au point de rotation souhaitée. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
La partie du code avec les trois tests
-- Toutes les rotations demandées au milieu ou des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
Dans le cas de notre modèle, il suffit de regarder dans la synthèse ID102 pour connaitre la valeur du 4ème argument Ar_Pos_Pt_Rot. Dans notre exemple il s'agit de la constante C.ROT_Z_COIN_ARRIERE_DROIT. Ainsi le point de rotation est fixé sur le côté droit et au milieu de celui-ci. Si on se réfère à la partie du code, le test de la ligne n° 1633 est vérifié et la condition est vraie. Le programme renseigne les deux variables Nb_Pos_Y_Cible et Rotation_Locale, ignore le dernier test et passe directement après la ligne n° 1653.
Nous venons de franchir la première étape car la fonction vient d'intégrer ces nouveaux éléments. Un petit récapitulatif rapide :
- Nous sommes sur une rotation Z,
- à la ligne n° 1639, la variable Nb_Pos_Y_Cible est paramétrée avec la valeur correcte pour la position Y. Dans notre exemple Nb_Pos_Y_Cible est égal à gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]. Ainsi Nb_Pos_Y_Cible = 200 m + 3 m ce qui fait un total de 203 m sur l'axe de position Y (voir dessin).
- La variable Rotation_Locale a hérité la référence de la fonction de rotation adéquate. Dans notre exemple Rotation_Locale est égal à fn_Rotation_Coin à la ligne n° 1641.
Important : Vous remarquerez à la ligne n° 1639 l'utilisation de la table gTb_Corr_Pos_Y[Ar_ID_Lua]. Je l'ai déjà évoquée au cours de l'exercice précédent. Ici, sa valeur est égale à 3 mètres. Ce qui correspond à la valeur incluse dans la table gTb_Larg_Y_Ech1[Ar_ID_Lua] des paramètres communs du modèle égale à 6 mètres divisée par 2.
Note : Pour faire écho à la remarque ci-dessus, les tables gTb_Corr_Pos_X[Ar_ID_Lua], gTb_Corr_Pos_Y[Ar_ID_Lua] et gTb_Corr_Pos_Z[Ar_ID_Lua] sont utilisées pour calculer les différentes valeurs par rapport aux positions souhaitées ou en cas de modification d'échelle des modèles. J'ai délibérément choisi de ne pas vous en parler pour le moment pour ne pas complexifier l'article en cours. J'aborderai exclusivement ce sujet dans le prochain article.
Il nous reste maintenant à déterminer la valeur correcte pour la position X, la valeur négative ou positive pour l'angle maximale de l'ouverture et pour terminer, appeler la fonction correspondante pour exécuter la boucle.
Analyse du test conditionnel - 2ème partie
La deuxième partie est parfaitement identique à l'exercice précédent. Nous devons renseigner les variables locales Nb_Pos_X_Cible, Nb_Rotation_Min_Max et déterminer la fonction fn_WhileInferieurOuEgal_Z() ou fn_WhileSuperieurOuEgal_Z() afin d'exécuter la boucle de l'ouverture :
Ci-dessous, voici le code correspondant à partir de la ligne 1656. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si le modèle possède son origine de base à gauche ou au centre
if gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_GAUCHE or
gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_CENTRE then
-- Additionner la position X dans les propriétés du modèle et la correction X
-- La correction X est égale à la largeur de l'axe X à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
elseif gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_DROITE then
-- Si le modèle possède son origine de base à droite
-- La position X cible est par défaut la position X dans les propriétés du modèle
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua]
end
if Ar_Sens_Mouvement == C.VERS_LARRIERE then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture arrière en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose la valeur négative par défaut enregistrée dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = gTb_Rot_Max[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
end
elseif Ar_Sens_Mouvement == C.VERS_LAVANT then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture avant en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose une valeur positive obtenue avec math.abs et la valeur par défaut enregistrée
-- dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = math.abs( gTb_Rot_Max[Ar_ID_Lua] )
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
end
end
Le premier test débute à la ligne n° 1656. Il est étalé sur 3 lignes pour une question de lisibilité. L'opérateur conditionnel utilisé est or qui se traduit par ou.
Explication : si Ar_Pos_Pt_Rot est égal à C.ROT_Z_COIN_AVANT_DROIT ou Ar_Pos_Pt_Rot est égal à C.ROT_Z_MILIEU_DROIT ou Ar_Pos_Pt_Rot est égal à C.ROT_Z_COIN_ARRIERE_DROIT alors... le programme continue à la ligne n° 1661 pour tester l'origine de base de la rotation. Pour le savoir, il suffit de regarder dans la synthèse ID102.
Dans l'exemple de notre modèle, la constante C.ROTATION_ORIGINE_GAUCHE nous informe qu'il s'agit d'une rotation d'origine de base située sur le côté gauche.
Le test de la ligne n° 1661 toujours basé sur le même opérateur conditionnel or (ou) compare les deux constantes C.ROTATION_ORIGINE_GAUCHE ou C.ROTATION_ORIGINE_CENTRE. Dans notre exemple, la constante C.ROTATION_ORIGINE_GAUCHE correspond, le test est validé et la condition est vraie. La ligne n° 1667 se charge de renseigner la variable Nb_Pos_X_Cible.
Ensuite le programme ignore le test de la ligne 1669 et passe directement au test à la ligne n° 1677. Ce test concerne le type de mouvement demandé, soit vers l'arrière ou soit vers l'avant. Dans les deux cas, cette information est disponible dans le 3ème argument Ar_Sens_Mouvement comme nous pouvons le vérifier dans la synthèse ID102 avec la constante C.VERS_LARRIERE. Le test est ainsi validé et la condition est vraie.
Arrivé à ce stade il ne reste plus qu'à déterminer le type d'opération demandée, c'est-à-dire l'ouverture ou la fermeture de notre modèle. Cette information est disponible dans le 5ème argument Ar_Operation comme nous pouvons toujours le vérifier dans la synthèse ID102. Ici est concernée l'opération pour l'ouverture. Le test étant validé, les lignes n° 1683 et 1685 vont être exécutées.
La ligne n° 1683 se charge de renseigner la variable Nb_Rotation_Min_Max avec la valeur de la table gTb_Rot_Max[Ar_ID_Lua] située dans la fonction Mod.Ouvre_ID102("#102") indiquée dans la synthèse ID102 et fixée à -88°. Ici nous conserverons la valeur négative.
Et pour terminer la ligne n° 1685 appelle la fonction fn_WhileSuperieurOuEgal_Z() pour démarrer le processus d'ouverture de notre modèle.
Fonction locale fn_WhileSuperieurOuEgal_Z()
Je vais réexpliquer le mécanisme concernant l'héritage des fonctions. Les deux fonctions locales fn_WhileInferieurOuEgal_Z() et fn_WhileSuperieurOuEgal_Z() concernent les boucles pour les ouvertures mais aussi pour les fermetures. Il serait contreproductif d'écrire autant de lignes de code identiques pour répéter ces boucles vu le nombre de combinaisons différentes. Il est bien plus judicieux d'assigner en amont les bonnes valeurs aux variables et ensuite laisser ces deux fonctions travailler sur la base de ces variables.
Et un des avantage vient du fait même de leurs emplacements particuliers. Vu qu'elles sont imbriquées dans la fonction fn_Operation(.....), les variables et tables de la fonction fn_Operation(.....) deviennent... globales pour ces deux fonctions. Ainsi, il est inutile de passer les valeurs des variables et tables sous forme d'arguments dans les en-têtes des deux fonctions.
Je vais maintenant détailler la fonction fn_WhileSuperieurOuEgal_Z(). Nous verrons pour l'opération de la fermeture sa jumelle fn_WhileInferieurOuEgal_Z(). Voici le code de cette fonction :
Fonction fn_WhileSuperieurOuEgal_Z()
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
Celle-ci gère aussi bien l'ouverture ou la fermeture (selon le cas) avec une boucle conditionnelle et l'opérateur relationnel supérieur ou égal ( >= ) d'où son nom.
Cette boucle démarre à la ligne n° 1608. La condition de notre boucle while est la suivante : Tant que gTb_Rot_Calcul[Ar_ID_Lua] est supérieur ou égal à Nb_Rotation_Min_Max, ce qui se traduit par : tant que 0 est supérieur ou égal à -88°, la boucle continue encore et encore.
Si nous regardons dans la synthèse ID102, la valeur contenue dans la table gTb_Rot_Calcul[Ar_ID_Lua] va être mise à jour après la soustraction (à la ligne n° 1611) de la valeur gTb_Rot_Increment[Ar_ID_Lua] qui est égale à 0.2 degré.
Une fois l'opération effectuée, la ligne n° 1613 applique tout simplement la rotation Z classique. Ensuite la variable Rotation_Locale avait hérité de la fonction fn_Rotation_Coin à la ligne n° 1641 :
L'héritage de la fonction fn_Rotation_Coin
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
Ce mécanisme d'héritage est très intéressant car il permet dans une seule et même variable d'assigner selon le cas une fonction différente par rapport au contexte. Dans notre cas, il s'agit de la fonction fn_Rotation_Coin . Mais selon les critères demandés comme par exemple une rotation au coin milieu gauche, la variable Rotation_Locale hériterait d'une autre fonction intitulée fn_Rotation_Z (aux lignes n° 1631 ou 1651). La variable Rotation_Locale devient à cet instant une fonction anonyme dans le jargon informatique.
Les calculs de la fonction anonyme sont retournés dans la table Tb_Calcul_Pos déclarée au début de la fonction fn_Operation(.....). Le fait de recourir à l'usage d'une table nous apporte déjà un élément de réponse important. Il est fort probable que la fonction Rotation_Locale retourne au minimum deux valeurs mais lesquelles ?
Rappelez-vous... l'axe de position Z ne bouge pas si vous manipulez la rotation Z ce qui est logique vu que que la rotation s'effectue autour de l'axe du même nom.
Donc par déduction, que nous reste-t'il ? si l'axe de position Z ne bouge pas, il nous reste... les axes de positions X et Y ! En effet, ces deux axes vont être calculés à chaque itération de la boucle et c'est la modification des positions X et Y combinée à la rotation Z qui va nous permettre d'ouvrir vers l'arrière notre modèle à partir du côté droit au milieu même si de base, la rotation d'origine ne peut s'effectuer qu'à partir de la gauche au milieu.
Nous allons déjà découvrir les trois arguments passés à la fonction mais avant, je vais afficher à nouveau le petit dessin et cette fois-ci avec les coordonnées X, Y du point de rotation souhaité :
Rappel de la ligne 1615 avec la fonction Rotation_Locale :
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
Dans un soucis de simplicité, cette fonction comprend trois arguments et peut être décomposée en trois parties :
1ère partie
Argument { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }. Il s'agit des axes de positions d'origine X et Y du modèle enregistrées dans les tables gTb_Pos_X et gTb_Pos_Y. Dans notre exemple X = 250 m et Y = 200 m. Ces deux valeurs sont entourées par des accolades et envoyées à la fonction formatées en tant que table.
2ème partie
Argument { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }. Ces deux valeurs ont été calculées dans les tests plus haut. Rappel du calcul de la variable Nb_Pos_X_Cible à la ligne n° 1667 :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si le modèle possède son origine de base à gauche ou au centre
if gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_GAUCHE or
gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_CENTRE then
-- Additionner la position X dans les propriétés du modèle et la correction X
-- La correction X est égale à la largeur de l'axe X à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
Cette variable est le résultat de la position X initiale gTb_Pos_X égale à 250 m auquel on additionne la valeur de la table gTb_Corr_Pos_X. Dans notre exemple, cette table est strictement égale à la valeur de la table gTb_Long_X_Ech1 (ligne n° 2032) dans les paramètres communs du modèle. Je reviendrai plus tard sur la table gTb_Corr_Pos_X. Pour le moment, la longueur du modèle est égale à 10.5 m. Si nous faisons le calcul, la variable Nb_Pos_X_Cible est égale à 250 + 10.5 = 260.5 m ce qui correspond exactement à l'axe de position X désiré à droite dans le dessin.
Rappel de la fonction M.ImmoBetonRE1(Ar_ID_Lua)
-- Paramètres communs pour le mur en béton
function M.ImmoBetonRE1(Ar_ID_Lua)
-- La longueur du mur Ar_ID_Lua à l'échelle 1 est = à 10.5 mètres
gTb_Long_X_Ech1[Ar_ID_Lua] = 10.5
-- La largeur du mur Ar_ID_Lua à l'échelle 1 est = à 6 mètres
gTb_Larg_Y_Ech1[Ar_ID_Lua] = 6
-- La hauteur du mur Ar_ID_Lua à l'échelle 1 est = à 8 mètres
gTb_Haut_Z_Ech1[Ar_ID_Lua] = 8
--[[ Variable pour stocker la valeur calculée des rotations
à chaque itération de la boucle while ]]
gTb_Rot_Calcul[Ar_ID_Lua] = 0
gTb_Rot_Origine[Ar_ID_Lua] = C.ROTATION_ORIGINE_GAUCHE
end
Le fait de faire "glisser" un point de rotation vers un autre s'appelle un mouvement de translation. Allez, on passe maintenant à la variable Nb_Pos_Y_Cible. Si nous regardons bien le dessin, la position Y se voit ajouter la valeur de la table gTb_Corr_Pos_Y[Ar_ID_Lua] égale ici à 3 mètres comme nous l'avons vu un peu plus haut. Le calcul à la ligne n° 1639 nous le prouve :
Extrait de la fonction fn_Operation(....)
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
Pour résumer Nb_Pos_Y_Cible est égal à la valeur 200 m + 3 m ce qui fait un total de 203 mètres.
Ces deux valeurs sont entourées par des accolades et envoyées à la fonction formatées en tant que table.
3ème partie
Argument gTb_Rot_Calcul[Ar_ID_Lua]. Il s'agit tout naturellement du calcul de l'angle à la ligne n° 1611 dans la boucle située à l'intérieur de la fonction fn_WhileSuperieurOuEgal_Z().
Passons maintenant à la fonction fn_Rotation_Coin !
Analyse de la fonction trigonométrique fn_Rotation_Coin
Cette fonction diffère par rapport à fn_Rotation_Z vue dans l'autre exercice.
-- **********************************************************************************
-- * *
-- * Cette fonction retourne les positions X et Y dans le cas d'une rotation Z *
-- * avec un point de rotation souhaité pour un coin situé en avant ou en arrière *
-- * *
-- * M = table avec 2 arguments (Voir l'appel de la fonction pour les arguments) *
-- * O = table avec 2 arguments (Voir l'appel de la fonction pour les arguments) *
-- * Angle = angle de rotation Z recalculé à chaque itération de la boucle while *
-- * *
-- **********************************************************************************
fn_Rotation_Coin = function(Ar_M, Ar_O, Ar_Angle)
local x, y, x1, y1
Ar_Angle = Ar_Angle * C.PI180
-- Translation du point pour que le point de rotation soit placé à la position voulue
x1 = Ar_M[1] - Ar_O[1]
y1 = Ar_M[2] - Ar_O[2]
-- Appliquer la rotation
x = x1 * math.cos( Ar_Angle ) - y1 * math.sin( Ar_Angle ) + Ar_O[1]
y = x1 * math.sin( Ar_Angle ) + y1 * math.cos( Ar_Angle ) + Ar_O[2]
-- Retourne les positions X et Y
return { x, y }
end
Commençons par la liste des arguments dans l'en-tête de la fonction à la ligne n° 2006 :
- Ar_M = argument table qui comprend les positions initiales X et Y du modèle,
- Ar_O = argument table qui comprend les positions initiales X et Y. (La position X comprend la position X initiale + la longueur total du modèle sur l'axe X),
- Ar_Angle = correspond à l'angle actuellement en cours de calcul,
- La ligne n° 2008 déclare 4 variables locales x, y, x1 et y1,
- La ligne n° 2010 multiplie l'angle par la valeur de la constante C_.PI180. Cette constante correspond au résultat de pi/180. Ce qui donne une valeur fractionnaire de pi ramenée à 1 degré. Il ne reste plus qu'à multiplier cette valeur par celle de l'angle indiquée dans l'argument Ar_Angle. Cette valeur est nécessaire pour calculer les nouvelles positions X et Y à l'aide des fonctions math.sin et math.cos.
- Ensuite x1 = position X - la position (X + longueur du modèle),
- y1 = position Y - position Y (c'est à dire ici dans notre cas = à 0)
- A la ligne 2017, la nouvelle position X est calculée en tenant compte des valeurs x1 pour le cosinus et de l'angle y1 pour le sinus de l'angle,
- A la ligne 2018, la nouvelle position Y est calculée en tenant compte des valeurs x1 pour le sinus et de l'angle y1 pour le cosinus de l'angle,
- Et pour finir, la ligne n° 2021 retourne les deux nouvelles positions X et Y sous la forme d'une table. Voilà pourquoi nous avons déclaré la table locale Tb_Calcul_Pos dans la fn_Operation(.....).
La fonction est maintenant terminée, le programme retourne dans la fonction fn_WhileSuperieurOuEgal_Z() à la ligne n° 1617:
Fonction fn_WhileSuperieurOuEgal_Z()
local function fn_WhileSuperieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est supérieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] >= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est décrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] - gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible}, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo(C.TEMPO)
end
end
Tout ce qu'il nous reste à faire est d'appliquer la nouvelle position au modèle avec la fonction EEPStructureSetPosition() :
- Le premier argument correspond au n° ID du modèle répertorié dans l'argument Ar_ID_Lua,
- Le deuxième argument se rapporte au premier indice de la table Tb_Calcul_Pos[1] et représente la nouvelle position X,
- Le troisième argument se rapporte au deuxième indice de la table Tb_Calcul_Pos[2] et représente la nouvelle position Y,
- Et le quatrième argument correspond à la position Z enregistrée dans la table gTb_PosZ[Ar_ID_Lua] qui reste inchangée car l'axe de position Z ne bouge pas si vous manipulez la rotation Z.
Ainsi les nouvelles positions sont recalculées autant de fois que la valeur de l'angle est modifiée à chaque itération de la boucle.
A la ligne n° 1619, la fonction fn_Tempo(C_.TEMPO) entre en action et la boucle recommence tant que sa condition est vérifiée.
Une fois celle-ci achevée, la fonction fn_Operation(.....) se termine, les variables et table locales sont détruites et le processus d'ouverture est terminé.
Nous allons maintenant entamer l'opération pour la fermeture !
La fonction fn_Operation() et l'opération pour la fermeture
Comme nous l'avons déjà constaté dans l'exercice précédent, l'opération pour la fermeture ressemble beaucoup à celle de l'ouverture car nous faisons appel à la même fonction fn_Operation(.....). Néanmoins, il y a deux changements à prendre en considération :
Le premier changement se situe au niveau du test conditionnel à la ligne n° 1291 :
Fonction fn_WhileSuperieurOuEgal_Z()
-- Les calculs s'effectuent uniquement pendant l'opération de l'ouverture
if Ar_Operation == C.OUVERTURE then
-- Récupèrer les valeurs des positions de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua], gTb_Pos_Z[Ar_ID_Lua] = EEPStructureGetPosition( Ar_ID_Lua )
-- Récupèrer les valeurs des rotations de la structure n° Ar_ID_Lua (format "#xx)
Bool_Ok, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Z[Ar_ID_Lua] = EEPStructureGetRotation( Ar_ID_Lua )
Lorsque la fonction fn_Operation(.....) est appelée pour l'opération de la fermeture, les valeurs des positions et rotations déjà enregistrées lors de l'ouverture dans les tables globales n'ont pas besoin d'être à nouveau récupérées.
Sinon le deuxième et dernier changement concerne la seule information manquante qui correspond à la valeur angulaire minimale pour la fermeture.
Je ne vais pas revenir sur l'analyse de la première partie de l'ordinogramme qui est strictement identique à l'opération de l'ouverture. Avant d'aller plus loin, nous avons juste besoin de connaitre la valeur minimale de l'angle pour la fermeture. Cette information est consignée dans la fonction M.Ferme_ID102(Ar_ID_Lua) à la ligne n° 1161 :
Fonction M.Ferme_ID101(Ar_ID_Lua)
-- Fermeture du mur ID102
function M.Ferme_ID102(Ar_ID_Lua)
-- Valeur minimale à atteindre en degrés pour le mur ID102
gTb_Rot_Min[Ar_ID_Lua] = 0
fn_Operation(Ar_ID_Lua, C.ROTATION_AXE_Z, C.VERS_LARRIERE, C.ROT_Z_COIN_ARRIERE_DROIT, C.FERMETURE)
end
La table gTb_Rot_Min[Ar_ID_Lua] contient la valeur minimal de l'angle fixée ici à 0°. Naturellement nous retrouvons exactement les mêmes arguments à la ligne n° 1163 comme dans l'opération pour l'ouverture excepté le cinquième argument avec la constante C.FERMETURE.
Reprenons la deuxième partie de notre ordinogramme. Comme il s'agit du même modèle avec les mêmes paramètres, nous pouvons passer directement aux tests pour l'opération de la fermeture.
Reprenons l'extrait de la fonction fn_Operation(.....) et regardons les tests correspondants. Toutes les lignes concernées dans notre exemple sont mises en surbrillance :
Extrait de la fonction fn_Operation(....)
-- Tester d'abord les points de rotations demandés à droite
if Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT or
Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si le modèle possède son origine de base à gauche ou au centre
if gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_GAUCHE or
gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_CENTRE then
-- Additionner la position X dans les propriétés du modèle et la correction X
-- La correction X est égale à la largeur de l'axe X à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle X
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua] + gTb_Corr_Pos_X[Ar_ID_Lua]
elseif gTb_Rot_Origine[Ar_ID_Lua] == C.ROTATION_ORIGINE_DROITE then
-- Si le modèle possède son origine de base à droite
-- La position X cible est par défaut la position X dans les propriétés du modèle
Nb_Pos_X_Cible = gTb_Pos_X[Ar_ID_Lua]
end
if Ar_Sens_Mouvement == C.VERS_LARRIERE then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture arrière en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose la valeur négative par défaut enregistrée dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = gTb_Rot_Max[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
end
elseif Ar_Sens_Mouvement == C.VERS_LAVANT then
if Ar_Operation == C.OUVERTURE then
-- L'ouverture de la porte en ouverture avant en rotation Z à partir du côté droit pour les trois rotations
-- d'origine impose une valeur positive obtenue avec math.abs et la valeur par défaut enregistrée
-- dans la table gTb_Rot_Max[Ar_ID_Lua]
Nb_Rotation_Min_Max = math.abs( gTb_Rot_Max[Ar_ID_Lua] )
-- Appelle la boucle while avec la condition inférieure ou égale
fn_WhileInferieurOuEgal_Z()
elseif Ar_Operation == C.FERMETURE then
Nb_Rotation_Min_Max = gTb_Rot_Min[Ar_ID_Lua]
-- Appelle la boucle while avec la condition supérieure ou égale
fn_WhileSuperieurOuEgal_Z()
end
end
Comme le cinquième argument de la fonction fn_Operation(.....) correspond à la constante C.FERMETURE, la seule différence par rapport à l'ouverture se situe à partir de la ligne n° 1687. Le test va être validée car la condition est vraie. A la ligne n° 1689, la valeur angulaire minimale va être transférée dans la variable Nb_Rotation_Min_Max. Il ne reste plus qu'à appeler la fonction locale fn_WhileInferieurOuEgal_Z() à la ligne n° 1691.
Fonction locale fn_WhileInferieurOuEgal_Z()
Comme je l'ai déjà expliqué à l'ouverture, cette fonction est locale à la fonction fn_Operation(.....). Ainsi les tables et variables utilisées dans fn_Operation(.....) sont accessibles dans fn_WhileInferieurOuEgal_Z().
Voici le code de cette fonction :
Fonction fn_WhileInferieurOuEgal_Z
local function fn_WhileInferieurOuEgal_Z()
-- Tant que la valeur calculée de la rotation est inférieure ou égale à Nb_Rotation_Min_Max alors la boucle continue...
while gTb_Rot_Calcul[Ar_ID_Lua] <= Nb_Rotation_Min_Max do
-- gTb_Rot_Calcul[Ar_ID_Lua] est incrémentée (valeur exprimée en degré)
gTb_Rot_Calcul[Ar_ID_Lua] = gTb_Rot_Calcul[Ar_ID_Lua] + gTb_Rot_Increment[Ar_ID_Lua]
-- Appliquer la rotation Z
EEPStructureSetRotation( Ar_ID_Lua, gTb_Rot_X[Ar_ID_Lua], gTb_Rot_Y[Ar_ID_Lua], gTb_Rot_Calcul[Ar_ID_Lua] )
-- Calculer dynamiquement les positions X et Y en fonction de la progression du calcul de l'angle pour la rotation Z
Tb_Calcul_Pos = Rotation_Locale( { gTb_Pos_X[Ar_ID_Lua], gTb_Pos_Y[Ar_ID_Lua] }, { Nb_Pos_X_Cible, Nb_Pos_Y_Cible }, gTb_Rot_Calcul[Ar_ID_Lua] )
-- Appliquer les valeurs calculées pour les positions X et Y
EEPStructureSetPosition( Ar_ID_Lua, Tb_Calcul_Pos[1], Tb_Calcul_Pos[2], gTb_Pos_Z[Ar_ID_Lua] )
-- Temporisation
fn_Tempo( C.TEMPO )
end
Celle-ci gère aussi bien l'ouverture ou la fermeture selon le cas avec une boucle conditionnelle et l'opérateur relationnel inférieur ou égal ( <= ) d'où son nom.
Cette boucle démarre à la ligne n° 1588. La condition de notre boucle while est la suivante : Tant que gTb_Rot_Calcul[Ar_ID_Lua] est inférieur ou égal à Nb_Rotation_Min_Max, ce qui se traduit par : tant que -88 est inférieur ou égal à 0°, la boucle continue encore et encore.
Rappelez-vous à l'ouverture, la valeur calculée dans la table gTb_Rot_Calcul[Ar_ID_Lua] était égale à la valeur angulaire maximale fixée à -88. Deux avantages justifient l'utilisation d'une table globale pour conserver cette valeur :
- Cette valeur reste accessible à n'importe quel endroit du script,
- Inutile d'utiliser la fonction EEPStructureGetRotation() pour récupérer à nouveau la valeur angulaire Z si celle-ci n'avait pas été enregistrée à l'ouverture.
Ainsi la valeur contenue dans la table gTb_Rot_Calcul[Ar_ID_Lua] va être mise à jour après l'addition (à la ligne n° 1591) de la valeur gTb_Rot_Increment[Ar_ID_Lua] qui est égale à 0.2 degré.
Une fois l'opération effectuée, la ligne n° 1593 applique tout simplement la rotation Z classique.
Ensuite comme à l'ouverture, nous sommes toujours sur une rotation Z au milieu du coté droit. La variable Rotation_Locale avait hérité de la fonction fn_Rotation_Coin à la ligne n° 1631 :
L'héritage de la fonction fn_Rotation_Coin
-- Toutes les rotations demandées au milieu, des côtés gauche ou droit
if Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_MILIEU_DROIT then
-- La position Y cible est par défaut la position Y dans les propriétés du modèle
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Z dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Z
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_ARRIERE_DROIT then
-- Si la rotation demandée est décalée vers le coin arrière gauche ou droit du modèle,
-- additionner la position Y dans les propriétés du modèle et la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] + gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
elseif Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_GAUCHE or Ar_Pos_Pt_Rot == C.ROT_Z_COIN_AVANT_DROIT then
-- Si la rotation demandée est décalée vers le coin avant gauche ou droit du modèle,
-- soustraire de la position Y dans les propriétés du modèle la correction Y
-- La correction Y est égale à la largeur de l'axe Y à l'échelle 1 et
-- prend en compte la modification éventuelle de l'échelle Y
Nb_Pos_Y_Cible = gTb_Pos_Y[Ar_ID_Lua] - gTb_Corr_Pos_Y[Ar_ID_Lua]
-- Passer la fonction anonyme fn_Rotation_Coin dans la variable Rotation_Locale
Rotation_Locale = fn_Rotation_Coin
end
A la ligne n° 1597, les calculs retournés par la fonction anonyme Rotation_Locale sont transférés dans la table Tb_Calcul_Pos.
Tout ce qu'il nous reste à faire, est d'appliquer la nouvelle position au modèle avec la fonction EEPStructureSetPosition() :
- Le premier argument correspond au n° ID du modèle répertorié dans l'argument Ar_ID_Lua,
- Le deuxième argument se rapporte au premier indice de la table Tb_Calcul_Pos[1] et représente la nouvelle position X,
- Le troisième argument se rapporte au deuxième indice de la table Tb_Calcul_Pos[2] et représente la nouvelle position Y,
- Et le quatrième argument correspond à la position Z enregistrée dans la table gTb_PosZ[Ar_ID_Lua] qui reste inchangée car l'axe de position Z ne bouge pas si vous manipulez la rotation Z.
Ainsi les nouvelles positions sont recalculées autant de fois que la valeur de l'angle est modifiée à chaque itération de la boucle.
A la ligne n° 1599, la fonction fn_Tempo(C_.TEMPO) entre en action et la boucle recommence tant que sa condition est vérifiée.
Une fois celle-ci achevée, la fonction fn_Operation(.....) se termine, les variables et table locales sont détruites et le processus de fermeture est terminé.
Maintenant, notre "mur-porte" devrait être revenu à sa position initiale ! Nous allons le vérifier en vidéo :
Comme prévu les deux opérations s'effectuent sans problème.
Conclusion
Nous voici arrivé à la fin de cet article. Nous avons appris comment "déplacer ou translater" un point de rotation prévu à l'origine du côté gauche vers le côté droit au milieu et au coin arrière droit. Je vous laisse trouver la solution pour placer le point de rotation sur le coin avant droit !
Le prochain article sera consacré aux mesures correctives apportées sur les trois axes en fonction des rotations souhaitées et des modifications éventuelles d'échelle. Une fois cette notion acquise, nous irons plus loin dans la rotation Z mais avec d'autres modèles et d'autres paramètres.
Et dans les articles suivants, nous aborderons les rotations Y et X !
Si quelque chose vous échappe dans cet article, n'hésitez pas à laisser un commentaire en bas de cette page.
Je vous remercie de m'avoir suivi jusqu'ici et je vous dis à très bientôt !
Merci d'avoir lu cet article. Si vous avez des questions ou des suggestions, n’hésitez pas à nous en faire part en bas de cette page en commentaires.
Amusez-vous à lire un autre article. A bientôt !