Getting Faces and Solids gives errors for Walls and Floors

Hello Dynamo Friends :slight_smile:

When getting faces/solids/geometry of walls and floors i get errors like:

  • trim_with_edge_loops requires all curves to touch surface
  • unable to create line. Points are likely coincident

While i have seen a thread about a workaround for floors some time ago (will look that up), i also need a workaround for walls.

I need solids for intersecting and i need faces for placing families, so i think i really need that wall faces and can not use a workaround of creating a new element etc…

So what are my options?

Intended use:

Place Rebend Connection Family for Stair Landing / Platform - Dynamo (dynamobim.com)

As always, thankful about any advice :slight_smile:

Kind regards :slight_smile:

Without seeing the specific geometry you’re using it’s hard to know what is causing the error, so a work-around is going to be tough.

One thought is to pull the faces directly in the Revit API. That would work 100% of the time, but you’d have to remove all the Dynamo stuff between where you pulled the geometry and the results, and do the entire thing in Python or C# (as a zero touch node). A big lift for sure.

Another thought is to try and pull the faces, get the curve loops, convert those to polycurves, and rebuild things into a Solid or surfaces that way. Again this a big lift, and it would require Python or C#; but it would be closer to core Dynamo processing as the new node would just replace the Element.Geometry which isn’t working.

You could also look at modifying the element to remove the issue; Stuff like removing crazy overlapping wall joins, and the like. This might enable your downstream work, and once that’s done you a re-join the elements. Hard to see the pitfalls without an example in hand.

Can you isolate a failing element in a 3D view and post a view images that so we can at least think up a few root causes?

2 Likes

Hello Jacob,

Thanks for your reply and sorry for not giving more specific information.

Starting with floors here an example that gives errors and one that gives geometry:

I don´t think that will help us at investigating the problem :smiley: It seems to be a random problem…

Some thoughts from me:

For floors i don´t really need the exact geometry and i don´t need the faces because i don´t want to place a family there. There is a element.boundingbox node that will give me a Dynamo Boundingbox, orientated to coordinatesystem so i have no use for that.
But you teached me that i can make a Revit Boundingbox with the help of a context “element” coordinatesystem.

BoundingBox.ByCornersCoordinateSystem(bb.MinPoint,bb.MaxPoint, cs)

It´s a little tricky to make an element CS for a floor but it´s possible. But how to get min.point and max.point? Would be a dream if there was a “element.BB” node for revit BB that would really fit the element. So maybe this is an option for floors? Can i get faces from that revit boundingbox somehow?

edit:
Ok, now i thought abou the next steps, i need the following distances for my family placing, so i really need that geometry…

Building such a “pull geometry” zero touch node would be a nice project i could do with a coworker. But you say i can´t use that geometry in my dynamo graph, so the whole graph has to be a zero touch node? Then thats no option for me.

The task which you’re after, pulling faces and placing families on them, can be done via a Zero Touch node or a Python node. The thing you can’t do is pass the faces from Revit back into Dynamo as the geometry is invalid in the Dynamo environment. Chaning your settings (ie: Geometry Scaling) might resolve the warning, but more likely you’ll have to go in and clean up the model to make a clean enough surface to get conversion working.

1 Like

Now i think i understand and started testing.

I can get Revit.DB geometry and i can get Revit.DB faces:

opt = Options()

for w in walls:
	solid = w.get_Geometry(opt)
	for f in solid:
		faces = f.Faces

I can get edges as curve loops and i can get surfaces.

for f in faces:
	 curve = f.GetEdgesAsCurveLoops()
for f in faces:
	 surface = f.GetSurface()

And by getting the Area i can quickly filter out the 2 faces that i need:

for f in faces:
	 area = f.Area

So now i just have to find a method to make a dynamo solid/face or whatever out of that, then i can do my calculations for getting the right faces, and later call that Revit.DB.Face by index to place the families :smiley:

2 Likes

And while some Revit faces can be converted to dynamo faces, the two desired faces can´t be converted. But yeah thats why I´m even doing this whole revit.db thing. :slight_smile:

And converting lines tells me that they are likely coincident:

Tomorrow i will try to brake the faces down to a rectangle with min and max point of the face, and then build a face/solid/cuboid out of that.

Exactly. Try to do your sorting/filtering in the one python node too.
Don’t have Revit handy, but something along the lines of this should do the trick:


for w in walls:
	solid = w.get_Geometry(opt)
	for f in solid:
		faces = f.Faces
		area = [f.Area for f in faces]
		srtdFaces = [a[1] for a in sorted(zip(faces,area))]
		largestAreaFaces = srtdFaces[-2:]
		dynSurfs = [ f.ToProtoType() for f in largestAreaFaces ]

