29 janv. 2020

[Revit] Familles et formules paramétrique 1/2




    1. Échapper un caractère spécial contenu dans un nom de paramètre

      
Il se peut qu'un caractère spécial soit inclut dans le nom d'un paramètre dans ce cas il faut utiliser les crochets [ ]
 Exemple : 

    • Paramètre oui-non : Alim Saillie/Encastrée
    • exemple formule associée : if([Alim Saillie/Encastrée], 0 m, 0.035 m)
Note :
Si vous avez pour habitude de "piloter" vos paramètres par programmation,  dans la mesure du possible éviter les caractères spéciaux, idem pour les caractères accentués (penser à l'encoding)


    2. Les Maths

Concernant l'utilisation de fonctions mathématiques elles sont similaires à  ceux que nous pouvons rencontrer en programmation




Syntaxe de fonctionDescriptionExemples
+Addition, additionnez des valeursLongueur totale = Hauteur + Largeur
-Soustraction, recherchez la différence entre des valeursVolume supprimé = Volume A - Volume B
*MultiplicationAire = Hauteur * Largeur
/DivisionMi-longueur = Longueur / 2
^Exponentiation, X élevé à la puissance YHauteur ^ 2
logLogarithme, l'exposant de la puissance à laquelle un nombre de base doit être élevé pour qu'il soit égal à un nombre donné.2 = log10 100
In(x)Logarithme népérien, le logarithme d'un nombre sur la base de la constante mathématique e.ln(x*y) = ln*x + ln*y
sqrt(x)Racine carrée4 = sqrt(16)
sin(x)SinusAvec des valeurs c et A connues, a = c * sin(A)
cos(x)CosinusAvec des valeurs c et A connues, b = c * cos(A)
tan(x)TangenteAvec des valeurs a et B connues, b = a * tan(B)
asin(x)Arc sinusAvec des valeurs a et c connues, A = asin(a/c)
acos(x)Arc cosinusAvec des valeurs a et c connues, B = acos(a/c)
atan(x)Arc tangenteAvec des valeurs a et b connues, A = atan(a/b)
exp(x)Constante mathématique e élevée à la puissance x.exp(3)
abs(x)Valeur absolue2 = abs(-2)
piRapport de la circonférence d'un cercle à son diamètreAire du cercle = pi * r^2
round(x)La fonction d'arrondi renvoie une valeur arrondie à l'entier le plus proche. Elle ne prend pas en compte la direction de l'arrondi.
round(3.1) = 3
round(3.5) = 4
round(-3.7) = -4
roundup(x)La fonction d'arrondi par excès renvoie une valeur arrondie à la valeur entière supérieure ou égale à x.
roundup(3) = 3
roundup(3.1) = 4
roundup(-3.7) = -3
rounddown(x)La fonction d'arrondi par défaut renvoie une valeur arrondie à la valeur entière inférieure ou égale à x.
rounddown(3) = 3
rounddown(3.7) = 3
rounddown(-3.7) = -4


     3. Les Opérateurs et les formules conditionnelles

La structure est similaire à celle que nous pouvons utiliser sous Excel.
  • Règle n°1
Le type de paramètre conditionne la formule. Inutile d'essayer de retourner un booléen à un paramètre qui attend une longueur 
  • Règle n°2
Les unités doivent être compatibles entre elles.
Cependant même si elles sont compatibles, attention tout de même à ne pas additionner n'importe quoi ...
Le scandale du théorème de Boucherot 
  • Règle n°3
À l'instar d'un appel de fonction suivi d'arguments, les opérateurs se mettent en préfixe suivis de parenthèses englobant la condition et/ou les valeurs.
  • Règle n°4
À ce jour Revit ne permet pas d’évaluer les valeurs des paramètres de type Texte, si vous y tenez, passez par une table de consultation 

Quand Revit nous envoi booler...



Pour les tests logiques les opérateurs supportés sont:
Opérateur Logique   Description
<  strictement inférieur
>strictement supérieur
=  égal
andretourne Vrai si toutes les valeurs sont Vrai 
orretourne Vrai si une des valeurs est Vrai
notretourne l'inverse de la valeur booléenne (ou du résultat de l'évaluation booléenne)
not(a > b) équivalent à : a <= b
not(a < b)équivalent à : a >= b


