Revit.Line intersects Revit.Plane and creates point?

Hello,

actually there is very smooth node, but hard coded…

how do i replicate that via python, so chatGPT is cheatingGPT on me


#functions
def tolist(x):
    if hasattr(x,'__iter__'): return x
    else: return [x]

#🔷 PickObject
ref_picked_object = selection.PickObject(ObjectType.Element)
picked_object = doc.GetElement(ref_picked_object)

# 🧱 get geometry
lstElemNetWork = tolist(picked_object)

lstDS_Geo = []
for e in lstElemNetWork:
	if isinstance(e.Location, LocationCurve):
		rvtCurve = e.Location.Curve
		lstDS_Geo.append(rvtCurve)
	else:
		locPt = e.Location.Point
		connSet = e.MEPModel.ConnectorManager.Connectors
		lstConn = list(connSet)
		if connSet.Size == 2:
			try:
				pline = DB.PolyLine.Create(List[XYZ]([lstConn[0].Origin, locPt, lstConn[1].Origin]))
				lstDS_Geo.append(pline)#ToProtoType())
			except:
				# Unable to create Line. Points are likely coincident
				line = DB.Line.CreateBound(lstConn[0].Origin, lstConn[1].Origin)
				lstDS_Geo.append(line)#ToProtoType())
		else:
			for con in connSet:
				line = DB.Line.CreateBound(locPt, con.Origin)
				lstDS_Geo.append(line)#ToProtoType())



# 🛒 all levels
collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType().ToElements()
# 🎣 BuildingStory
levels = [i for i in collector if i.get_Parameter(BuiltInParameter.LEVEL_IS_BUILDING_STORY).AsInteger() == 1]
# 🏢 elevation
elevations = [i.Elevation for i in levels]
# ⚾ points
points = [XYZ(0, 0, elev) for elev in elevations]
# 🏓 planes
planes = [Plane.CreateByNormalAndOrigin(XYZ.BasisZ, point) for point in points]

# 🤖 from here chatGPT
# Function to find intersection points of a line with planes
def intersect_line_with_planes(line, planes):
    intersection_points = []
    for plane in planes:
        result = plane.Intersect(line)
        if result:
            intersection_points.append(result)
    return intersection_points

# Intersect the selected line with the planes
all_intersections = []
for line in lstDS_Geo:
    if isinstance(line, DB.Line):  # Ensure it's a line and not a PolyLine
        intersections = intersect_line_with_planes(line, planes)
        all_intersections.extend(intersections)

# Output the intersection points
OUT = all_intersections

how can i Intersect Plane vs Line and create point?

GPT is not going to work very well for Revit geometry stuff as it is built to the lowest common denominator of use, and most of the time when a method doesn’t directly exist it hallucinates stuff, and many geometry API methods that are common in other tools don’t exist in the Revit API.

The hard coded node you illustrated uses the Dynamo geometry library, not Revit elements. You could convert the objects to Dynamo geometry and intersect them by calling the Dynamo method.

As the Revit API doesn’t have an intersection method to use here, you have to build your own. One method I like is to build a face/solid and intersect that, as there are Revit API intersection tools for those classes. However for the simple case of a line you can build your own intersection method without too much legwork (curves are more complex). There is a blog post with outlined logic and a C# implementation here: https://www.parametriczoo.com/index.php/2020/03/07/plane-line-intersection-in-revit-api/ .

2 Likes

@jacob.small ,