This attempts to push the two faces you’re after (perhaps they’d work, after all the error says ONE of the surfaces is failing to convert, not all of them).

1 Like

Hello Jacob, sorry for the late reply,

This code don´t gives us faces, only areas:

Further progress on my graph, got RevitPoints and DynPoints from my edgeloops:

for l in lines:
	 startpoint = l.GetEndPoint(0)
	 dynstartpoint = startpoint.ToPoint()
	 endpoint = l.GetEndPoint(1)
	 dynendpoint = endpoint.ToPoint()
	 dynstartpoints.append(dynstartpoint)
	 dynendpoints.append(dynendpoint)

Next step is creating a solid by min max points…

Just having the always same issues with my output…

getting only first item of the lists :grimacing:

or getting all items but flattened :grimacing:

line 20 should be srtdFaces = [a[1] for a in sorted(zip(area, face))] - the zrea and face lists are reversed in the zip function. Sorry!

1 Like

Thanks, additional info on this method:

You can also use sorted() and zip() together to achieve a similar result:

>>> letters = ['b', 'a', 'd', 'c']
>>> numbers = [2, 4, 3, 1]
>>> data = sorted(zip(letters, numbers))  # Sort by letters
>>> data
[('a', 4), ('b', 2), ('c', 1), ('d', 3)]
In this case, sorted() runs through the iterator generated by zip() and sorts the items by letters, all in one go. 

Using the Python zip() Function for Parallel Iteration – Real Python

Here we go, solved my python issues and now i get my desired points and i know the min and max z points.
I also can make a polygon out of the points.

So thinking about how to continue, i think i have to make a localCoordinateSystem for the wall, so i can check which points are max x and min x. With my x and z points i can then create a new solid.

So i again have to use the revit.DB elements to build my CS.

Or is there a better way to go?

Achieved to make surfaces but i don´t think this method will be reliable:

Ok, after testing on many walls i found out my python code did not work well, i have again split up in several python blocks…

now i have the problem that i can´t handle a Level 5 List with python :confused:

for w in walls:
	solid = w.get_Geometry(opt)
	for f in solid:
		faces = f.Faces
		area = [f.Area for f in faces]
		srtdFaces = [a[1] for a in sorted(zip(area,faces))]
		largestAreaFaces = srtdFaces[-2:]
		FacesList.append(largestAreaFaces)
		
OUT = FacesList
for faces in facelist:
	curves=[]
	for f in faces:
		curve = f.GetEdgesAsCurveLoops()
		curves.append(curve)
	curvelist.append(curves)
OUT = curvelist

Strange, i get a different list structure for list 0 @L4, then the other lists @L4 all have the same structure. :grimacing:

what am I missing here?

See here that List0 is wrong (i think it is doubled) but all other lists are correct, List 3 as example:

I found the reason for the different list structure.
Some faces give more then 1 edge curve loop, for example if there is a wall opening like this door opening:

Edit:

After comparing the fails of dynamo geoemtry and revit geometry i ask myselfe if it´s worth it.

Dynamo geometry fails for 1,4% of walls.
Revit geometry fails for 0,07% of walls.

Yes, thats 20 times better to work with Revit geometry, but it´s 100 times harder :smiley:

Thanks to you @jacob.small i managed to change my graph to use revit geometry and as a final step write some code to place my first family with the revit geometry :slight_smile:

face = UnwrapElement(IN[0])
point = UnwrapElement(IN[1])
vector = UnwrapElement(IN[2])
family = UnwrapElement(IN[3])

TransactionManager.Instance.EnsureInTransaction(doc)

place = doc.Create.NewFamilyInstance(face,point,vector,family)

TransactionManager.Instance.TransactionTaskDone()

OUT = place

Still a long road to get this code working for lists of points, vectors and faces…

Thank you very much for your help, learned so much working on this :slight_smile:

2 Likes

@gerhard.p I did a few tests and it really seems much better to use your method for obtaining solids other than counting on the geometry scaling :slight_smile: how do you handle wrapping though?

I just tried getting a floor’s solid and I get the following error:

image

even though I have GeometryConversion imported:

import clr
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')

import System
from System import Array
from System.Collections.Generic import *

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

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

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

clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument

opt = Options()

outlist = []

if isinstance(IN[0], list):
	floors = UnwrapElement(IN[0])
	for floor in floors:
		solid = f.get_Geometry(opt)
		outlist.append(solid.ToProtoType())
else:
	floor = UnwrapElement(IN[0])
	solid = floor.get_Geometry(opt)
	outlist.append(solid.ToProtoType())
	
OUT = outlist

When you remove .ToProtoType() method, you would still get a DB.Solid: