Polylines to Revit

Got some complex curves I want to get into Revit as model lines. modelcurve.bycurve isnt working great. Ideally would want them in as one line or splines.

1 Like

Still going @vanman :sweat_smile:
Are these the topo-polycurves? What does the error say?

Yep looks like you’re using the wrong boolean mask output, out list.

I do this all the time.

Edit: Nevermind, misunderstood the algorithm. That seems a fine approach if you decnonstruct into curves, maybe consider Python for better flow/error catch control.

1 Like

haha still working on the best solution. What a nightmare :rofl: Thinking a fast processing less detailed script and a more detailed slower script. But! The error is below. Thought I captured it in the screenshot.

Maybe I just bake a bunch of lines in and be done with it :sweat_smile:

I also forgot the Polycurve.curves node before the second modelcurve.bycurve node. But alas one line baked in would be good

Also tried extruding every line as a cylinder and direct shape but that failed

1 Like

Took away the initial build to PolyCurve and replaced it with NurbsCurve.
Also, pre-filtered the triangulation intersect to ignore facets outside the target plane before calculating intersects. :sunglasses: Seems to work, but give it a go in your environment.
Took 25 seconds to run. :clock3:
TessellatedShapeBuilder for Complex Floors 5.dyn (45.6 KB)

3 Likes

Awesome works great. Thanks so much. Is a tessellated shape a mesh? Is it possible to have a mesh as an input for it so I can tinker more with stuff? Figured out quick ways to get detailed meshes of weird floors. Trying to figure out how to get top surfaces of meshes but doesn’t seem to be as much flexibility with meshes then poly surfaces.

I may have to turn the nurbs into a poly and back to offset it slightly away from the shape. Sometimes its getting caught inside the floor

Generally yes it will be a Mesh, this is due to the Triangulation of faces.
Can be simplified by merging co-planar faces to create a simplified Solid, but you probably don’t need that for the majority of cases.

As for top surface identification. Taking a reference point on the Nurbs and using a ReferenceIntersector to check whats above (with a slight tolerance) should tell you which ones are on the top. :wink:

    # Find only top lines
    filtered_top = []
    for c,s in zip(converted_poly,start_pts):
        # Ref Pt
        base_pt = s.ToXyz()
        ref_pt = XYZ(base_pt[0],base_pt[1],base_pt[2]+0.005)
        # Ref Targets
        cat_list = [BuiltInCategory.OST_Floors]
        typed_list = System.Collections.Generic.List[BuiltInCategory](cat_list)
        cat = ElementMulticategoryFilter(typed_list)
        refInter = ReferenceIntersector(cat, FindReferenceTarget.Face, doc.ActiveView)
        refInter.FindReferencesInRevitLinks = True
        refContext = refInter.Find(ref_pt, XYZ(0,0,1))
        inter = [c.GetReference() for c in refContext]
        if inter ==[]:
            filtered_top.append(c)    

TessellatedShapeBuilder for Complex Floors 6.dyn (38.8 KB)

If the initial element is a floor you can use the HostObjectUtils class to pull the top faces: GetTopFaces Method

1 Like

Will this work with weird faces?

Should - but I don’t see an RVT file so I can’t confirm anything just yet.

I’ve been looking for nodes or code progressed enough I can manipulate to use the method your talking about with my skill level. I’m using rhythms object host top face for a script placing trees on floors, if that’s the same method. However it started failing with more complex geometry. Having a stable method to get top faces of floors to intersect with for points or other things like planes would be amazing!

There’s a Revit file here with a curious shape if you wanted to look.

Looking over that file…

Well I’ll start by saying anyone who’d build a floor shaped like this should be charged with crimes against humanity.

Next: this warning is likely cluing you into the issue - if you remove it you’ll get better results, but that would require less significant shape edits so I get that it likely isn’t an option.
image

So, teaching a @vanman to fish here:

  1. Start by unwrapping the element as yourRevitElement.
  2. Initiate the method call by typing the class (HostObjectUtils) followed by a dot and the method (GetTopFaces) followed by the inputs in parenthesis (yourRevitElement). Net line is topFaces = HostObjectUtils.GetTopFaces(yourRevitElement).
  3. If you output that you’ll see you have a list of references, each of which can be converted to a GeometryObject via the GetGeometryObjectFromReference method, which is called by taking the instance of the element class you want to pull the geometry from (yourRevitElement) followed by a . to indicate the method/function and then adding the call GetGeometryObjectFromReference and putting your reference in parenthesis after it. however you have a list of references so you’ll have to iterate over them, which means doing it all in a for loop or list comprehension (I prefer the later). Looks something like this: geometryObjects = [yourRevitElement.GetGeometryObjectFromReference(ref) for ref in topFaces]

From there we now have to convert the geometryObjects to Dynamo ones. Most of your samples pass a ToProtoType method without issue, but some fail specifically the final ruled loft in the case of the file you linked. So we have to work around that. One way to do this is to tessellate the shape and work with the mesh… This is what Ewan’s solution in the other thread is doing (well a version thereof anyway). It’s more involved but that can work too. The problem with it is that the number of faces produced will be extreme - you go from 5 faces in the origin list of top surfaces to something like 2654 surfaces, and so the polysurface will be VERY slow to build and slow to work with. You can also try converting to a mesh instead of a PolySurface (but that might not suit your needs).

