The two options here are to wrap that all into a function of indent it within a for loop.
If you aren’t familiar with these aspects of Python I have videos on both here. If you’re new to Python use this as a learning opportunity:
The two options here are to wrap that all into a function of indent it within a for loop.
If you aren’t familiar with these aspects of Python I have videos on both here. If you’re new to Python use this as a learning opportunity:
Hi @dkeinys
To classify by typology (shape) and type of material (face properties), it is preferable to process Revit elements directly.
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
class SolidsUtils:
lst_solid = []
def __init__(self, element_object, use_material = False):
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, 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):
volume = self.DS_solid.Volume
if self._use_material:
return "Solid_Repr(Volume = {0:.3f}, Sum_Vector_fromCenter = {1:.3f}, Cross_Dist_Pts = {2:.3f}, MaterialId : {3})".format(volume,
self.Sum_Vector_fromCentroid,
self.Cross_distace_pt,
self.matId_value)
else:
return "Solid_Repr(Volume = {0:.3f}, Sum_Vector_fromCenter = {1:.3f}, Cross_Dist_Pts = {2:.3f})".format(volume,
self.Sum_Vector_fromCentroid,
self.Cross_distace_pt)
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
@classmethod
def GetAllComputePoints(cls):
return [obj.DS_computePoint for obj in cls.lst_solid]
toList = lambda x : x if hasattr(x, '__iter__') else [x]
input_Objs = toList(UnwrapElement(IN[0]))
use_material = IN[1]
for s in input_Objs:
SolidsUtils(element_object = s, use_material = use_material)
result = SolidsUtils.GroupSolids().items()
OUT = result
Maybe this will help:
If we have 2 transformed FamilyInstances, we can reset transforms of their solids (move to 0,0,0 and rest rotation) and compare vertices count positions, maybe?
1- convert solid 1and solid 2 to point cloud
2- use ICP registration between these two pointcloud (solid after convert it to point cloud) to get the transformation matrix
this trasformation matrix is the matrix you need it to transform solid 1 to solid 2
If its parts or solid element you are not be able to identify 0 point for comparing points
Hello,
rvt samples where you can test your methods are attached above in this topic. Please demonstrate positive results if you get it
thanks
PS: method with comparing angle of vectors from @c.poupin is better solution of this moment. I use double matrix combination of vectors length and angles
We just need to be sure if Amount of Vertexes for each solid are the same (circles, arcs could have more vertexes)
Thanks you very much @c.poupin
Problem i have using revit elements is that all Elements are “Directshapes” generated by dynamo.
And dont have all geometry options available.
All im trying to do is get counts for same geometry so then i can produce relevant numbering based on same instances.
Thanks again.