Finding intersections points between pipes and walls

Hello everyone,

I’m trying to find the intersection point when my pipes clashes walls.

I’m working on an empty file wich has in link the pipes model and walls model.

I write this script, but the intersection points I get are not the ones I expect. Is anyone see where i am wrong ?

I’m using the ElementIntersectsElementFilter to see which walls intersect the different pipes. Then by getting their center line, I get the intersection point with basics geometrical opérations but it doesn’t seems to wrok.

Just for you to know, I’m not using Dynamo’s functions cause I want to develop it in C# as a plugin and I’m only writing it in dynamo to control the geometry with a conversion from the revit geometry to the dynamo one.

here is the test Model I used :

Thanks in advance for your help.

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference("RevitAPI");
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Plumbing import *
from Autodesk.Revit.Creation import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('DSCoreNodes')
#from DSCore import Math
import math

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

import DSCore
from DSCore import *

import Autodesk

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication

wall_lnk = IN[0]
pipe_lnk = IN[1]

walls = FilteredElementCollector(wall_lnk).OfClass(Wall)

pipes = FilteredElementCollector(pipe_lnk).OfClass(Pipe)

# géométrie 

walls_UW = UnwrapElement(walls.ToElements())

opt = Options()
geo = []

for i in range(len( walls_UW )):
	geo.append(list(walls_UW[i].get_Geometry(opt))[0].ToProtoType())

# --------------------- CANA --------------------

pipes_UW = UnwrapElement(pipes.ToElements())

# Lignes d'axes canalisations
licana = [i.Location.Curve.ToProtoType() for i in pipes_UW]

# Vecteur directionnel
startpt = [i.StartPoint for i in licana]
endpt = [i.EndPoint for i in licana]
vector = [Vector.ByTwoPoints(x,y) for x,y in zip(startpt,endpt)]

# Angle des canalisations par rapport à Z
angle = [Vector.AngleWithVector(x, Vector.ZAxis() ) for x in vector]

#filtres canalisations verticales
vert = [True if -0.001<an<0.001 or 179.99<an<180.001 else False for an in angle]

pipes_nv = List.FilterByBoolMask(pipes_UW, vert)["out"]

# Lignes des canalisations 
cu =[]	
p1 = []
p2 = []
d = []
for i in pipes_nv:
	sp = i.Location.Curve.GetEndPoint(0)
	sp2 = XYZ(sp.X, sp.Y, 0)
	p1.append(sp2)
	ep = i.Location.Curve.GetEndPoint(1)
	ep2 = XYZ(ep.X, ep.Y, 0)
	p2.append(ep2)
	cu.append(Line.CreateBound(sp2, ep2).ToProtoType())
	
#Coeff droite cana	
a1 = []
for i, j in zip(p1, p2):
	a1.append( (j.Y - i.Y) / (j.X - i.X) )
	
#calcul de b pour cana 
b1 = []
for i, j in zip(p1, a1):
	b1.append( i.Y - (j * i.X ) ) 
	
# ---------------- INTERSECTION ----------------

inter = []
for i in pipes_nv:
	inter.append(FilteredElementCollector(wall_lnk).OfClass(Wall).WherePasses(ElementIntersectsElementFilter(i)).ToElements())
	
	

# ------------------ MURS ----------------------


# Lignes des murs
line_wall = []
p3 = []
p4 = []
for j in inter:
	p3_temp = []
	p4_temp = []
	line_temp = []
	for i in j:
		line_temp.append(i.Location.Curve.ToProtoType())
		p3b = i.Location.Curve.GetEndPoint(0)
		p3_temp.append( XYZ(p3b.X, p3b.Y, 0) )
		p4b = i.Location.Curve.GetEndPoint(1)
		p4_temp.append( XYZ(p4b.X, p4b.Y, 0) )
	line_wall.append(line_temp)
	p3.append(p3_temp)
	p4.append(p4_temp)
	
#Coeff droite murs	
a2 = []
for f, g in zip(p3, p4):
	a2_temp = []
	for i, j in zip(f, g):
		a2_temp.append( (j.Y - i.Y) / (j.X - i.X) )
	a2.append(a2_temp)
	
#calcul de b pour murs 
b2 = []
for f, g in zip(p3, a2):
	b2_temp= []
	for i, j in zip(f, g):
		b2_temp.append( i.Y - (j * i.X ) ) 
	b2.append(b2_temp)


# -------------------- POINT INTER -------------------

