Compare Match Geometry of Solids

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:

2 Likes

Hi @dkeinys

To classify by typology (shape) and type of material (face properties), it is preferable to process Revit elements directly.

group by topology and mat

Code Python (all engines)
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
1 Like

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 Like

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

1 Like

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.