So instead I wrote some code to convert a ruled loft (type of nurbs surface) to a Dynamo NurbsSurface which will enable you to rebuild the failing ruled loft face as native Dynamo geometry from the underlaying data in Revit… effectively doing all the work in a heartbeat. It’s pretty quick too - the test file you provided runs between 60ms and 80ms on my machine.

There might still be edge cases of floors not accounted for, so I have some additional error handling. if you come across anything post it back and we can try to build this up to something even more robust (and perhaps get it into Dynamo for Revit).

The Python code
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Get the top polysurface for floors containing a ruled loft as a result of shape editing"
__RevitBuilds__ = "2023.1"
__DynamoBuilds__ = "2.16"
__ReleaseNotes__ = "Not really something I have tested at scale - use with caution"
__Dependancies__ = "none"
__Copyright__ = "2024, Autodesk Inc"
__License__ = "Apache 3"



########################################
### Configure the Python environment ###
########################################
### standard 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

### basic Dynamo imports ###
clr.AddReference('ProtoGeometry') #add the Dynamo geometry library to the CLR
from Autodesk.DesignScript import Geometry as DG #add the Dynamo geometry class using the alias DG, to ensure no overlap between class calls in Revit or Dynamo

### Dynamo Revit imports ###
clr.AddReference("RevitNodes") #add Dynamo's Revit nodes library to the clr
import Revit #import Dynamo's Revit node class
clr.ImportExtensions(Revit.Elements) #add the element conversion methods to the CLR
clr.ImportExtensions(Revit.GeometryConversion) #add the geometry conversion methods to the CLR

### 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 * #import every class of the Revit API to the Python environment



#########################################
########### beginning of code ###########
#########################################
### imports and unwrapping ###
elem = UnwrapElement(IN[0]) #import the elements from IN[0] of the Dynamo environment and convert to native Revit elements
topFaces = HostObjectUtils.GetTopFaces(elem) #get the top faces
surfs = [elem.GetGeometryObjectFromReference(i) for i in topFaces] #get the geometry object from the top faces
### output list for results and failures
results = []
failures = []

#extract the geometry as Dynamo geometry elements
for srf in surfs: #for every surface in the list of top face surfaces
    try: #try
        dynamoSurfs = srf.ToProtoType() #convert the Revit surface to a list of Dynamo surfaces
        [results.append(s) for s in dynamoSurfs] #append the surfaces to the list of results
    except: #if that didn't work
        try: #try
            srf = srf.GetSurface() #get the revit face as a surface
            nurbsdata = ExportUtils.GetNurbsSurfaceDataForSurface(srf) # get the nurbs curve data from the surface
            verts = [[p.ToPoint() for p in nurbsdata.GetControlPoints()]] #get the verticies as a nested list
            weights = [nurbsdata.GetWeights()] #get the weights as a nested list
            uKnots = nurbsdata.GetKnotsU() #get the uKnots as a list
            vKnots = nurbsdata.GetKnotsV() #get the vKnots as a list
            uDegree = nurbsdata.DegreeU #get the u degree asa n integer
            vDegree = nurbsdata.DegreeV #get the v degree as an integer
            dynamoSurf = DG.NurbsSurface.ByControlPointsWeightsKnots(verts, weights, uKnots, vKnots, uDegree, vDegree) #build the Dynamo nurbs surface
            results.append(dynamoSurf) #append the surface to the list of results
        except: #if that also didn't work
            import traceback #import traceback
            failures.append([srf, traceback.format_exc()]) #append the surface and the exception to the list of failures

OUT = [DG.PolySurface.ByJoinedSurfaces(results), failures] #convert the list of results to a polysurface and return it and the list of failures to the Dynamo environment
2 Likes

Thanks so much. Appreciate the insight too. Got the perfect landscape job to test it on.

:rofl: landscape does some things that really go against Revit with the creativity so I made something to try encompass everything.

1 Like

Any chance it can accept a flattened list if thats the issue here? Worked a charm on that floor

holy moly that worked good with the contours on floors! Should be awesome for intersecting lines to place trees all over the show. I guess I better figure out how to make a package and get it in there to accept listing. I’ve tried before but weird things happened :no_mouth:

Choose your own adventure:

  1. Make a custom node and deploy it individually
  2. Make a package with the custom node in it and deploy it locally or online
  3. Edit the Python (28 minute mark) to iterate over the elements
  4. Use Dynamo player and hit run many, many, many times.
2 Likes

Thank you. Hopefully this helps currently pulling my hair out as nodes arnt turning up under my custom package name :upside_down_face:

How do I dissociate these nodes from this old package when I was toying around and put in this new package. The surface node I made from your code is coming up in its own category but want them all under Vanning package… Tried save as, moving to another folder to create package from but doesn’t want to load up. Seems the node category doesn’t change when created with a new package?

whats this mean too :slight_smile:

Keep that checked typically, but much of this is stuff I won’t be able to review until later tomorrow (or very late for you I think) as I don’t know it offhand.