Utilisation de l'Opérateur AND
Exemple:
x = 5
y = 4
and(x = 5, y = 5) retourne Faux (False)


Utilisation de l'Opérateur OR
Exemple :
x = 5
y = 4
or(x = 5, y = 5) retourne Vrai (True)


Utilisation de l'Opérateur NOT
Exemple :
x = 5
not(x = 6) retourne Vrai (True), x = 6 étant Faux


      4. Les Syntaxes des Formules 

  • La syntaxe de base est la suivante :
 if (test logique, valeurA si vrai, valeurB si fausse)retourne valeurA ou valeurB
   
  • Test logique avec AND
 if (and(LongA = 10, LongB = 10), valeurA, valeurB)- retourne valeurA ou valeurB
 - Paramètre de Type Longueur

L'équivalent Pseudo-Code à titre de comparaison

   
SI LongA = 10 ET LongB = 10:
    retourne valeurA
SINON:
    retourne valeurB

  • Test logique avec OR
 if (or(LongA = 10, LongB = 10), valeurA , valeurB )- retourne valeurA ou valeurB
 - Paramètre de Type Longueur

Pseudo-Code
   
SI LongA = 10 OU LongB = 10:
    retourne valeurA
SINON:
    retourne valeurB

  • Test logique avec multiple conditions
La syntaxe est la suivante : 
 if (test logique1, valeurA si vrai, (test logique2 si faux, valeurB si vrai, valeurC si vrai))retourne valeurA ou valeurB ou valeurC

Prenons pour l'exemple 3 paramètres de longueur, nous cherchons à retourner la valeur max dans un nouveau paramètre "MaxLong:
LongA = 50
LongB = 40
LongC = 30

Pour simplifier la compréhension commençons par l’équivalent avec un Pseudo-Code

Pseudo-Code
  
SI LongA > LongB and LongA > LongC:
     MaxLong = LongA  
SINON SI LongB > LongA and LongB > LongC: 
    MaxLong = LongB
SINON: 
    MaxLong = LongC   


 l'expression sous Revit devient la suivante :

 if (and(LongA > LongB, LongA > LongC), LongA , if(and(LongB > LongA, LongB > LongC), LongB, LongC ))- retourne LongA ou LongB ou
LongC
 - Paramètre de Type Longueur

On peut comme cela imbriquer autant de conditions que nécessaire...dans la limite du raisonnable

Moralité (ou conseil): si vous n'êtes pas à l'aise avec les formules Revit commencer par écrire la formule sous forme Pythonesque ou Pseudo-Code ou autre



À suivre les formules de recherche dans les tables de consultation...


10 janv. 2020

[Revit] Interopérabilité Excel







Analyses, modifications,  import de données, transfert vers une application tierce, transmissions de quantitatifs, etc..., dans l'écosystème du BIM, l'utilisation d'Excel devient incontournable.

Le plus souvent la nécessité d'exporter vers Excel provient des Nomenclatures, plusieurs méthodes pour y parvenir.

  • Avec l'export natif de Revit 

Après export de rapport de nomenclatures au format texte avec choix du délimiteur, il suffit de d'importer le fichier txt en tant que données depuis Excel ou de changer directement l'extension "txt" en "csv" puis de l'ouvrir avec Excel.


  • Avec des Add-Ins
Ce n'est pas le choix qui manque, exemple sur l'AutodeskAppStore

voici mes préférés (gratuit) :
    • Import/Export Excel de BIM One (export - import de nomenclatures, modification possible des paramètres d’Occurrence)
    •  SheetLink de Diroots  (export - import de nomenclatures et bien plus, modification possible des paramètres d’Occurrence et de Type avec précautions)

  • Avec Dynamo 

    • avec ses 2 nœuds natifs 



  • Par programmation avec les API(s) 

    • Pour des besoins très spécifiques, on peut également personnaliser ses propres exports avec les API de Revit et de Microsoft, par exemple pour exporter des données spécifiques ou pour personnaliser la mise en page des données sur Excel.
