Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Creating a Generic Model from Area Boundaries

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
loren_routh
963 Views, 8 Replies

Creating a Generic Model from Area Boundaries

loren_routh
Enthusiast
Enthusiast

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

 

 

 

0 Likes

Creating a Generic Model from Area Boundaries

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

 

 

 

8 REPLIES 8
Message 2 of 9
jeremy_tammik
in reply to: loren_routh

jeremy_tammik
Autodesk
Autodesk

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. 

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

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. 

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 3 of 9

Mohamed_Arshad
Advisor
Advisor

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 🙂

Thanks & Regards,
Mohamed Arshad K

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 🙂

Thanks & Regards,
Mohamed Arshad K
Message 4 of 9
loren_routh
in reply to: jeremy_tammik

loren_routh
Enthusiast
Enthusiast

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? 

 

 

 

 

 

0 Likes

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? 

 

 

 

 

 

Message 5 of 9

loren_routh
Enthusiast
Enthusiast

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.

0 Likes

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.

Message 6 of 9
loren_routh
in reply to: jeremy_tammik

loren_routh
Enthusiast
Enthusiast

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.  

 

 

0 Likes

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.  

 

 

Message 7 of 9
loren_routh
in reply to: loren_routh

loren_routh
Enthusiast
Enthusiast
Accepted solution

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()

lorenrouth_0-1699747002855.png

 

 

 

 

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()

lorenrouth_0-1699747002855.png

 

 

 

 

Message 8 of 9
jeremy_tammik
in reply to: loren_routh

jeremy_tammik
Autodesk
Autodesk

Thank you very much for this simple and effective solution. I shared ot on the blog for posterity:

  

https://thebuildingcoder.typepad.com/blog/2023/11/journal-magic-adjacent-rooms-and-room-boundary.htm...

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open

Thank you very much for this simple and effective solution. I shared ot on the blog for posterity:

  

https://thebuildingcoder.typepad.com/blog/2023/11/journal-magic-adjacent-rooms-and-room-boundary.htm...

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 9 of 9
loren_routh
in reply to: jeremy_tammik

loren_routh
Enthusiast
Enthusiast
Wow, I can remember reading your blog posts and hardly understanding anything! This is a huge honor. Just to be clear, I am still struggling to create Generic Models via script. Also, I had an error with an extremely complex Area (10+ donut holes), but other than that it has been very stable. I can create Generic models using the GeometryCreationUtilities solid method and then the Springs Node to create Family Instance. More to come. Off to AU2023 day two!

Wow, I can remember reading your blog posts and hardly understanding anything! This is a huge honor. Just to be clear, I am still struggling to create Generic Models via script. Also, I had an error with an extremely complex Area (10+ donut holes), but other than that it has been very stable. I can create Generic models using the GeometryCreationUtilities solid method and then the Springs Node to create Family Instance. More to come. Off to AU2023 day two!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report