I work with models that contain Gross Building AreaPlans with their respective Areas and AreaBoundary lines. The goal is to use the AreaBoundaries to extrude a solid, into a GenericModel per Level. This can be achieved in Dynamo, but is not reliable and has difficulty with more complicated geometry. Trying with a Python node gives me an 'internal exception' where the NewExtrusion is called.
Trying with pyRevit, it gives a 'managed exception' where the NewExtrusion is called. Here is my script:
#Create Extrusions from Gross Boundaries
import clr
import sys
import pyrevit
from pyrevit import revit, DB, script, forms, api
doc = revit.doc
doc_create = doc.Create
with DB.Transaction(doc, "Create Family Doc") as tx:
tx.Start()
m_familyDocument = doc.Application.NewFamilyDocument("C:/ProgramData/Autodesk/RVT 2023/Family Templates/English-Imperial/Generic Model.rft")
m_creationFamily = m_familyDocument.FamilyCreate
print(m_creationFamily)
tx.Commit()
all_views = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_Views).WhereElementIsNotElementType()
viewsGross = []
viewsGrossNames = []
for v in all_views:
if not v.IsTemplate:
if (doc.GetElement(v.GetTypeId())).get_Parameter(DB.BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString() == "Gross Building":
if not v.GenLevel.Name == "R001":
if not v.GenLevel.Name == "S001":
viewsGross.append(v)
viewsGrossNames.append(v.GenLevel.Name)
#print(viewsGross)
print(viewsGrossNames)
gross_areas = []
#for view in viewsGross:
gross_view_areas = DB.FilteredElementCollector(doc, viewsGross[0].Id).OfCategory(DB.BuiltInCategory.OST_Areas).WhereElementIsNotElementType()
for view_areas in gross_view_areas:
gross_areas.append(view_areas)
print(gross_view_areas)
print("Gross Areas")
print(len(gross_areas))
with DB.Transaction(doc, "Get Segments and Sketchplanes") as tx:
tx.Start()
area_segments = []
sketch_planes = []
for area in gross_areas:
sketch_plane = DB.SketchPlane.Create(doc,area.LevelId)
sketch_planes.append(sketch_plane)
opt = DB.SpatialElementBoundaryOptions()
segments = area.GetBoundarySegments(opt)
area_segments.append(segments)
segments_sketchplanes = zip(area_segments,sketch_planes)
tx.Commit()
#print(sketch_planes)
#print(segments_sketchplanes)
#print("Area Segments")
#print(area_segments)
#print(len(area_segments))
one_loop = segments_sketchplanes[0]
#print(one_loop)
area_curves = []
for segmentz in one_loop[0]:
curveArrArray = DB.CurveArrArray()
for seg in segmentz:
curveArray = DB.CurveArray()
curve = seg.GetCurve()
area_curves.append(curve)
# Sorting and grouping the curves into contiguous loops
def find_contiguous_loop(start_curve, remaining_curves):
loop = [start_curve]
next_point = start_curve.GetEndPoint(1)
while loop[0].GetEndPoint(0) != loop[-1].GetEndPoint(1):
found = False
for curve in remaining_curves:
if next_point.IsAlmostEqualTo(curve.GetEndPoint(0)):
loop.append(curve)
next_point = curve.GetEndPoint(1)
remaining_curves.remove(curve)
found = True
break
elif next_point.IsAlmostEqualTo(curve.GetEndPoint(1)):
curve.CreateReversed()
loop.append(curve)
next_point = curve.GetEndPoint(0)
remaining_curves.remove(curve)
found = True
break
if not found:
break
return loop
# Sorting and grouping the curves into contiguous loops
sorted_loops = []
#while area_curves:
remaining_curves = area_curves[:]
loop = find_contiguous_loop(area_curves[0], remaining_curves)
# Check if the loop is valid (more than one curve)
if len(loop) > 1:
sorted_loops.append(loop)
print("Sorted Loops")
print(sorted_loops)
print(len(sorted_loops))
# Now, sorted_loops contains the contiguous loops of curves
for loop in sorted_loops:
curveArrArray = DB.CurveArrArray()
for curve in loop:
curveArray = DB.CurveArray()
curveArray.Append(curve)
curveArrArray.Append(curveArray)
print(curveArrArray)
if curveArrArray.Size > 0:
# Create extrusion for each loop
with DB.Transaction(doc, "Create Extrusion") as tx:
tx.Start()
area_extrusion = m_creationFamily.NewExtrusion(True, curveArrArray, one_loop[1], 20.0)
tx.Commit()
#print(m_familyDocument)
#print(m_creationFamily)
#print(normal)
#print(origin)
#print(plane)
#print(sketchplane)
#print(curveArrArray)
#print(area_curves)
#print(len(area_curves))
#print(startcurve)
#print(startP)
#print(endP)
#print(curve)
#print(curveArray.Size)
#print(curveArrArray.Size)
This script is only meant to process a single Area. Hopefully I can scale up later.
Since I established that the GetBoundarySegments method is the one to use, are the Segments returned contiguous or need to be sorted?
I have tried with and without the 'find_continuous_loop' function , same error. See the attached Revit file containing a Gross Building AreaPlan with a relatively simple Area for testing.
Any help is appreciated.
Thanks,
-Loren
Solved! Go to Solution.
I work with models that contain Gross Building AreaPlans with their respective Areas and AreaBoundary lines. The goal is to use the AreaBoundaries to extrude a solid, into a GenericModel per Level. This can be achieved in Dynamo, but is not reliable and has difficulty with more complicated geometry. Trying with a Python node gives me an 'internal exception' where the NewExtrusion is called.
Trying with pyRevit, it gives a 'managed exception' where the NewExtrusion is called. Here is my script:
#Create Extrusions from Gross Boundaries
import clr
import sys
import pyrevit
from pyrevit import revit, DB, script, forms, api
doc = revit.doc
doc_create = doc.Create
with DB.Transaction(doc, "Create Family Doc") as tx:
tx.Start()
m_familyDocument = doc.Application.NewFamilyDocument("C:/ProgramData/Autodesk/RVT 2023/Family Templates/English-Imperial/Generic Model.rft")
m_creationFamily = m_familyDocument.FamilyCreate
print(m_creationFamily)
tx.Commit()
all_views = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_Views).WhereElementIsNotElementType()
viewsGross = []
viewsGrossNames = []
for v in all_views:
if not v.IsTemplate:
if (doc.GetElement(v.GetTypeId())).get_Parameter(DB.BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString() == "Gross Building":
if not v.GenLevel.Name == "R001":
if not v.GenLevel.Name == "S001":
viewsGross.append(v)
viewsGrossNames.append(v.GenLevel.Name)
#print(viewsGross)
print(viewsGrossNames)
gross_areas = []
#for view in viewsGross:
gross_view_areas = DB.FilteredElementCollector(doc, viewsGross[0].Id).OfCategory(DB.BuiltInCategory.OST_Areas).WhereElementIsNotElementType()
for view_areas in gross_view_areas:
gross_areas.append(view_areas)
print(gross_view_areas)
print("Gross Areas")
print(len(gross_areas))
with DB.Transaction(doc, "Get Segments and Sketchplanes") as tx:
tx.Start()
area_segments = []
sketch_planes = []
for area in gross_areas:
sketch_plane = DB.SketchPlane.Create(doc,area.LevelId)
sketch_planes.append(sketch_plane)
opt = DB.SpatialElementBoundaryOptions()
segments = area.GetBoundarySegments(opt)
area_segments.append(segments)
segments_sketchplanes = zip(area_segments,sketch_planes)
tx.Commit()
#print(sketch_planes)
#print(segments_sketchplanes)
#print("Area Segments")
#print(area_segments)
#print(len(area_segments))
one_loop = segments_sketchplanes[0]
#print(one_loop)
area_curves = []
for segmentz in one_loop[0]:
curveArrArray = DB.CurveArrArray()
for seg in segmentz:
curveArray = DB.CurveArray()
curve = seg.GetCurve()
area_curves.append(curve)
# Sorting and grouping the curves into contiguous loops
def find_contiguous_loop(start_curve, remaining_curves):
loop = [start_curve]
next_point = start_curve.GetEndPoint(1)
while loop[0].GetEndPoint(0) != loop[-1].GetEndPoint(1):
found = False
for curve in remaining_curves:
if next_point.IsAlmostEqualTo(curve.GetEndPoint(0)):
loop.append(curve)
next_point = curve.GetEndPoint(1)
remaining_curves.remove(curve)
found = True
break
elif next_point.IsAlmostEqualTo(curve.GetEndPoint(1)):
curve.CreateReversed()
loop.append(curve)
next_point = curve.GetEndPoint(0)
remaining_curves.remove(curve)
found = True
break
if not found:
break
return loop
# Sorting and grouping the curves into contiguous loops
sorted_loops = []
#while area_curves:
remaining_curves = area_curves[:]
loop = find_contiguous_loop(area_curves[0], remaining_curves)
# Check if the loop is valid (more than one curve)
if len(loop) > 1:
sorted_loops.append(loop)
print("Sorted Loops")
print(sorted_loops)
print(len(sorted_loops))
# Now, sorted_loops contains the contiguous loops of curves
for loop in sorted_loops:
curveArrArray = DB.CurveArrArray()
for curve in loop:
curveArray = DB.CurveArray()
curveArray.Append(curve)
curveArrArray.Append(curveArray)
print(curveArrArray)
if curveArrArray.Size > 0:
# Create extrusion for each loop
with DB.Transaction(doc, "Create Extrusion") as tx:
tx.Start()
area_extrusion = m_creationFamily.NewExtrusion(True, curveArrArray, one_loop[1], 20.0)
tx.Commit()
#print(m_familyDocument)
#print(m_creationFamily)
#print(normal)
#print(origin)
#print(plane)
#print(sketchplane)
#print(curveArrArray)
#print(area_curves)
#print(len(area_curves))
#print(startcurve)
#print(startP)
#print(endP)
#print(curve)
#print(curveArray.Size)
#print(curveArrArray.Size)
This script is only meant to process a single Area. Hopefully I can scale up later.
Since I established that the GetBoundarySegments method is the one to use, are the Segments returned contiguous or need to be sorted?
I have tried with and without the 'find_continuous_loop' function , same error. See the attached Revit file containing a Gross Building AreaPlan with a relatively simple Area for testing.
Any help is appreciated.
Thanks,
-Loren
Solved! Go to Solution.
Solved by loren_routh. Go to Solution.
Unfortunately, afaik, GetBoundarySegments is not guaranteed to return the segments in any specific order.
I would suggest just creating ,model lines first to ensure that the extrusion can be executed successfully when you drive it manually through the end user interface, cf.
Unfortunately, afaik, GetBoundarySegments is not guaranteed to return the segments in any specific order.
I would suggest just creating ,model lines first to ensure that the extrusion can be executed successfully when you drive it manually through the end user interface, cf.
HI @jeremy_tammik @loren_routh
I think @loren_routh trying to create Extrusion in Project Template. Actually We don't have support to create In-Place Families in Revit API.
I have a workaround to fix this. Get Points of the boundary and Create a Solid using BRep Bulilder Class.
In B-Rep you can able to create a Solid Extrusion (Kindly check the sample code in above link). And Convert that into Direct Shape.
Hope this will helps 🙂
HI @jeremy_tammik @loren_routh
I think @loren_routh trying to create Extrusion in Project Template. Actually We don't have support to create In-Place Families in Revit API.
I have a workaround to fix this. Get Points of the boundary and Create a Solid using BRep Bulilder Class.
In B-Rep you can able to create a Solid Extrusion (Kindly check the sample code in above link). And Convert that into Direct Shape.
Hope this will helps 🙂
So my understanding is this:
The boundary of an Area object (which by definition is closed in order to create an Area) does not necessarily return segments in contiguous order (more likely in the order they were created, I guess?), and there is no guarantee the resultant curves can be extruded or form a polycurve/polyline.
I'll try the tip in the blogpost, but have no interest in a manual workflow. Too many buildings to model.
There are a lot of quirky things Revit does when importing/interpreting geometry. Is there a known issues log somewhere?
So my understanding is this:
The boundary of an Area object (which by definition is closed in order to create an Area) does not necessarily return segments in contiguous order (more likely in the order they were created, I guess?), and there is no guarantee the resultant curves can be extruded or form a polycurve/polyline.
I'll try the tip in the blogpost, but have no interest in a manual workflow. Too many buildings to model.
There are a lot of quirky things Revit does when importing/interpreting geometry. Is there a known issues log somewhere?
Thanks for the tip, I'll try that. The only problem is that DirectShape objects are one-and-done. You can't adjust them like a Generic Model. But, it may be the only way with extremely complicated shapes.
Thanks for the tip, I'll try that. The only problem is that DirectShape objects are one-and-done. You can't adjust them like a Generic Model. But, it may be the only way with extremely complicated shapes.
I tried to do what the Building Coder post suggested, but was not able to ‘fix’ any warnings. In fact, when I pasted the model lines into an extrusion sketch (within a Generic Model routine ) I could not make the extrusion! Many errors showed up and as I went about fixing them, more appeared. No matter how many times I filleted, aligned, replaced, or redrew the lines, it continued to give me warnings in other segments that were previously fine.
Granted, this was very complicated geometry, but my view is:
If you can create an Area, you should be able to form any closed loop object.
Sadly, this is not the case. I will be at AU next week and hope to get some answers.
Despite the frustration, I will look into incorporating methods detailed in this BC article: https://thebuildingcoder.typepad.com/blog/2015/01/exporterifcutils-curve-loop-sort-and-validate.html
With architects designing more organic structures, Revit needs to keep up and find a way to accommodate the geometry.
I tried to do what the Building Coder post suggested, but was not able to ‘fix’ any warnings. In fact, when I pasted the model lines into an extrusion sketch (within a Generic Model routine ) I could not make the extrusion! Many errors showed up and as I went about fixing them, more appeared. No matter how many times I filleted, aligned, replaced, or redrew the lines, it continued to give me warnings in other segments that were previously fine.
Granted, this was very complicated geometry, but my view is:
If you can create an Area, you should be able to form any closed loop object.
Sadly, this is not the case. I will be at AU next week and hope to get some answers.
Despite the frustration, I will look into incorporating methods detailed in this BC article: https://thebuildingcoder.typepad.com/blog/2015/01/exporterifcutils-curve-loop-sort-and-validate.html
With architects designing more organic structures, Revit needs to keep up and find a way to accommodate the geometry.
This just in:
I tried the GetRoomBoundaryAsCurveLoopArray() method, and it totally worked! You need to import the ExporterIFC module, etc. This method eliminated a chunk of code, no sorting or extracting the curves. It let me create an extrusion (manually) with no errors at all! As you can see by the pic below, this was not a rectangle. Definitely has my vote to be included in the regular Revit API. Now to make it work with Generic Models...
import clr
clr.AddReferenceToFileAndPath(r'C:\Program Files\Autodesk\Revit 2023\AddIns\IFCExporterUI\Autodesk.IFC.Export.UI.dll')
clr.AddReference("RevitAPIIFC")
from Autodesk.Revit.DB.IFC import ExporterIFC
from Autodesk.Revit.DB.IFC import ExporterIFCUtils
opt = DB.SpatialElementBoundaryOptions()
curve_loop = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(selected_area, opt, True)
with DB.Transaction(doc, "Create Model Lines") as tx:
tx.Start()
sketch_plane = DB.SketchPlane.Create(doc,selected_area.LevelId)
for loop in curve_loop:
for line in loop:
crv = doc_create.NewModelCurve(line, sketch_plane)
tx.Commit()
This just in:
I tried the GetRoomBoundaryAsCurveLoopArray() method, and it totally worked! You need to import the ExporterIFC module, etc. This method eliminated a chunk of code, no sorting or extracting the curves. It let me create an extrusion (manually) with no errors at all! As you can see by the pic below, this was not a rectangle. Definitely has my vote to be included in the regular Revit API. Now to make it work with Generic Models...
import clr
clr.AddReferenceToFileAndPath(r'C:\Program Files\Autodesk\Revit 2023\AddIns\IFCExporterUI\Autodesk.IFC.Export.UI.dll')
clr.AddReference("RevitAPIIFC")
from Autodesk.Revit.DB.IFC import ExporterIFC
from Autodesk.Revit.DB.IFC import ExporterIFCUtils
opt = DB.SpatialElementBoundaryOptions()
curve_loop = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(selected_area, opt, True)
with DB.Transaction(doc, "Create Model Lines") as tx:
tx.Start()
sketch_plane = DB.SketchPlane.Create(doc,selected_area.LevelId)
for loop in curve_loop:
for line in loop:
crv = doc_create.NewModelCurve(line, sketch_plane)
tx.Commit()
Thank you very much for this simple and effective solution. I shared ot on the blog for posterity:
Thank you very much for this simple and effective solution. I shared ot on the blog for posterity:
Can't find what you're looking for? Ask the community or share your knowledge.