HexaBlock
Bibliothèque de formes
Documentation interne du moteur
Ce document constitue la documentation interne du composant HEXABLOCK.
Ce
document fait partie des sources du projet et est géré en
configuration Pour le mettre à jour, utiliser kompozer, disponible sur
Linux
Table of contents :
See also :
Partie 1 : prévisualisation des éléments créés
Pour
que SALOME puisse prévisualiser le "document" en cours de modivication,
il est nécessaire de créer une copie de l'original. L'évolution
majeure du moteur consiste à implémenter une fonction de
copie.
Ce qui revient à ajouter dans la classe Document la méthode suivante :
Document* copyDocument ();
|
La valeur rendue est le document copié.
Les éléments copiés sont les éléments
intrinsèques du document : vertices, edges, quadrangles, hexaèdres,
lois. Qui sont aussi ceux sauvegardés dans un texte XML. On ne stocke
pas les éléments intermédiaires de construction (grilles, vecteurs,
etc...).
Cette rège pourra être remise en cause si l'application interactive a besoin d'autres informations.
La copie se déroule ainsi :
- Chaque élément dispose d'un champ (nommé clone) qui permet de mémoriser l'adresse du dernier élément copié à partir de cet élément dans le cadre de la copie de document.
- Copier les
éléments de bas niveau (les vertices) : méthode Vertex::duplicate(Document* cible). Cette méthode ne rend pas de pointeur ; elle crée un clone du sommet courant et le mémorise sur le champ clone de la classe Vertex.
- Copier les éléments présents de plus haut niveau dans l'ordre (edges, quads, hexas) : méthode virtuelle EltBase::duplicate ()
- Si
un élément utilise n éléments de plus bas niveau, sa copie utilise les
copies des éléments utilisés. Il est aisé de retrouver la copie car on
en a stocké l'adresse.
- Détail
: la méthode duplicate n'a pas besoin d'argument de type Document* elle
reprend le document cible des éléments de plus bas niveau..
- Copier les lois
Les associations sont copiées lors de la copie de chaque élément.
Retour au début
Partie 2 : Nouvelles fonctions de construction
2.1 Création d'Hexaèdres
Il s'agit de construire un hexaèdre à partir de quadrangles déjà créés dans le
modèle de blocs. Cet hexaèdre est produit à partir des 4 cas suivants :
- à partir de 2 quadrangles reliés ou pas,
- à partir de 3 quadrangles tous reliés,
- à partir de 4 quadrangles tous reliés,
- à partir de 5 quadrangles tous reliés.
Les points d'entrée sont naturellement :
Hexa* addHexa2Quads (Quad* q1, Quad* q2); Hexa* addHexa3Quads (Quad* q1, Quad* q2, Quad* q3); Hexa* addHexa4Quads (Quad* q1, Quad* q2, Quad* q3, Quad* q4); Hexa* addHexa5Quads (Quad* q1, Quad* q2, Quad* q3, Quad* q4, Quad* q5); |
Chacun des cas amène une discussion. La démarche est la suivante :
- Les quadrangles passés en argument dans un ordre quelconque.
- Un
analyseur d'intersections (classe AnaQuads,
définie dans
HexAnaQuads.hxx) détermine les arêtes communes aux n quadrangles passés
en argument. et mémorise ces informations qui seront transmises aux
fonctions internes, afin de ne pas répêter ces calculs d'intersection.
- En fonction du nombre d'arêtes communes à chaque quadrangle, on se ramène à 7 cas.
- Si le nombre d'arêtes communes est insuffisant, le cas est rejeté.
- On nomme ces cas en se servant de la nomenclature des faces dans le projet : (A, B, C, D, E,F)
Remarques :
- Les quadrangles passés en arguments sont considérés comme planaires, il n'y a pas de contrôle d'arguments en entrée à ce niveau
- Ils
sont sensés générer des quadrangles planaires et non croisés. Par
exemple pour addHexaQuadsACD() : il n'y a pas de contrôle de planéité
de la face B.
Rappel : formalisation des Hexaèdres/Quadrangles/Vertex
Pour rappel :
// z=0 z=1
y=0 y=1 x=0 x=1 enum EnumHQuad {Q_A, Q_B, Q_C, Q_D, Q_E, Q_F, HQ_MAXI};
|
La face A est opposée à B, C est opposée à D, E à F. La
position des faces A, C, E normales à Oz, Oy, Ox est purement
théorique. Elle améliore la visualisation. Les orientations sont en
fait interchangeables. On peut intervertir les couples (C,D) et
(E,F), on peut intervertir A et B, C et D, E et F, l'essentiel est
d'avoir une cohérence avec les autres dénominations d'edges (ac,
af, ad ...) ou de sommets (ace, acf, adf ...) et les
conventions de départ.
L'arête située la face X et la face Y est notée xy. Un sommet situé entre les 3 faces X, Y Z est noté xyz :
6=bde +----bd-----+ bdf=7
/| /| be | B bf | / | / | 4=bce +----bc-----+...|...bcf=5 | de D | df
| E | | F
|
z ce |
C cf
|
^ 2=ade...|...+----ad-|---+ adf=3 | y
| / |
/
| / |
ae A |
af
| /
|/
|/
|/ 0=ace +----ac-----+ acf=1 +-----> x
|
La classe AnaQuads
Cette
classe analyse les relations topologiques existant entre plusieurs
quandrangles. Un objet de ce type est créé au début de chaque fonction
addHexaXQuads.
Le constructeur est :
AnaQuads (Quad* q1, Quad* q2, Quad* q3=NULL, Quad* q4=NULL, Quad* q5=NULL);
|
Les champs affectés lors de la construction de l'objet :
MawQuads | Constante égale à 5 5 |
int nbr_quads; | Nombre de quadrangles |
int nbr_aretes; | Nombre total d'arêtes |
Quad* tab_quads [i]; | Le ième quadrangle passé en argument |
int inter_nbre [i]; | Nombre d'intersections du ième quadrangle |
int inter_edge [i][j]; | Nro d'arête de i intersectant le jème quadrangle |
int inter_quad [i][j]; | Nro de quadrangle présent sur la jème arete du iéme quad |
La classe Cramer
Cette
classe résout un système linéaire de n équations à n inconnues.
Quand n est "petit", n<10. Peut être réutilisée sur un autre projet.
Implémentation : HexCramer.hxx.
Test unitaire : test_cramer dans test_hexa1.cxx
int test_cramer (int nbargs, cpchar tabargs[]) { double matrice [] = { 2, 7, 1, 0, 3, 0, 1, 9, 5 }; double second_membre [] = { 1, 1, 1 }; Hex::Real3 solution; Hex::Cramer system(3); // On dimensionne le système de Cramer à la construction
int ier = system.resoudre (matrice, second_membre, solution);
cout << " Solution = (" << solution[0] << ", "
<< solution[1] << ", " << solution[2] << "), ier = " << ier << endl;
// Pour vérifier : Hex::Real3 produit; system.multiply (solution, produit);
cout << " Produit = (" << produit[0] << ", "
<< produit[1] << ", " << produit[2] << ")" << endl; return HOK; }
|
Création d'un hexaèdre avec deux quadrangles : - La classe AnaQuads ne détecte aucune arête commune : appel à addHexaQuadsAB
- La classe AnaQuads détecte une arête commune : appel à addHexaQuadsAC
| |
addHexa2Quads avec 2 quadrangles non reliés. Fonction : addHexaQuadsAB | addHexa2Quads avec 2 quadrangles reliés. Fonction : addHexaQuadsAC |
addHexaQuadAB :
Les huits sommets sont présents, il manque quatre arêtes verticales ce, cf, df, de.
La
difficuté consiste à faire correspondre deux à deux les quatre sommets
de chaque quadrangle sans que les arêtes ne se croisent. La situation
est analogue à mergeQuads ou joinQuads, sauf que dans ces cas, on passe
en argument quatre vertex pour déterminer dans ambiguité les sommets
correspondants deux à deux. On a choisi ici
d'automatiser l'association :
- il
existe huit combinaisons permettant d'associer deux à deux les 4
sommets de 2 quadrangles : décalage de 0 à 3 dans un sens puis dans
l'autre.
- Pour chaque combinaison, on calcule la longueur totale des 4 arêtes créées.
- On retient la combinaison qui mène à une longueur minimale.
Une fois la combinaison optimale établie :
- Création
des 4 arêtes verticales en fonction des combinaisons, stockées dans tu
tableau tedge[4]. Le premier vertex fourni est celui de la face A.
- Création des 4 quadrangles verticaux.
- Création de l'hexaèdre.
Pour créer le i-ème quadrangle vertical :
- l'arête verticale de gauche e_left = tedge[i]
- l'arête verticale de droite e_left = tedge[(i+1) modulo 4]
- l'arête
horizontale basse s'obtient grâce à la fonction Quad::findEdge (v1,
v2). On recherche dans le quadrangle A l'arête dont les 2 sommets sont
les extrémités amont des arêtes e_left et e_right, qui par construction
appartiennent au quadrangle A.
- idem pour l'arête haute ; sauf que ce sont les extrémités aval.
addHexaQuadAC :
Deux
faces présentes A et C, une arête commune ac. Il manque deux sommets
bde et bdf à partir des quels on construit les arêtes manquantes bd,
be, de, bf, df.
On doit s'assurer de ma planéité des quadrangles créés.
La
solution n'est pas unique. Il existe une infinité de sommets pouvant
définir un hexaèdre conforme. On se contente ici d'en choisir une
et de démontrer que l'objet créé est conforme.
Le point bde (resp bdf) est construit en
sommant les vecteurs ac et ce (resp af et cf), ce qui revient à
définir un parallélogramme. On assure ainsi sa présence dans le plan E
(resp. F). Les faces créées E et F sont donc planaires.
Pour démontrer la planéité de la face B :
- (ae , af) coplanaires (donnée)
- be = ae (construction d'un parallèlogramme)
- df = af (idem)
- (1), (2), (3) => (be, bf) coplanaires
|
On procéderait de la même manière pour établir que la face D est plane.
La fonction s'écrit ainsi :
- q_a est le premier quadrangle fourni, q_c le second
- L'edge e_ac est défini qrâce à la strucrure AnaQuads
- On effectue un calcul analogue aux deux extrémités s (amont et aval) de e_ac :
- vx1 = extrémité étudiée de e_ac, vx2 l'autre
- vxa
= sommet adjacent de vx1 sur la face A, différent de vx2. Il est
obtenu en prenant le sommet opposé à vx2 sur la face A. L'indice de vxa
dans qa vaut vx2+2 modulo 4.
- de même, vxc = sommet adjacent de vx1 sur la face C.
- Calcul des coordonnées et création du vertex tv_bdx[s] (x représente la choix entre e et f).
- Création des deux arêtes manquantes te_bx[s] et te_dx[s]
- Création des du quadrangle latéral tq_ef [s]
- Création de l'arête e_bd à partir dses deux vertices de tv_bdx
- Création
du quadrangle D à partir de te_dx[], e_ef, te_dx[1] et de l'arête
opposée à e_ac dans A (fonction Quad::getOpposedEdge(e,n)
- Création du quadrangle B à partir de te_bx[], e_ef, te_bx[1] et de
l'arête opposée à e_ac dans C (fonction Quad::getOpposedEdge(e,n)
- Création de l'hexaèdre
Création d'un hexaèdre avec trois quadrangles :
- La
classe AnaQuads détecte deux arêtes communes : appel à
addHexaQuadsACD. La face A sera celle qui possède deux arêtes communes,
la face C la suivante, D la derniére.
- La classe AnaQuads
détecte trois arêtes communes : appel à addHexaQuadsACE. Ce cas est
symétrique, la face A sera la première de la liste, C la seconde.
| |
addHexa2Quads avec 3 quadrangles disposés en U. Fonction : addHexaQuadsACD | addHexa2Quads avec quadrangles disposés en trièdre Fonction : addHexaQuadsACE |
addHexaQuadACD :
Tous les sommets sont présents. Il reste à créer :
- Les arêtes be et bf
- Les quadrangles E, B, F en déterminant les arêtes qui les constituent.
La difficulté est d'identifier clairement les arêtes et les sommets à partir des quadrangles existant :
- q_a est le quadrangle qui possède deux arêtes communes, q_b et q_c les suivants.
- On exploite les indices d'edges communs fournis par AnaQuads. Rappelons que dans un quadrangle le ième edge est adjacent aux edges i-1 et i+1 et opposé à l'edge i+2.
- e_ac et e_ad sont les intersections de q_a avec q_c et q_d. (nommés ici mais non utilisés dans le programme)
- e_bc est l'arête opposée à e_ac dans q_c, e_bd est l'arête opposée à e_ad dans q_d.
- e_ae
est l'arête suivante de e_ac dans q_a, e_af l'arête précédente.
Ce choix arbitraire détermine les autres dénominations.
- e_ce est l'arête adjacente de e_ac dans q_c qui a un sommet commun (v_ace) avec e_ae.
- e_de est l'arête adjacente de e_ac dans q_d qui a un sommet commun (v_ade) avec e_ae.
- v_acf est le sommet commun à e_af et e_cf
- v_adf est le sommet commun à e_af et e_df
- Création de l'arete e_be à paertir de e_bce et v_bde, puis de l'arête e_bf,
- Créations des quadrangles q_b, q_e, q_f
- Création de l'hexaèdre.
addHexaQuadACE :
Il
est nécessaire de créer le sommet v_bdf. Il est l'intersection des
plans (B, D, F). Chaque plan est défini par trois points présents sur
les autres quadrangles :
- Le plan B est défini par les sommets bcd, bce, bcf . Son vecteur normal norm_b est le produit vectoriel de bc par be.
- Le plan D est défini par les sommets adf, ade, adb. Son vecteur normal norm_d est le produit vectoriel de ac par ae.
- Le
plan F est défini par les sommets adf, acf, bcf. Son vecteur
normal norm_f est le produit vectoriel de ae par ec.
Soit un plan P défini par un point A et un vecteur normal N. Un point M appartient à P si le produit scalaire N.AM est nul
On obtient le système d'équations
- (bdf,bce) . norm_b = 0
- (bdf,ade) . norm_d = 0
- (bdf,acf) . norm_f = 0
|
Equivalent à :
- norme_b[0]*X + norme_b[1]*Y + norme_b[2]*Z = prod_scalaire (v_bce, norm_b)
- norme_d[0]*X + norme_d[1]*Y + norme_d[2]*Z = prod_scalaire (v_ade, norm_d)
- norme_f[0]*X + norme_f[1]*Y + norme_f[2]*Z = prod_scalaire (v_acf, norm_f)
|
Ce système de 3 équations à trois inconnues est résolu par la méthode de Cramer au moyen d'une classe du même nom défini dans le nouveau fichier HexCramer.hxx. Notons qu'un test unitaire de la classe Cramer existe dans le fichier test_quads.cxx
Une fois le système résolu ; s'il n'est pas régulier on retourne un vecteur nul, la suite est aisée :
- Création du vertex s_bdf à partir des coordonnées calculées
- Détermination
des sommets opposés s_be, s_bcf, s_adf en utilisant la méthode
Edge::commonVertex (edge) sur les arêtes dèja déterminées.
- Création des 3 arêtes manquantes e_bd, e_bf, e_df
- Création des quadrangles manquants q_b, q_d, q_f.
- Création de l'hexaèdre.
Création d'un hexaèdre avec quatre quadrangles :
- La classe AnaQuads
détecte trois arêtes communes : appel à addHexaQuadsACDE. La face A sera la première trouvée qui en possède trois,
la face E la suivante à trois arêtes,C et D lles deux autres faces à deux arêtes communes.
- La classe AnaQuads détecte quatre arêtes communes : appel à
addHexaQuadsABCD. Ce
cas est
symétrique chaque face possède deux arêtes. La face A sera la première
de la liste, C et D les deux faces sécantes à A, B la dernière.
| |
addHexa2Quads avec 4 quadrangles disposés en tunnel. Fonction : addHexaQuadsABCD | addHexa2Quads avec 4 quadrangles disposés en fauteuil. Fonction : addHexaQuadsACDE |
addHexaQuadABCD :
Le
rôle des quadrangles passés en arguments est symétrique On nommera q_a
le premier trouvé, q_c le premier qui intersecte q_a, q_c le second et
q_d celui qui n'intersecte pas q_a.
Tous les sommets et arêtes sont présents. Il suffit de créer les deux quadrangles E et F:
Les problèmes de détermination des variables se traiotent comme dans addHexaQuadABC
addHexaQuadACDE :
Le rôle de q_a et qc est symétrique. Ils ont chacun trois arêtes communes avec les autres. q_e et q_f n'en ont que 2.
Tous les sommets sont présents. Il suffit de créer l'arête e_bf et les deux quasrangles B et F:
Les edges se déterminent aisément grâce aux indices d'interection fournis par AnaQuads. Il suffiit de prendre l'opposé :et à la propriété de la classe Quad : l'indice d'une arête opposée à l'arête i vaut i+2.
int nc_ac = strquads.inter_edge[pos_c][pos_a]; // Nro dans q_c de e_ac int nc_ce = strquads.inter_edge[pos_c][pos_e]; // Nro dans q_c de e_ce int nd_ad = strquads.inter_edge[pos_d][pos_a]; // Nro dans q_d de e_ad int nd_de = strquads.inter_edge[pos_d][pos_e]; // Nro dans q_d de e_de int ne_ae = strquads.inter_edge[pos_e][pos_a]; // Nro dans q_e de e_ae
Edge* e_af = q_a->getEdge ((na_ac + 3) MODULO QUAD4); Edge* e_bc = q_c->getEdge ((nc_ac + 2) MODULO QUAD4); Edge* e_cf = q_c->getEdge ((nc_ce + 2) MODULO QUAD4); Edge* e_bd = q_d->getEdge ((nd_ad + 2) MODULO QUAD4); Edge* e_df = q_d->getEdge ((nd_de + 2) MODULO QUAD4); Edge* e_be = q_e->getEdge ((ne_ae + 2) MODULO QUAD4);
Vertex* v_bcf = e_cf->opposedVertex (e_cf->commonVertex (e_af)); Vertex* v_bdf = e_df->opposedVertex (e_df->commonVertex (e_af)); |
Création d'un hexaèdre avec cinq quadrangles :
Il n'y a pas besoin de dissocier plusieurs cas, le travail est réalisé directement dans la méthode addHexa5Quads().
Ce cas est assez simple : c'est un hexaèdre auquel il manque la face que l'on convient de nommer B.
- La face q_a est celle qui possède 4 intersections.
- Une
fois son indice qbase déterminé dans AnaQuads, on boucle sur les 4
quadrangles sécants à q_a et on obtient l'arête opposée t_edge[i] et le
quadrangle associé tquad[i]. Les quadrangles q_c, g_d, q_e, q_f se
retrouvent dans le tableau tquad dans un ordre exploitable, le problème
étant symétrique.
- On construit qb à partir des t_edge[i] donnés dans
cet odre : q_b = new Quad (tedge[0], tedge[1], tedge[2],
tedge[3]);
- Enfin l'hexaèdre : hexa = new Hexa (q_a, q_b, tquad[0], tquad[2], tquad[1], tquad[3])
Retour au début
2.2 Génération d'Hexaèdres par révolution
Le pont d'entrée dans la classe Document est :
Elements* revolutionQuads (Quads& start, Vertex* center, Vector* axis, vector<double>& angles);
|
Avec :
- start est la liste des quadrangles de départ. Ils doivent costituer une surface libre.Rappelons que le type Quads est équivalent à vector<Quad*>
- center et axis sont respecttivement le centre et l'axe de la rotation
- le vecteur angles définit pour chaque incrément l'angle de la rotation.
| |
Avant la revolution : on a coloré les quadrangles à déplacer, ainsi que l'axe de rotation. | Résultat de la revolution. | |
Retour au début
2.3 Substitution d'Hexaèdres
Il
s'agit de remplacer un hexaèdre ou des hexaèdres par une autre série
d'hexaaaèdres. Pour pouvoir effectuer ce remplacement, cette autre
série d'hexaèdres doit vérifier des conditions de collage de faces.
| |
Exemple
: remplacement des 3 hexaèdres jaunes centraux par 3 fois 5 nouveaux
hexaèdres qui sont construits par extrusion des 5 quadrangles marrons
(le motif). | Résultat du remplacement. |
La signature de la fonction est la suivante :
Elements* Document::revolutionQuads (Hexas& pattern, Vertex* p1, Vertex* c1,
Vertex*
p2, Vertex* c2, Vertex* p3, Vertex* c3);
|
Avec :
- pattern est la liste des hexaèdres à extruder.
- c1, c2 et c3
définissent le quadrangle "cible" QB. Soit QA la face opposée à QB. A
partir de cette face, on définit l'ensemble des n hexaèdres liés par
les faces "opposées" (QAi, QBi).
- p1, p2 et p3 sont les vertices du pattern qui seront associés resperctivement à c1, c2 et c3.
Conditions :
- Le pattern doit avoir exactement 4 faces latérales externes, que l'on appellera pqc, pqd, pqe, pqf.
- Les
faces"transversales" inférieure et supérieure et doivent être
superposables : le motif des quadrangles qui constituent ces faces doit
être identique.
- c1,c2 et c3 sont sur une face externe
Pour désigner les faces d'un hexaèdres, on utilise les notations habituelles définies plus haut.
Conventions et orientations :
- Les
3 vertices de départ du pattern définissent deux edges : pe_ac (sens
Ox) et pe_ae (sens oy) et 3 vertices pv_aed, pv_ace, pv_acf.
pv_ace est celui des 3 vertices qui est commun aux deux edges, pv_ade
la première des extrémités.
- Les 4 faces latérales externes du pattern sont notées pq_c, pq_d, pq_e, pq_f.
- Les
3 vertices cibles définissent un quadrangle cq_b , deux edges
ce_bc (Ox) et ce_be (Oy) et 3 vertices cv_bed, cv_bce et cv_bcf.
définis comme pout le pattern.
(A suivre ...)
Retour au début
Partie 3 : Bibliothèque de formes
Cette biblothèque est constituée de quatre nouvelles formes :
- La demi-sphère trouée
- Le huitième de sphère trouée
- La demi-écorce trouée
- Le huitième d'écorce trouée
Ces formes sont implémentées (et traduites ....) de la façon suivante :
Elements* makeSphere (Vertex* center, Vector* vx, Vector* vz, double radius, double radhole, Vertex*
plorig, int
nrad, int nang, int nhaut);
|
Elements* makePartSphere (Vertex* center, Vector* vx, Vector* vz, double radius, double radhole, Vertex*
plorig, double angle, int
nrad, int nang, int nhaut); |
Elements* makeRind (Vertex* center, Vector* vx, Vector* vz, double radius, double radint, double radhole, Vertex*
plorig, int
nrad, int nang, int nhaut); |
Elements* makePartRind (Vertex* center, Vector* vx, Vector* vz, double radius, double radint, double radhole, Vertex*
plorig, double angle, int
nrad, int nang, int nhaut); |
Remarques :
- Le plan sécant ne passe pas
forcément par le centre de la sphère. Il définit le demi-espace dans
lequel se situe l'objet à dessiner.Il peut même être disjoint de la
sphère. S'il est "en dessous", la sphère est entièrement dessinée, s'il
est "au dessus", l'objet sera vide.
3.1 Aspect visuel (exemples)
Conditions
| |
Demi-Sphère trouée (makeSphere ()) | Huitième de sphère trouée (makePartSphere ()) |
| |
Demi écorce trouée (makeRind ()) | Huitième d'écorce trouée (makePartRind ()) |
3.1 Implémentation
Toutes ces fonctions publiques appellent une seule fonction de la classe Elements :
int makeRind (EnumGrid type, Vertex* center, Vector* vx, Vector* vz, double radius, double radint, double radhole, Vertex*
plorig, double angle, int
nrad, int nang, int nhaut); |
Une fonction controlRind() vérifie
la cohérence des arguments passés en entrée et calcule les deux extrema
de l'angle d'élévation phi (voir chapitre suivant).
Une boucle à triple indice sur (nrad, nang, nhaut) calculme les coordonnées des sommets par appel de la fonction getCylPoint (nx,ny,nz, px,py,pz).
Ce
calcul se fait sur une sphère de centre O et d'axe Oz, dont les
hexaedres sont comptés à partir de l'axe Ox de façon à remplir le
secteur angulaire theta.. Les coordonnées sont ensuite replacées dans
les coordonnes utilisateur grâce à la fonction transfoVertices(). Edges, quadrangles et hexaèdres sont ensuites assemblés grâce à la fonction fillGrid().
3.2 Détermination de l'angle d'élévation (phi)
L'angle
d'azimuth theta (longitude) est défini explicitement par l'utilisateur.
L'angle d'élévation phi est délimité par la position du plan
horizontal et par le diamètre du trou. Soit :
- Une sphère de centre O et de rayon R.
- La phère est traversée par un trou cylindrique de rayon r.
- Un plan défini par son vecteur normal n (vertical sur la figure qui suit) et un point A
A partir de ces éléments de base, on définit :
- Le point H, projection orhogonale du centre O de la sphere sur le plan (A,n)
- L'angle alpha défini par le centre O et le cylindre : sin (alpha) = r/R
- L'angle beta délimité par l'horizontale et l'intersection du plan (A,n) et de la sphère.
- sin (beta) = OH/R
- et OH = OA.n/|n| (produit scalaire de OA par le vecteur unitaire associé à n).
L'angle d'élévation phi est encadré par les valeurs suivantes : - max (alpha-pi/2, beta) <= phi <= pi/2 - alpha
Retour au début
Partie 4 : Evolution des associations
Cette tâche est composée de 3 évolutions, au sujet de
l'association, qui sont décrites ci-après.
4.1 Nouveaux objets
Il s'agissait d'ajouter de nouveaux objets
intermédiaires de travail, comme le vecteur ou le cylindre :
- le
cercle défini par un
centre, un
rayon et une
normale,
- la
sphère définie par un
centre et un
rayon.
Il était p aussi associer
les objets intermédiaires aux objets géométriques venant du
composant GEOM de SALOME :
- un
cercle,
- une
sphère,
- un
cylindre.
4.2 Associations prédéfinies
La deuxième évolution consiste à améliorer les associations
des modèles de blocs prédéfinis en calculant automatiquement des associations
implicites, les fonctions concernées sont les suivantes:
- ajouter
une grille cylindrique .
- ajouter
une grille sphérique.
- ajouter
un cylindre, un tuyau.
- ajouter
l’intersection de 2 cylindres ou de 2 tuyaux.
- faire
de même pour toutes les nouvelles fonctions : ecorces et portions de sphères.
Pour chaque arête (sphérique) concernée, on associera un arc de cercle.
Il est inutile d'associer laes faces, SMESH est capable de mailler si les arêtes sont associées.
Implémentation
Rappelons que l'objet Shape de HexaBlock est constitué :
- D'une chaine de caractère (b_rep) qui permet de décrire toute forme de GEOM
- De deux paramètres debut et fin compris entre 0 et 1 permettant de délimiter la ligne décrite par la b-rep quand c'en est une
Deux opérations sont nécessaires :
- créer un cercle au moyen de GEOM,
- découper ce cercle en arcs de cercle qui seront associés aux arêtes concernées.
Opération 1 :
Le cercle à créer est en fait un arc de cercle orienté de 360
degrés. Il est en effet nécessaire de définir une origine. GEOM
permet de générer finir un tel
cercle, à partir duquel HexaBlock définira des arcs au moyen des
paramètres debut et fin des shapes :
Cette action est assurée par la fonction geom_create_circle
qui génère la brep du cercle à partir de son centre, de son rayon,
de son vecteur normal, du vecteur Ox qui permet de définir son
origine.
void geom_create_circle (double* centre, double rayon, Vector* normale,
double* ox, string& brep) { gp_Pnt gp_center (centre [dir_x], centre [dir_y], centre [dir_z]);
gp_Vec gp_ox (ox
[dir_x], ox [dir_y], ox
[dir_z]); gp_Vec gp_norm (normale->getDx(), normale->getDy(), normale->getDz());
gp_Ax2 gp_axes (gp_center, gp_norm, gp_ox); gp_Circ gp_circ (gp_axes, rayon);
TopoDS_Edge geom_circ = BRepBuilderAPI_MakeEdge(gp_circ).Edge(); ostringstream stream_shape; BRepTools::Write(geom_circ, stream_shape);
brep = stream_shape.str(); } |
GEOM permet de définir un tel cercle (calsse gp_Circ) à partir d'un systemes d'axes 2D (classe gp_Ax2 ) et d'un rayon. Ce cercle esr ensuite transformé en forme TopoDS_Shape qui est l'objet GEOM que l'on maniplule habituellement losque l'on procède aux associations. Plus précisément en TopoDS_Edge au moyen de BRepBuilderAPI_MakeEdge. Enfin l'objet est transformé en chaine de caractères de format Brep via ostringstream.
Opération 2 :
On dispose d'une b-rep décrivant un arc de cercle de 360 degrés.
Il est aisé de définir proportionnellement pour chacun des edges
constituant la ligne associée à la portion de cercle les paramètres
début et fin par rapport aux angles de départ et d'arrivée de chaque
arc. Cette opération fut d'ailleurs réalisée lors de la préservarion
des associations lors d'un "cut". On réutilisera donc la fonction Elements::cutAssociation(...) moyennant quelques aménagements.
4.3 Propagation des associations
La troisième évolution consiste à améliorer la propagation
automatique des associations, les fonctions concernées sont les suivantes:
- fusionner
ou déconnecter des sommets, des arêtes, des quadrangles :
- s’il
y a des associations, ne pas les perdre,
- prismer
un ou des quadrangles :
- s’il
y a des associations dans la direction de l’extrusion, les propager,
- ajouter
des hexaèdres par translation, par rotation, par homothétie ou par
symétries :
- si
les hexaèdres sources ont une association, alors les propager,
- faire
de même pour la fonction «révolution» du lot 2.
Ces trois évolutions seront implémentées dans le moteur.
Seule le première évolution (création de sphères ou cercles) sera implémentée
dans la partie interactive.
Implémentation :
En
ce qui concerne les associations propagées par les transformations, la
fonction de base de l'implémentation est transfo_brep.
Cette fonction reprend la matrice du modèle de blocs et l'applique à la shape décrite par la brep.
// ====================================================== transfo_brep void transfo_brep (string& brep, Matrix* matrice, string& trep) { BRep_Builder builder; TopoDS_Shape shape_orig; ostringstream stream_shape; gp_Trsf transfo;
double
a11,a12,a13,a14, a21,a22,a23,a24, a31,a32,a33,a34; matrice->getCoeff (a11,a12,a13,a14, a21,a22,a23,a24, a31,a32,a33,a34); transfo.SetValues (a11,a12,a13,a14, a21,a22,a23,a24, a31,a32,a33,a34,
Epsil2, Epsil2);
istringstream stream_brep (brep); BRepTools::Read (shape_orig, stream_brep, builder); BRepBuilderAPI_Transform brep_transfo (shape_orig, transfo, Standard_True); TopoDS_Shape result = brep_transfo.Shape();
BRepTools::Write (result, stream_shape); trep = stream_shape.str(); }
|
Pour la fonction de prismQuads, on utilise une variante simplifiée :
// ====================================================== translate_brep void translate_brep (string& brep, double dir[], string& trep) { gp_Trsf transfo; BRep_Builder builder; TopoDS_Shape orig; ostringstream stream_shape;
gp_Vec
vecteur (dir [dir_x], dir [dir_y],
dir [dir_z]); transfo.SetTranslation (vecteur); istringstream stream_brep (brep); BRepTools::Read (orig, stream_brep, builder); TopLoc_Location loc_orig = orig.Location(); gp_Trsf trans_orig = loc_orig.Transformation(); TopLoc_Location loc_result (transfo * trans_orig); TopoDS_Shape result = orig.Located (loc_result);
BRepTools::Write (result, stream_shape); trep = stream_shape.str(); }
|
Pour la fonction de prismQuads, on utilise une variante simplifiée :
Propagation des associations pour les transformations :
L'exemple donné est makeScale
# ======================================================= make_grid def make_grid (doc) : ori = doc.addVertex ( 0, 0, 0) vz = doc.addVector ( 0, 0, 1) vx = doc.addVector ( 1 ,0, 0)
dr = 1 da = 360 dl = 1 nr = 1 na = 6 nl = 1 grid = doc.makeCylindrical (ori, vx,vz, dr,da,dl, nr,na,nl, False) return grid
# ======================================================= test_scale def test_scale () :
doc = hexablock.addDocument() grid = make_grid (doc)
dest = doc.addVertex (15, 0, 0) grid2 = doc.makeScale (grid, dest, 0.5) return doc |
ss
Propagation des associations pour la fonction prismQuads
Pour la fonction prismQuads :
# ========================================================== test_prism def test_prism () : doc = hexablock.addDocument() nr = 1 na = 6 nl = 1 grid = make_grid (doc, nr, na, nl)
liste = [ ] for nx in range (nr) : for ny in range (na) : cell = grid.getQuadIJ (nx, ny, nl) liste.append (cell);
axis = doc.addVector (1, 1, 1); bloc = doc.prismQuads (liste, axis, 3) return doc |
Propagation des associations pour la fonction revolutionQuads
Pour la fonction revolutionQuads
# ========================================================== test_revolution def test_revolution () : doc = hexablock.addDocument() nr = 1 na = 6 nl = 1 grid = make_grid (doc, nr, na, nl)
liste = [ ] for nx in range (nr) : for ny in range (na) : cell = grid.getQuadIJ (nx, ny, nl) print " ... cell = ", cell liste.append (cell); ss
center = doc.addVertex (0, -10, 0); axis = doc.addVector (1, 0, 0); angles = [5, 10, 15, 20, 30, 20, 15, 10, 5 ] ss bloc = doc.revolutionQuads (liste, center, axis, angles); return doc
|
Retour au début