# Calcul de x
x = []
for i, j in zip(a2, b2):
	x_temp = []
	for m, n, o, p in zip(i, j, a1, b1):
		x_temp.append( ( p - n) / ( m - o) )
	x.append(x_temp)

# Calcul de y 
y = []
for i, j, k in zip(x, a2, b2):
	y_temp = []
	for m, n, o in zip(i, j, k):
		y_temp.append( ( n * m ) + o )
	y.append(y_temp)
		
# point d'intersection
pti = []
for i, j in zip(x, y):
	pti_temp = []
	for a, b in zip(i, j):
		pti_temp.append( XYZ(a, b, 0).ToPoint() )
	pti.append(pti_temp)		

		
OUT = pti, cu, line_wall

Most likely your link is moved and Revit API only returns element geometry from links in its origin to origin location, so you’ll need to use the link transform to transform the geometry.

hello @Thomas_Mahon,
thank you for your answer.
But I’m using the revit link document to use the collector and the GetTransform works with the RevitLinkInstance, so how do I use it ?

Are the walls and pipes inside the same link?

No, walls are in one link and pipes in an other one

And are the links moved/aligned in your active file (i.e. not in their origin-to-origin locations)? If your not sure, the transforms of the link instances should be equal to the identity transform; if not then the answer is no.

I see where to find the transform of the link instances but I dont get what u mean by the identity transform

Get the total transform of the link instances and evalaute linkTransform.IsIdentity.

Anyway, assuming either one or both are not in their origin-to-origin locations, when you use the ElementIntersectsElementFilter it will always fail or give unexpected results because the links are in two different locations for the reason I said before; in the RevitAPI context linked elements will only ever be located in their originating files origin-to-origin location.

Instead, you have to extract the solid from the element, transform it to the other links origin-to-origin location then use the ElementIntersectsSolidFilter instead. If both files are in different locations then you need to perform two transforms of the solid: 1 to the location you currently see it in Revit and then another to align in the relative location of the other link. Its not pretty or easy and there isn’t any other way to do it, not unless you just use the sledge-hammer approach and copy the elements into your active document, perform the intersection then maybe rollback the transaction. However, you’ll need to wave goodbye to performance and there will be new challenges to contend with, such as how Revit decides it wants to host the element in the active file when its copied through.

Wow thank you for the explainations.

I evaluate both of my linkinstance and they are both equals to the identity transform. And my ElementIntersectsElementFilter doesn’t fail.

Do I still have to do the double transform method ?

Nope, that’s the ideal and the exception to the rule; if the links are aligned and not transformed then the ElementIntersectsElementFilter will work. You only need to transform if the links are moved from their origin-to-origin locations.

ok I see. Then I think i will still have to do it cause that’s a test model but not all models will be in this ideal situation.

But if in this case the coordonates are not the problem, the mystery of the weird result I get become bigger.

Hello
another way to find intersection on a Wall Face with Intersect Method (Curve, IntersectionResultArray)

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Plumbing import *


clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

lnkInstWall = UnwrapElement(IN[0])
lnkInstPipe = UnwrapElement(IN[1])

docWall = lnkInstWall.GetLinkDocument()
docPipe = lnkInstPipe.GetLinkDocument()

lstWall = FilteredElementCollector(docWall).OfClass(Wall)

lstPipe = FilteredElementCollector(docPipe).OfClass(Pipe)

lstIntersect = []

for pipe in lstPipe:
    curvPip = pipe.Location.Curve
    for wall in lstWall:
        linkrefface = HostObjectUtils.GetSideFaces(wall, ShellLayerType.Interior)
        intFace = wall.GetGeometryObjectFromReference(linkrefface[0])
        interArRef = clr.Reference[IntersectionResultArray]()
        if intFace.Intersect(curvPip, interArRef) == SetComparisonResult.Overlap:
            pointIntersect = interArRef.Value[0].XYZPoint 
            lstIntersect.append([pipe, pointIntersect.ToPoint() ])
            

OUT = lstIntersect

Note: if you want the middle Point Intersection get the second face with HostObjectUtils.GetSideFaces(wall, ShellLayerType.Exterior) then calculate the middle Point with the 2 IntersectionResult

1 Like

wow, thank you so much @c.poupin it works perfectly.

1 Like

It’s more efficient then extracting solids :+1:…but it still won’t work if the links are transformed. There’s no escaping the limitation unfortunately; any approach requires obtaining a geometric represention of the linked element so it can transformed to its host location (where you see it in Revit) to get the correct result.

2 Likes

yeah I see but where do the get transform method should be used in the c.poupin explained ?