Les données des Nomenclatures peuvent être récupérées au travers de diverses Classes comme TableData ou TableView
Pour rappel les nomenclatures sont des vues... on peut donc parcourir les éléments via la méthode 
FilteredElementCollector

    Voici un exemple d'utilisation de l'API de Microsoft pour exporter les paramètres de fichiers Familles Revit contenu dans un dossier.
    Ici les données sont stockées dans un Array puis transférées dans un Range  via la variable Value2 (sauf pour les couleurs de cellules 😕...y'en a qui ont essayé ils ont eu des problèmes 😅... bref, obligé de passer une boucle)

    Plus d'informations ici 
      gist_export_Excel
      
      # Copyright (c) 20119- POUPIN.C
      import clr
      import re
      clr.AddReference('ProtoGeometry')
      from Autodesk.DesignScript.Geometry import *
      clr.AddReference('RevitAPI')
      import Autodesk
      from Autodesk.Revit.DB import *
      clr.AddReference('RevitServices')
      import RevitServices
      from RevitServices.Persistence import DocumentManager
      from RevitServices.Transactions import TransactionManager
      app = DocumentManager.Instance.CurrentUIApplication.Application
      
      clr.AddReference('System.Drawing')
      from System import Array
      from System.Drawing import *
      
      clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c')
      from Microsoft.Office.Interop import Excel
      from System.Runtime.InteropServices import Marshal
      import sys
      sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
      import os
              
      class ParameterProp():
          def __init__(self, para):
              self._para = para
              self._pname = para.Definition.Name
              self._pisinstance = para.IsInstance
          
      class FamiliesCheck():
          def __init__(self, dirpath):
              self.dirpath = dirpath
              self.full_lstP = [] 
              for dir, subdir, files in os.walk(dirpath):
                  for file in files:
                      if file.endswith(".rfa") and re.search(r'\.\d+.rfa$', file) is None and re.search(r'[aA]ncien', dir) is None:
                          famDoc = app.OpenDocumentFile(dir + '\\' + file)
                          if famDoc.IsFamilyDocument:
                              famManager = famDoc.FamilyManager
                              lst_para = [ ParameterProp(x) for x in famManager.Parameters]
                              lst_para.sort(key = lambda x : x._pname)
                              lst_para[0:0] = [file]
                              self.full_lstP.append(lst_para)             
                          famDoc.Close(False)     
          
          def XlsWriter(self):
              ex = Excel.ApplicationClass()
              ex.Visible = True
              ex.DisplayAlerts = False
              workbook = ex.Workbooks.Add()
              workbook.SaveAs(self.dirpath + "\\reportFamilies.xlsx")
              ws = workbook.Worksheets[1]     
              nbr_row = 1
              for sub_lst in self.full_lstP:
                  nbr_col = len(sub_lst)
                  xlrange  = ws.Range[ws.Cells(nbr_row, 1), ws.Cells(nbr_row, len(sub_lst))]
                  a = Array.CreateInstance(object, 1, len(sub_lst))
                  b = Array.CreateInstance(object, 1, len(sub_lst))
                  for index, i in enumerate(sub_lst):
                      # if i is string
                      if isinstance(i, str):
                          a[0,index] = i
                      #else i is a ParameterProp objet    
                      else:
                          a[0,index] = i._pname
                          if i._pisinstance == False:
                              b[0,index] = 6
                          elif i._pisinstance == True:
                              b[0,index] = 8      
                          else:
                              b[0,index] = None
                  #copy Array in range            
                  xlrange.Value2 = a  
                  for cell, color_index in zip(xlrange, b):
                      cell.Interior.ColorIndex = color_index
                  nbr_row += 1
              used_range = ws.UsedRange   
              for column in used_range.Columns:
                  column.AutoFit()
              workbook.Save() 
                  
      dirpath = IN[0]
      obj_check = FamiliesCheck(dirpath)
      obj_check.XlsWriter()
      OUT = obj_check.full_lstP
      
      Aperçu en Vidéo avec quelques lignes supplémentaire


      un 2ᵉ exemple ou l'on exporte des preview des familles vers Excel
      (avec les méthodes Clipboard  et Paste )
      
      # coding: utf-8 
      import clr
      clr.AddReference('ProtoGeometry')
      from Autodesk.DesignScript.Geometry import *
      
      clr.AddReference('RevitAPI')
      import Autodesk
      from Autodesk.Revit.DB import *
      
      clr.AddReference('RevitServices')
      import RevitServices
      from RevitServices.Persistence import DocumentManager
      
      doc = DocumentManager.Instance.CurrentDBDocument
      uiapp = DocumentManager.Instance.CurrentUIApplication
      app = uiapp.Application
      uidoc = uiapp.ActiveUIDocument
      
      
      import System
      from System import Environment
      from System import Array
      from System.Collections.Generic import *
      
      clr.AddReference('System.Drawing')
      import System.Drawing
      from System.Drawing import *
      clr.AddReference('System.Windows.Forms')
      from System.Windows.Forms import Clipboard
      
      
      clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' )
      from Microsoft.Office.Interop import Excel
      from System.Runtime.InteropServices import Marshal
      
      
      class ExcelUtils():
          def __init__(self, expSettings, filepath, sizebitmap):
              self.expSettings = expSettings
              self.filepath = filepath
              self.sizebitmap = sizebitmap
              print self.filepath
              
              
          def exportXls(self):
              
              ex = Excel.ApplicationClass()
              ex.Visible = True
              ex.DisplayAlerts = False
              workbook = ex.Workbooks.Add()
              workbook.SaveAs(self.filepath)
              ws = workbook.Worksheets[1] 
              nbr_row = len(self.expSettings)
              nbr_colum = len(self.expSettings[0])
              #
              xlrange  = ws.Range[ws.Cells(1, 2), ws.Cells(nbr_row, nbr_colum )]
              xlrange.EntireRow.RowHeight  = self.sizebitmap.Height
              ws.Range("a1").EntireColumn.ColumnWidth = self.sizebitmap.Width / 4
              a = Array.CreateInstance(object, nbr_row, nbr_colum - 1)
              #
              for indexR, row in enumerate(self.expSettings):
                  for indexC , value in  enumerate(row):
                      if indexC == 0 and value is not None:
                          Clipboard.SetDataObject(value)
                          rng  = ws.Range[ws.Cells(indexR + 1, 1), ws.Cells(indexR + 1 , 1)]
                          ws.Paste(rng, False)
                          
                      else:
                          a[indexR, indexC - 1] = value
                      
              #copy Array in range            
              xlrange.Value2 = a      
              used_range = ws.UsedRange   
              for column in used_range.Columns:
                  column.AutoFit()
                  
      toList = lambda x : x if hasattr(x, '__iter__') else [x]        
      listcat = toList(UnwrapElement(IN[0]))          
      #make filter            
      lstbipCat = [System.Enum.ToObject(BuiltInCategory, x.Id.IntegerValue) for x in listcat]         
      filtercat = ElementMulticategoryFilter(List[BuiltInCategory](lstbipCat))
      #collector
      fecSymb = FilteredElementCollector(doc).WherePasses(filtercat).WhereElementIsElementType().ToElements()
      outdata = []
      imgSize = Size( 100, 100 )
      for symb in fecSymb:
          bitm = symb.GetPreviewImage(imgSize) 
          famName = symb.Family.Name
          symbName = Element.Name.GetValue(symb)
          outdata.append([bitm, famName, symbName])
          
      #define folder to export        
      directory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
      
      objXls = ExcelUtils(outdata, directory + '\\test.xls', imgSize)
      objXls.exportXls()
      
      OUT = directory  
      

       Bonne Année et Meilleurs vœux