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…
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?
I don´t think that will help us at investigating the problem 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.
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.
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
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.
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).
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.
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.
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
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:
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
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
@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 how do you handle wrapping though?
I just tried getting a floor’s solid and I get the following error:
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: