Area Boundary not working for non rectangular areas

I am trying to create floor from Area Boundaries it works fine for rectangular area but when i select not rectangular area it does not work :
Code as

area_boundaries = area.GetBoundarySegments(SpatialElementBoundaryOptions())
        
with Transaction(doc, 'Create Floor') as t:
                t.Start()
                List_curve_loop = List[CurveLoop]()
                for area_outline in area_boundaries:
                    curve_loop = CurveLoop()
                   
                    for seg in area_outline:
                        curve_loop.Append(seg.GetCurve())
                        
                    List_curve_loop.Add(curve_loop)
                    
                new_floor = Floor.Create(doc, List_curve_loop, selected_floor_type.Id, level.Id)

Hi,
The issue probably has to do with the order of creation of your curve loop.

  1. The complete script would help
  2. the error generated too

references:

https://thebuildingcoder.typepad.com/blog/2013/03/sort-and-orient-curves-to-form-a-contiguous-loop.html

1 Like
import traceback

from Autodesk.Revit.DB import *
from pyrevit import forms

# .NET IMPORTS
from Autodesk.Revit.DB import CurveLoop, Line, XYZ
import clr
clr.AddReference("System")
from System.Collections.Generic import List
from Autodesk.Revit.DB import XYZ, CurveLoop, CurveArray, SpatialElementBoundaryOptions, BuiltInParameter, Floor, Transaction, FilteredElementCollector
#Custom
from Snippets._context_manager  import ef_Transaction, try_except
from Snippets._selection        import get_selected_areas
from GUI.forms                  import select_from_dict
from GUI.Tools.AreaMapper  import AreaMapper
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')

uidoc    = __revit__.ActiveUIDocument
app      = __revit__.Application
doc      = __revit__.ActiveUIDocument.Document
rvt_year = int(app.VersionNumber)

active_view_id      = doc.ActiveView.Id
active_view         = doc.GetElement(active_view_id)
active_view_level   = active_view.GenLevel

class FloorsCreationWarningSwallower(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        failList = failuresAccessor.GetFailureMessages()
        for failure in failList: #type: FailureMessage
            failuresAccessor.DeleteWarning(failure)
            # print(failure)
            # fail_id = failure.GetFailureDefinitionId()
            # if fail_id == BuiltInFailures.OverlapFailures.FloorsOverlap:
            #     failuresAccessor.DeleteWarning(failure)
            # elif fail_id == BuiltInFailures.ExtrusionFailures.CannotDrawExtrusionsError:
            #     failuresAccessor.DeleteWarning(failure)

        return FailureProcessingResult.Continue


def select_floor_type(area_type_value):
    """Function to select Floor Type based on Area Type value."""
    if area_type_value == "Hotel":
        floor_type_name = "CDA_Mass_Hotel"
    elif area_type_value == "Office":
        floor_type_name = "CDA_Mass_Office"
    elif area_type_value == "Commercial":
        floor_type_name = "CDA_Mass_Commercial"
    elif area_type_value == "Market Rental":
        floor_type_name = "CDA_Mass_MarketRental"
    elif area_type_value == "Market Residential":
        floor_type_name = "CDA_Mass_MarketResidential"
    elif area_type_value == "Social Housing":
        floor_type_name = "CDA_Mass_SocialHousing"
    elif area_type_value == "Affordable Rental":
        floor_type_name = "CDA_Mass_AffordableRental"
    else:
        floor_type_name = "CDA_Mass_Site"
    
    floor_types = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors) \
                    .WhereElementIsElementType().ToElements()
    
    selected_floor_type = None  # Initialize the variable before the loop
    
    for floor_type in floor_types:
        type_comments = floor_type.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_COMMENTS).AsString()
        
        
        if type_comments == floor_type_name:  # Use the correct variable name for comparison
            selected_floor_type = floor_type
            break  # Exit the loop once a matching floor type is found
    
    return selected_floor_type  # Return the selected floor type outside the loop
        

def SortCurves(originalLoop):
    sortedLoop = CurveLoop()
    if len(originalLoop) == 0:
        return None
    
    sortedLoop.Append(originalLoop[0])
    sorted = [originalLoop[0]]
    print sorted 
    for i in range(len(originalLoop)):
        for oCurve in originalLoop:
            if oCurve in sorted:
                continue  # Prevent curve duplication

            # Case of curve is in correct position
            if sortedLoop.Last().GetEndPoint(1).IsAlmostEqualTo(oCurve.GetEndPoint(0)):
                sorted.append(oCurve)
                sortedLoop.Append(oCurve)
                break
            # Case of curve is inverted
            elif sortedLoop.Last().GetEndPoint(1).IsAlmostEqualTo(oCurve.GetEndPoint(1)):
                sortedLoop.Append(oCurve.CreateReversed())
                sorted.append(oCurve)
                break
            else:
                continue

    return sortedLoop