so in theory it is possible to write (or translate it from C#) to python, yes ?

i need just to make script that takes Revit lines clashes with Revit planes and finally create points. the points later on used for the splitting.

Yes.

1 Like

@jacob.small

a work around would be to create the projected points, based on XY of the pipeCurve Startpoint. and Z i get from the planes(levels) elevations… first i will try to get the realy intersection.

i would say “phantom points” they are duct-taped by XY,Z :wink:

1 Like

Hi Andreas…probably a closest point to could work…but best with intersect if you ask me :wink: :wink:

1 Like

@jacob.small

does it work also with Revit Geometry ?

Not sure I see where you attempted to implement the method I showed before. Ask over there?

Also, you can load the protogeometry and to DS libraries in Python. The base Python template does so.

1 Like

@jacob.small

but how ?


from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import TaskDialog

import Autodesk.DesignScript.Geometry
import DSCore
import Tessellation
import DSOffice

import System
import rpw

https://primer.dynamobim.org/13_Best-Practice/13-3_Scripting-Reference.html

Need to add Dynamo to the Common Language Runtime.

Therefore you need to Import clr (and likely sys, etc. etc.).

1 Like

@jacob.small

thats it looks like right now


import os, sys, datetime
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.DB.Architecture import *


# pyRevit

from pyrevit import forms, revit, script

# .NET Imports
import clr
clr.AddReference('System')
from System.Collections.Generic import List
# List_example = List[ElementId]()

from Autodesk.Revit.UI import TaskDialog

import Autodesk.DesignScript.Geometry
import DSCore
import Tessellation
import DSOffice

import System
import rpw

DesignScript is unloadable

You still aren’t adding stuff to the CLR.

Assuming you haven’t edited your standard Python template:

  1. Open Dynamo
  2. Place a Python node
  3. Copy that code
  4. Try that in your other interpreter
1 Like

Hi,

here, an example using Equations of planes

plane_line_intersection

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


def is_between(line,pt):
    """
    Check if a point lies between the start and end points of a given line.

    Parameters:
    line (Line): The line segment to check.
    pt (Point): The point to check.

    Returns:
    bool: True if the point lies between the start and end points of the line, False otherwise.
    """
    return abs((pt.DistanceTo(line.EndPoint) + pt.DistanceTo(line.StartPoint)) - line.Length) < 0.001
    
def plane_equation_from_points(plane):
    """
    Calculate the equation of a plane given its normal vector and origin point.

    Parameters:
    plane (Plane): The plane whose equation is to be calculated.

    Returns:
    tuple: A tuple containing the coefficients (a, b, c, d) of the plane equation.
    """
    a, b, c = plane.Normal.X, plane.Normal.Y, plane.Normal.Z
    o = plane.Origin
    d = -(a * o.X + b * o.Y + c * o.Z)
    return a, b, c, d

def get_line_plane_intersection(line, plane, projection=False):
    """
    Calculate the intersection point of a line and a plane in 3D space.

    Parameters:
    line (Line): The line segment.
    plane (Plane): The plane.
    projection (bool, optional): If True, return the projection of the intersection point on the plane.
                                  If False, return the intersection point only if it lies on the line segment (default).

    Returns:
    Point: The intersection point or its projection on the plane.
    None: If there is no intersection point.
    """
    a, b, c, d = plane_equation_from_points(plane)
    px, py, pz = line.StartPoint.X, line.StartPoint.Y, line.StartPoint.Z
    qx, qy, qz = line.EndPoint.X, line.EndPoint.Y, line.EndPoint.Z
    t_denom = a * (qx - px) + b * (qy - py) + c * (qz - pz)
    if t_denom == 0:
        return None
    t = - (a * px + b * py + c * pz + d) / t_denom

    inter_pt = Point.ByCoordinates(
                px + t * (qx - px),
                py + t * (qy - py),
                pz + t * (qz - pz))
    if projection:
        return inter_pt
    elif is_between(line, inter_pt) :
        return inter_pt
    else : 
        return None

OUT = get_line_plane_intersection(IN[0], IN[1], IN[2])

2 Likes

Question: Which is faster in first run from open, this or the Geometry.Intersect node? (I’ll post a Revit API only implementation of a plane-line intersection tomorrow)

2 Likes

I think so, but I will do some tests to confirm.

image
@Draxl_Andreas this node use the method from Revit API you referred

1 Like

Isn’t that API method for intersecting curves and curves?

yes You are right it for intresect curve with curve

1 Like

I did a small test intersecting 600 curve elements with a single reference plane 10 times.

The standard Geometry.Intersect node performed quite quickly, returning the results between 123 and 166 ms, with an average of 139.

@c.poupin method returned results between 119ms and 158 ms, with an average of 136. Comparable to the Geometry intersect results.

Both of those methods require converting the Revit Elements to Dynamo geometry, which is included in the time. If geometry conversion is included in Cyril’s Python code things ran slower (though it could have been optimized better), returning results between 126 and 166 ms, with an average of 144ms.

A code block consisting of a.Curve.Intersect(plane.Plane); returned results comparable to the in-python geometry conversion.

But the fastest was to skip the geometry conversion, extract the data, and let Revit’s XYZ class do the heavy lifting. The code below executed between 61 and 91ms, with an average of 72.

Now a few things to note: if you reduce the dataset to say 100 lines, Geometry intersect won the day - consistently outperforming all other options including the Revit API method - the VM is a penalty for small actions, but scales up very well.

This is the direct Revit API intersection method I used.
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "get the intersection of a line and a plane using the Revit API"
__RevitBuilds__ = "2025.1"
__DynamoBuilds__ = "2.18"
__ReleaseNotes__ = "based on the implementation found on ParametricZoo (https://www.parametriczoo.com/index.php/2020/03/07/plane-line-intersection-in-revit-api/), but with an added check to ensure the line crosses the plane"
__Dependancies__ = "none"



########################################
### Configure the Python environment ###
########################################
### standard Python imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries

### Revit API imports ###
clr.AddReference("RevitAPI") #add the Revit API to the CLR
import Autodesk #add the Autodesk class to the Python environment 
from Autodesk.Revit.DB import Line, Plane, XYZ #import the relevant sectiosn of the Revit API to the Python environment



########################################
########### Code starts here ###########
########################################
### Get the line and plane geometry from the elements ###
lines = [UnwrapElement(i).GeometryCurve for i in IN[0]] #get the curves from the lines
plane = UnwrapElement(IN[1]).GetPlane() #get the plane from the reference plane

results = [] #empty list to hold the results

for line in lines: #for each line
    ### check for parallel relationship to the plane, and get the distance from the start and end point to the plane ###
    parallelTest = line.Direction.DotProduct(plane.Normal) #if the dot product of the direction (a normalized vector) and the normal (another normalized vector) is zero, the plane and the line are parallel
    v1 = plane.Origin.Subtract(line.GetEndPoint(0)) #subtract the start point from the origin of the plane
    startTest  = v1.DotProduct(plane.Normal) #get the dot product from the subtracted vector and the normal of the plane. if this is the same sign for the start and end point the line doesn't cross the plane.
    v2 = plane.Origin.Subtract(line.GetEndPoint(1)) #subtract the4 end point from the origin of the plane
    endTest = v2.DotProduct(plane.Normal) #get the dot product from the subtracted vector and the normal of the plane
    if parallelTest < 0.0000001: #line is parallel to the plane 
        if abs(startTest) < 0.0000001: #check if the start point is on the plane - if so the intersection is the line itself
            results.append("Line is on the plane") #append message for user to the result
        else: #if not, the intersection is null
            results.append("Line is parallel to plane") #append message for user to the result
    else: #line isn't parallel to the plane and still may or may not intersect
        startSign = startTest>=0 #get the sign of the start test
        endSign = endTest>=0 #get the sign of the end test
        if startSign == endSign: #if the sign of the start test and end test are the same both on the same side of the plane and the line doesn't intersect thep lane
            results.append("Line does not cross plane") #append message for user to the result
        else: #otherwise we finally have a working intersection result
            param = startTest / parallelTest #get the start test as a percentage of the parallel test - vectors are fun!
            vect = line.Direction.Multiply(param) #get the direction of the line and multiply by the parameter to scale the vector
            pnt = line.GetEndPoint(0).Add(vect) #move the start point by the vector
            results.append(pnt)#append the point to the results



########################################
##### Return the results to Dynamo #####
########################################
OUT = results #return the results to the Dynamo environment
3 Likes

@RMohareb ,

i come unitl here

one pipe(line) intersect with 5 levels (plane)

# -*- coding: utf-8 -*-
__title__ = "Split"
__doc__ = """Date    = 29.12.2022
_____________________________________________________________________
Description:
Code  - Split pipes, ducts, AirSegments,...
_____________________________________________________________________
Author: """

# IMPORTS
#==================================================
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB import XYZ
from Autodesk.Revit.DB.Architecture import Room
from Autodesk.Revit.UI.Selection import ObjectType, PickBoxStyle, Selection

#pyrevit
from pyrevit import forms



# .NET Imports
import clr
clr.AddReference("System")
from System.Collections.Generic import List
from Autodesk.Revit.UI.Selection import Selection, ObjectType



#VARIABLES
#==================================================
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document
selection = uidoc.Selection #type: Selection

#functions
def tolist(x):
    if hasattr(x,'__iter__'): return x
    else: return [x]

#🔷 PickObject
ref_picked_object = selection.PickObject(ObjectType.Element)
picked_object = doc.GetElement(ref_picked_object)

# 🧱 get geometry
lstElemNetWork = tolist(picked_object)

lstDS_Geo = []
lstDS_point = []
for e in lstElemNetWork:
	if isinstance(e.Location, LocationCurve):
		rvtCurve = e.Location.Curve
		lstDS_Geo.append(rvtCurve)
		lstDS_point.append(e.Location.Curve.GetEndPoint(0))
	else:
		locPt = e.Location.Point
		connSet = e.MEPModel.ConnectorManager.Connectors
		lstConn = list(connSet)
		if connSet.Size == 2:
			try:
				pline = DB.PolyLine.Create(List[XYZ]([lstConn[0].Origin, locPt, lstConn[1].Origin]))
				lstDS_Geo.append(pline)#ToProtoType())
			except:
				# Unable to create Line. Points are likely coincident
				line = DB.Line.CreateBound(lstConn[0].Origin, lstConn[1].Origin)
				lstDS_Geo.append(line)#ToProtoType())
		else:
			for con in connSet:
				line = DB.Line.CreateBound(locPt, con.Origin)
				lstDS_Geo.append(line)#ToProtoType())



# 🛒 all levels
collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType().ToElements()
# 🎣 BuildingStory
levels = [i for i in collector if i.get_Parameter(BuiltInParameter.LEVEL_IS_BUILDING_STORY).AsInteger() == 1]
# 🏢 elevation
elevations = [i.Elevation for i in levels]
# ⚾ points to split
points = [XYZ(lstDS_point[0].X, lstDS_point[0].Y, elev) for elev in elevations]

i get the points too

can i continue from here ? i slice the selected pipe by this coordinates ?

1 Like