Nouvelle étape dans la modélisation de notre PythonPart « Poteau en Béton Armé », regardons aujourd’hui comment configurer les ancrages de notre objet 3D.
Par ancrage je pense tout particulièrement à 2 types :
- la gestion de l’altimétrie ;
- le point d’insertion en vue en plan.
1) Script Interface
-
Altimétrie
Bien plus qu’un simple objet 3D positionné sans intelligence, l’API d’Allplan nous permet d’accrocher automatiquement notre PythonPart aux niveaux présents dans la structure du bâtiment.
Pour cela, nous allons compléter notre variable Hauteur ou « Height » précédemment utilisée ; pour mémoire nous avions mis en place un champ de type « Length », type générique pour saisir une longueur.
En effet, pour nous permettre de définir une référence à un niveau, nous allons d’abord mettre en place la boîte de dialogue utilisée par les éléments d’architecture standards :
A noter :
Il y a 3 façons de définir une référence à un plan :
- saisie du bord supérieur ;
- saisie du bord inférieur ;
- les 2.
Nous allons choisir cette dernière hypothèse.
<Parameter>
<Name>PlaneReferences</Name>
<Text> </Text>
<Value></Value>
<ValueType>PlaneReferences</ValueType>
<ValueDialog>PlaneReferences</ValueDialog>
</Parameter>
Puis, afin d’obtenir une meilleure expérience utilisateur, nous allons lier notre champ Height à ce PlaneReferences par l’ajout d’une nouvelle balise nommée Constraint.
De cette manière Allplan va calculer et afficher automatiquement la hauteur absolue de notre objet.
Voici la syntaxe :
<Parameter>
<Name>ColumnHeight</Name>
<Text>Hauteur</Text>
<FontFaceCode>4</FontFaceCode>
<Value>-1</Value>
<ValueType>Length</ValueType>
<Constraint>PlaneReferences</Constraint>
</Parameter>
<Parameter>
<Name>PlaneReferences</Name>
<Text> </Text>
<Value></Value>
<ValueType>PlaneReferences</ValueType>
<ValueDialog>PlaneReferences</ValueDialog>
<Constraint>ColumnHeight</Constraint>
</Parameter>
A noter : cette balise Constraint a été ajoutée dans les 2 champs afin de créer une liaison bidirectionnelle
Dans Allplan, j’obtiens :
Si je modifie une hauteur comme suit :
la valeur se met à jour :
Inversement, lorsque je change ma valeur dans la palette :
les champs dans la boîte de dialogue sont correctement mis à jour :
-
Point d’Insertion
Dans le premier chapitre de cet exemple, nous avons mis en place un poteau qui pouvait être soit rectangulaire, soit circulaire.
Avec les paramètres mis en place nous obtenons :
En suivant la même logique que pour l’altimétrie, nous allons reproduire le comportement natif d’Allplan avec les éléments d’architecture Poteaux :
Je vais donc créer un nouvel Expander dédié à cette fonction :
<Parameter>
<Name>AttachmentExpander</Name>
<Text>Accrochage</Text>
<ValueType>Expander</ValueType>
</Parameter>
puis j’ajoute les champs suivants :
<Parameter>
<Name>AttachmentPointButtonRow</Name>
<Text>Point d’accrochage</Text>
<ValueType>Row</ValueType>
<Parameter>
<Name>AttachmentPoint</Name>
<Text>Attachment point index</Text>
<Value>5</Value>
<ValueType>RefPointButton</ValueType>
</Parameter>
</Parameter>
A noter :
- je place mon bouton dans un Row et je complète le texte qui s’affichera dans Allplan ;
- avec 5 comme Value par défaut, on propose un ancrage au centre
De retour dans Allplan, j’obtiens :
Voici le fichier complet :
2) Script Principal
Je commence par compléter ma liste d’imports :
import NemAll_Python_ArchElements as ArchElements
-
Fonction create_element
Je me place ensuite dans la fonction create_element pour compléter la liste des informations récupérées :
attach_point = build_ele.AttachmentPoint.value
plane_ref: ArchElements.PlaneReferences = build_ele.PlaneReferences.value
Puis je crée une variable pour récupérer l’arase inférieure de mon objet :
column_bottom = plane_ref.GetAbsBottomElevation()
Cette arase ainsi que la hauteur (pour mémoire column_height) nous permettent de modéliser notre objet.
-
Classe Objects3D
Je vais compléter ma classe mère Objects3D pour intégrer les 3 valeurs d’ancrage (X, Y et Z) :
class Objects3D:
def __init__(self, object_prop, attach_point, column_bottom):
self.object_prop = object_prop
self.geo = None
self.attach_point = attach_point
self.x_offset = 0
self.y_offset = 0
self.z_offset = column_bottom
A noter : je stocke le point d’insertion à part et laisse le décalage en X et Y à 0 par défaut car le calcul va dépendre de la forme du poteau
Je prépare ma future fonction attachment_point pour calculer ce décalage :
def attachment_point(self):pass
-
Classe Cuboid
Je me place dans ma classe enfant Cuboid que je complète :
class Cuboid(Objects3D):
def __init__(self, object_prop, attach_point, column_bottom, column_length, column_thick, column_height):
Objects3D.__init__(self, object_prop, attach_point, column_bottom)
self.column_length = column_length
self.column_thick = column_thick
self.column_height = column_height
self.name_dim = f »{round(column_length / 10)}x{round(column_thick / 10)} »
self.placement_pt = self.attachment_point()
Pour bien comprendre le placement en vue en plan, il faut avoir en tête les valeurs renvoyées par notre AttachmentPoint que je rappelle ici :
Par défaut notre poteau rectangulaire est placé à partir du coin inférieur gauche, donc :
- en X
-
- les valeurs à gauche (1, 4 et 7) sont ignorées ;
- les valeurs au centre (2, 5, 8) donneront un décalage égal à la moitié de la longueur ;
- les valeurs à droite (3, 6, 9) induisent un décalage égal à la longueur.
- en Y
- les valeurs en haut (1, 2 et 3) imposent un décalage égal à la largeur ;
- les valeurs au centre (4, 5, 6) donneront un décalage égal à la moitié de la largeur ;
- les valeurs en bas (7, 8 et 9) sont ignorées.
En Python, je traduis ces données avec les 2 conditions suivantes :
def attachment_point(self):
if self.attach_point in {2, 5, 8}:
self.x_offset = -self.column_length / 2
elif self.attach_point in {3, 6, 9}:
self.x_offset = -self.column_length
if self.attach_point in {1, 2, 3}:
self.y_offset = -self.column_thick
elif self.attach_point in {4, 5, 6}:
self.y_offset = -self.column_thick / 2
placement_pt = Geometry.Point3D(self.x_offset, self.y_offset, self.z_offset)
return placement_pt
A noter : je concatène le résultat en un point 3D qui sera renvoyé par la fonction
-
Classe Cylinder
Même logique que précédemment, mais cette fois-ci nous partons d’un point central.
Nous obtenons donc :
class Cylinder(Objects3D):
def __init__(self, object_prop, attach_point, column_bottom, column_rad, column_height):
Objects3D.__init__(self, object_prop, attach_point, column_bottom)
self.column_radius = column_rad
self.column_height = column_height
self.name_dim = f »Ø{round(column_rad / 10)} »
self.placement_pt = self.attachment_point()
et pour le calcul :
- en X
-
- les valeurs à gauche (1, 4 et 7) donneront un décalage égal au rayon ;
- les valeurs au centre (2, 5, 8) sont ignorées ;
- les valeurs à droite (3, 6, 9) induisent un décalage égal au rayon.
- en Y
- les valeurs en haut (1, 2 et 3) imposent un décalage égal au rayon ;
- les valeurs au centre (4, 5, 6) sont ignorées ;
- les valeurs en bas (7, 8 et 9) donneront un décalage égal au rayon.
Voici la fonction :
def attachment_point(self):
if self.attach_point in {1, 4, 7}:
self.x_offset = self.column_radius
elif self.attach_point in {3, 6, 9}:
self.x_offset = -self.column_radius
if self.attach_point in {1, 2, 3}:
self.y_offset = -self.column_radius
elif self.attach_point in {7, 8, 9}:
self.y_offset = self.column_radius
placement_pt = Geometry.Point3D(self.x_offset, self.y_offset, self.z_offset)
return placement_pt
-
Fonction create_element
De retour dans create_element, je complète l’appel de mes classes avec les nouveaux arguments :
# Create 3D object
if choice == « rectangle »:
my_column = Cuboid(com_prop, attach_point, column_bottom, column_length, column_thickness, column_height)
else:
my_column = Cylinder(com_prop, attach_point, column_bottom, column_radius, column_height)
Ensuite, je vais effecteur une translation globale sur mon PythonPart via une Matrix3D :
# Reference point
placement_mat = Geometry.Matrix3D()
placement_mat.SetTranslation(Geometry.Vector3D(my_column.placement_pt))
# Create the PythonPart
model_ele_list = pyp_util.create_pythonpart(build_ele, placement_mat)
A noter : l’utilitaire create_pythonpart prend comme deuxième argument (optionnel) une matrice de placement utilisée pour la transformation de la géométrie
Notre PythonPart est correctement affecté par ma translation mais il y a un problème : les différentes poignées ne suivent pas.
Nous allons donc modifier ces dernières pour prendre en compte le décalage :
-
Classe Handle
Le principe est très simple, je vais additionner mes points d’accroche au point d’insertion :
class Handle:
def __init__(self, placement_pt, handle_id, handle_point, ref_point, handle_param_data, handle_move_dir, handle_info_text):
self.handle_id = handle_id
self.handle_point = placement_pt + handle_point
self.ref_point = placement_pt + ref_point
self.handle_param_data = handle_param_data
self.handle_move_dir = handle_move_dir
self.handle_info_text = handle_info_text
Je fais bien attention à transmettre le point d’ancrage lors de la construction des poignées pour la forme rectangulaire :
self.handles_prop = [Handle(self.placement_pt,
« ColumnLengthHandle »,
Geometry.Point3D(self.column_length, 0, 0),
Geometry.Point3D(),
« ColumnLength« ,
HandleDirection.X_DIR,
« Longueur »
),
Handle(self.placement_pt,
« ColumnThickHandle »,
Geometry.Point3D(self.column_length, self.column_thick, 0),
Geometry.Point3D(self.column_length, 0, 0),
« ColumnThick« ,
HandleDirection.Y_DIR,
« Largeur »
),
Handle(self.placement_pt,
« ColumnHeightHandle »,
Geometry.Point3D(0, 0, self.column_height),
Geometry.Point3D(),
« ColumnHeight« ,
HandleDirection.Z_DIR,
« Hauteur »
)
]
mais également pour la forme circulaire :
self.handles_prop = [Handle(self.placement_pt,
« ColumnRadiusHandle »,
Geometry.Point3D(self.column_radius, 0, 0),
Geometry.Point3D(),
« ColumnRadius« ,
HandleDirection.X_DIR,
« Rayon »
),
Handle(self.placement_pt,
« ColumnHeightHandle »,
Geometry.Point3D(0, 0, self.column_height),
Geometry.Point3D(),
« ColumnHeight« ,
HandleDirection.Z_DIR,
« Hauteur »
)
]
Voici le fichier complet :
Nous avons vu dans ce chapitre comment mettre en place notre point d’insertion ainsi que l’accrochage aux niveaux, améliorant grandement l’utilisation de notre objet PythonPart.
0 commentaires