Un exemple de sérialisation pour comparer des éléments par topologie (solides)
#DynamoBIM #Revit #Python #Solid #Serialization #AutodeskExpertElite #AutodeskCommunity
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
Contexte
Nous avons plusieurs Éléments de Fabrication et nous souhaitons les regrouper par leur forme (topologie) sans se préoccuper du nom de leur type.- Pour ce faire, nous allons sérialiser ces solides avec les propriétés suivantes :
- Le Volume
- La somme vectorielle des vecteurs (centroïde → sommets)
- La somme de la distance entre tous les points
- Et Éventuellement le type de matériels
Définition
La sérialisation est un procédé d'entrée-sortie permettant de sauvegarder et recharger l'état d'un objet. Cette fonctionnalité permet de faire abstraction du format de fichier utilisé.
L'état d'un objet correspond à l'ensemble des valeurs de ses champs. Les propriétés sont calculées en fonction des champs, et le code des méthodes ne change pas au cours de l'exécution.
Processus avec Python
SolidsUtils
a pour objectif principal est
d'identifier et de regrouper des solides similaires.
Voici un aperçu des méthodes clés de cette classe :
-
GetSolid_Data
: Cette méthode extrait des données essentielles à partir d'éléments Revit, telles que la géométrie du solide et l'ID du matériau.
-
DS_computePoints
: Une fois les données du solide obtenues, cette méthode relève / calcule des points significatifs sur la plus grande face du solide.
-
Sum_Vector_fromCentroid
: En calculant la somme des vecteurs allant du centroïde du solide aux points sur sa surface, cette méthode offre une mesure de la dispersion géométrique du solide, aidant à caractériser sa forme de manière unique.
-
Cross_distace_pt
: Cette méthode calcule la distance globale entre tous les points significatifs sur la surface du solide.
-
__repr__
: Pour faciliter la sérialisation et la comparaison des solides, cette méthode génère une représentation string d'une instanceSolidsUtils
, à l'aide de la bibliothèque Newtonsoft.Json .
-
__eq__
: Cette méthode (opérateurs de comparaison Python) définit l'égalité entre deux instances deSolidsUtils
basée sur leur représentation sérialisée.
-
GroupSolids
: Finalement, cette méthode de classe regroupe les solides semblables en se basant sur leurs caractéristiques calculées.
Aperçu
code Python
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference('Newtonsoft.Json')
import Newtonsoft.Json
from Newtonsoft.Json import JsonConvert
class SolidsUtils:
lst_solid = []
def __init__(self, element_object):
self.element_object = element_object
self.DS_solid, self.matId_value = self.GetSolid_Data(element_object)
#self._use_material = False if isinstance(element_object, DS.Solid) else use_material
self.__class__.lst_solid.append(self)
self._computePoints = []
def GetSolid_Data(self, elem):
""" get Proto Solid and MaterialId"""
# sub function
def get_Solid_MaterialId(geo_elem):
solid = geo_elem.ToProtoType()
max_face = max(geo_elem.Faces, key = lambda x : x.Area)
matId_value = max_face.MaterialElementId.IntegerValue
return solid, matId_value
# Main Function
if isinstance(elem, DS.Solid):
return elem, matId_value
else:
opt = Options()
for geo in elem.get_Geometry(opt):
if isinstance(geo, DB.Solid) and geo.Volume > 0.1:
return get_Solid_MaterialId(geo)
#
elif isinstance(geo, DB.GeometryInstance) :
for gi in geo.GetInstanceGeometry():
if isinstance(gi, DB.Solid) and gi.Volume > 0.1:
return get_Solid_MaterialId(gi)
return None, -1
@property
def DS_computePoints(self):
""" get all points on largest Face with loops"""
if len(self._computePoints) > 0:
return self._computePoints
#get the max face with loops and points on edges or centerpoints
max_face = max(self.DS_solid.Faces , key = lambda f : (len(f.Loops), f.SurfaceGeometry().Area))
curves = max_face.SurfaceGeometry().PerimeterCurves()
pts_on_max_face = []
for c in curves:
endsPoints = [c.CenterPoint] if isinstance(c, DS.Arc) else [getattr(c, tp) for tp in ['StartPoint', 'EndPoint']]
for pta in endsPoints:
if all(pta.DistanceTo(p) > 0.01 for p in pts_on_max_face ):
pts_on_max_face.append(pta)
# delete points located on 2 aligned curves
self._computePoints = []
for p in pts_on_max_face:
tempNormal = []
for c in curves:
if c.DistanceTo(p) < 0.01:
tempNormal.append(c.NormalAtParameter(0.5))
if len(tempNormal) == 2 and tempNormal[0].Cross(tempNormal[1]).Length < 0.01:
pass
else:
self._computePoints.append(p)
return self._computePoints
@property
def Sum_Vector_fromCentroid(self):
""" compute sum of Vectors: centroid -> points on surface """
centroid = self.DS_solid.Centroid()
sum_vector = Vector.ByCoordinates(0,0,0)
for p in self.DS_computePoints:
v = Vector.ByTwoPoints(centroid, p)
sum_vector = sum_vector.Add(v)
sum_Vector_fromCentroid = sum_vector.Length
return sum_Vector_fromCentroid
@property
def Cross_distace_pt(self):
""" compute global distance between points """
cross_distace_pt = 0.0
for i in self.DS_computePoints:
for j in self.DS_computePoints:
cross_distace_pt += i.DistanceTo(j)
return cross_distace_pt
def __repr__(self):
return JsonConvert.SerializeObject({"Type" : self.__class__.__name__,
"Volume": "{:.3f}".format(self.DS_solid.Volume),
"Sum_Vector_fromCenter" : "{:.3f}".format(self.Sum_Vector_fromCentroid),
"Cross_Dist_Pts" : "{:.3f}".format(self.Cross_distace_pt),
"MaterialId" : self.matId_value})
def __eq__(self, other):
return repr(self) == repr(other)
@classmethod
def GroupSolids(cls):
dictCount = {}
for obj in cls.lst_solid:
if repr(obj) not in dictCount:
dictCount[repr(obj)] = [obj.DS_solid]
else:
dictCount[repr(obj)].append(obj.DS_solid)
return dictCount
toList = lambda x : x if hasattr(x, '__iter__') else [x]
input_Objs = toList(UnwrapElement(IN[0]))
for s in input_Objs:
o = SolidsUtils(element_object = s)
result = SolidsUtils.GroupSolids().items()
OUT = result
...Note :
Pour information, il est possible aussi de sérialiser des solides avec le
nœud (méthode)
Geometry.ToSolidDef
incluant d'autre type de données
comme la localisation des sommets, les courbes des arêtes,
0 commentaires:
Enregistrer un commentaire