def area_to_floor(area, offset, level):
    new_floor = None
    
    try:
        # Make sure that Area is bounding.
        if not area.get_Parameter(BuiltInParameter.ROOM_AREA).AsDouble():
            return None

        # Check the shared parameter "Area Type" value
        area_type_param = area.LookupParameter("Area Type")
       
        if area_type_param:
            area_type_value = area_type_param.AsString()
            selected_floor_type = select_floor_type(area_type_value)
        
        #  AREA BOUNDARIES
        area_boundaries = area.GetBoundarySegments(SpatialElementBoundaryOptions())
        
        List_curve_loop = list[CurveLoop]()
        
        for area_outline in area_boundaries:
            print area_outline
            curve_loop = SortCurves(area_outline)  # Sort the curves using SortCurves function
            print curve_loop
            if curve_loop:
                List_curve_loop.Add(curve_loop)
        print List_curve_loop
        floor_shape = area_boundaries[0]
        
        openings = list(area_boundaries)[1:] if len(area_boundaries) > 1 else []
        
               
        
        if rvt_year < 2022:

            with Transaction(doc,'Create Floor') as t:
                t.Start()
                
                curve_array = CurveArray()
                for seg in floor_shape:
                    curve_array.Append(seg.GetCurve())
                new_floor = doc.Create.NewFloor(curve_array, selected_floor_type, level, False)
                if new_floor:
                    # SET OFFSET
                    param = new_floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM)
                    param.Set(offset)
                    
                failOpt = t.GetFailureHandlingOptions()
                failOpt.SetFailuresPreprocessor(FloorsCreationWarningSwallower())
                t.SetFailureHandlingOptions(failOpt)

                t.Commit()

            #  CREATE FLOOR OPENINGS SEPERATELY BEFORE RVT 2022
            if openings:
                with Transaction(doc,'Create FloorOpening') as t2:
                    t2.Start()
                    # with ef_Transaction(doc, "Create Openings"):
                    for opening in openings:
                        with try_except():
                            opening_curve = CurveArray()
                            for seg in opening:
                                opening_curve.Append(seg.GetCurve())
                            floor_opening = doc.Create.NewOpening(new_floor, opening_curve, True)
                    t2.Commit()



        if rvt_year >= 2022:
            with Transaction(doc, 'Create Floor') as t:
                t.Start()
                
                    
                new_floor = Floor.Create(doc, List_curve_loop, selected_floor_type.Id, level.Id) #FIXME
                if new_floor:
                    # SET OFFSET
                    param = new_floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM)
                    param.Set(offset)

                failOpt = t.GetFailureHandlingOptions()
                failOpt.SetFailuresPreprocessor(FloorsCreationWarningSwallower())
                t.SetFailureHandlingOptions(failOpt)
                t.Commit()


    except:
        # print(traceback.format_exc())
        pass

    if new_floor:
        return new_floor



def create_floors(selected_areas,  offset):
    """Function to loop through selected areas and create floors from them."""
    new_floors = []

    with TransactionGroup(doc, __title__) as tg:
        tg.Start()
        for r in selected_areas:
            with try_except(debug=True):
               

                new_floor = area_to_floor(area = r, offset = offset, level=r.Level)
                if new_floor:
                    new_floors.append(new_floor)
        tg.Assimilate()
    return new_floors


def get_user_input():
    all_ceil_types  = FilteredElementCollector(doc).OfClass(FloorType).OfCategory(BuiltInCategory.OST_Floors).ToElements()
    dict_ceil_types = {Element.Name.GetValue(fr): fr for fr in all_ceil_types}

    GUI = AreaMapper(
                          title=__title__,
                          button_name='Create Floors',
                          version=__version__)
    return GUI



if __name__ == '__main__':
    selected_areas      = get_selected_areas(uidoc, exitscript=True)
    GUI                 = get_user_input()
    selected_floor_type = GUI.selected_type
    offset              = GUI.offset

    new_floors = create_floors(selected_areas, offset)

    with try_except():
        uidoc.Selection.SetElementIds(List[ElementId]([f.Id for f in new_floors if f.IsValidObject]))

I try using Sorted Curve (ps I am using EF tool )

I’m glad this issue is being addressed. What I have found is that there are some geometries that Revit just doesn’t like. This will be something I will bring up next week at AU.

I have been working on a script that takes Area boundaries and makes Generic Models out of an extrusion. Anything more complex than your basic orthogonal boundaries would not work. I ended up generating model lines and manually creating the extrusions. Some of the more complex geometries would not let me create an extrusion no matter what I did! Constant warnings of either gaps, intersections, short lines.

My view is: If you can create an Area, you should be able to get those boundaries and use them in any closed loop operation with no issues.

If I get info from AutoDesk, I’ll post it.

Best,
Loren

Update:
I had some good luck using GetRoomBoundaryAsCurveLoopArray() from the ExporterIFC module. Check out my post on the Revit API forum:

it worked !! thanks Lorhal `def area_to_floor(area, offset, level):
new_floor = None

try:
    # Make sure that Area is bounding.
    if not area.get_Parameter(BuiltInParameter.ROOM_AREA).AsDouble():
        return None

    # Check the shared parameter "Area Type" value
    area_type_param = area.LookupParameter("Area Type")
    
    if area_type_param:
        area_type_value = area_type_param.AsString()
        selected_floor_type = select_floor_type(area_type_value)
    
    # AREA BOUNDARIES
    opt = DB.SpatialElementBoundaryOptions()
    curve_loop_array = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(area, opt, True)
     
    with Transaction(doc, 'Create Floor') as t:
        t.Start()
        curve_loop_list = List[CurveLoop]()  # Create an empty list of CurveLoop
        for curve_loop in curve_loop_array:
            # Commenting out the print statement
            # print(curve_loop)
            curve_loop_list.Add(curve_loop)  # Add each CurveLoop to the list
        new_floor = Floor.Create(doc, curve_loop_list, selected_floor_type.Id, level.Id)
        
        if new_floor:
            # SET OFFSET
            param = new_floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM)
            param.Set(offset)

        failOpt = t.GetFailureHandlingOptions()
        failOpt.SetFailuresPreprocessor(FloorsCreationWarningSwallower())
        t.SetFailureHandlingOptions(failOpt)
        t.Commit()

except:
    # Print the exception for debugging purposes
    print(traceback.format_exc())

return new_floor`