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: 

Split a floor into two pieces, given a reference plane

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
sebastian.galindoMRERM
419 Views, 6 Replies

Split a floor into two pieces, given a reference plane

Hello,

 

I have done several searches about it, but the information found does not work correctly.
What I am looking to achieve is to select a floor (INITIAL FLOOR) and a reference plane and that this plane allows the floor to be divided into 2 instances (FLOOR A AND FLOOR B).

 

Imagen1.png

The reason I need this is to model certain projects much faster, for this reason it does not work for me to do it using parts.

 

I share down below the code.... I already did a Dynamo Script using Python and it works preaty well, but for security reasons we have to convert it into C#.

 

using Autodesk.Revit.Attributes
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;


namespace YourNamespace
{
    [Transaction(TransactionMode.Manual)]
    public class FloorCutter : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            // Get the Revit document and UI application
            UIDocument uiDoc = commandData.Application.ActiveUIDocument;
            Document doc = uiDoc.Document;

            // Prompt the user to select the floor to cut
            Reference floorRef = uiDoc.Selection.PickObject(ObjectType.Element, new FloorSelectionFilter(), "Select floor to cut");
            Element floor = doc.GetElement(floorRef);

            // Prompt the user to select the reference plane
            Reference planeRef = uiDoc.Selection.PickObject(ObjectType.Plane, "Select reference plane");
            SketchPlane plane = doc.GetElement(planeRef) as SketchPlane;

            // Create a plane object from the reference plane
            Plane cuttingPlane = plane.GetPlane();

            // Get the geometry of the floor
            GeometryElement geom = floor.get_Geometry(new Options());

            // Loop through the geometry objects and find the floor slab
            foreach (GeometryObject obj in geom)
            {
                Solid solid = obj as Solid;
                if (solid != null && solid.Volume > 0)
                {
                    // Split the floor at the cutting plane
                    Face face = FindSplittingFace(solid, cuttingPlane);
                    Solid[] splitSolids = solid.Split(face);

                    // Create a new floor from the split solids
                    foreach (Solid splitSolid in splitSolids)
                    {
                        if (splitSolid.Volume > 0)
                        {
                            Floor newFloor = Floor.Create(doc, floor.GetTypeId(), floor.LevelId, splitSolid);
                            newFloor.Name = floor.Name + " (cut)";
                        }
                    }
                }
            }
            return Result.Succeeded;
        }

        private Face FindSplittingFace(Solid solid, Plane plane)
        {
            // Find the face of the solid that intersects with the cutting plane
            foreach (Face face in solid.Faces)
            {
                if (face.Intersect(plane) == SetComparisonResult.Overlap)
                {
                    return face;
                }
            }
            return null;
        }
    }


    public class FloorSelectionFilter : ISelectionFilter
    {
        public bool AllowElement(Element element)
        {
            return element is Floor;
        }
        public bool AllowReference(Reference reference, XYZ position)
        {
            return false;
        }
    }
}

;

 

 

 

 

Thank you very much in advance

6 REPLIES 6
Message 2 of 7

@sebastian.galindoMRERMFirst, please post any code you have tried. Second, just spitballing a generic workflow for you:

1) grab the floor geometry and look a the Edges, this should return a list of lines that form the boundary of the floor.

2) determine what lines are "relative left" and "relative" right of your reference plane, and store them in separate lists for later. I use the term "relative" as it can get tricky when dealing with rotated coordinate systems.

3) Determine what lines are intersecting the ref planes, then generate new lines for each side based on distance from ref plane to end points.

3) Create two new floors  with the two sets of lines.

Message 3 of 7

@ctm_mka Thank you very much for your prompt response, I had the strategy you mentioned in mind, I would like to know if there is any method of the Revit API that can be responsible for dividing a surface, given that, as you mention, it would be very tedious to take care of the coordinates and the different variations , the post has been modified to include the code.

Message 4 of 7

@sebastian.galindoMRERM hrmm now that you mention a surface, maybe that's a route to try? create a toposurface (or toposolid if your in 2024) from the edge list of the floor. Then split the surface, which would negate the need to check coordinates entirely. I believe i saw a post on the forum about converting a topo to a floor (did not read it) so that could be a possibility.

Message 5 of 7

@sebastian.galindoMRERM  so after playing with it for most of the day (was a slow day at work) toposurfaces are a bust, no way to split with a line. So i think the code you posted is in the right direction. Try using SplitVolumes on the floors solid geometry. Then grab the points or curve loop from the resulting two solids and create new floors based on those?

Message 6 of 7

@sebastian.galindoMRERM! IT IS ALIVVVVVVEE! Mwahahahaha. Ahem. so got a bit excited by this, i have some basic working code for you below with the following Caveats:

  • Only works on flat slabs, no slopes or slab edits
  • the reference plane is converted to a line at the floors' elevation. You should definitely look  into line/plane intersections for a more robust solution.
  • The 3D points of the reference point must extend past the floors perimeter.

Here's how it works:

  • Select floor and ref plane.
  • Drill down the to the floors sketch and curve array which defines the floors boundary. Unlike what i mentioned before, this is just sketch definition of the boundary, the other method gave interior edges which we dont want.
  • Iterate through, and create two separate CurveLoops for floor creation at the end.
  • First for loops, checks for intersection with the ref plane line against each curve in the floor definition. if not intersection found, adds to a curveloop for the "First side" floor.
  • When intersection is found, creates two lines using the boundary lines end points and the intersection point, adding them to "First side" and "Second side" curve loops, respectively.
  • Records next position of the for loop and breaks out of the loop.
  • Second for loop picks up where we left off, repeating until another intersection is found.
  • Creates two lines using the boundary lines end points and the intersection point, adding them to "Second side" and First side" curve loops, respectively.
  • Creates the intersection line in the direction of "Second side" curve loop, add it, then reverses its direction, adds that line to "First side" loop.
  • Records next position of the for loop and breaks out of the loop.
  • Third for loop picks up where we left off and adds the remaining boundary elements to the "Firs side" curve loop.
  • Lastly, create two new floors using the two curveloops and delete original floor.

 

using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;

namespace TestProject
{
    [Transaction(TransactionMode.Manual)]
    public class SplitFloor : IExternalCommand
    {
        UIDocument _uidoc;
        Document _doc;

        public Result Execute(ExternalCommandData commdata, ref string msg, ElementSet elemset)
        {
            _uidoc = commdata.Application.ActiveUIDocument;
            _doc = _uidoc.Document;
            List<Line> floorboundary = new List<Line>();
            CurveLoop Floorpart1 = new CurveLoop();
            CurveLoop Floorpart2 = new CurveLoop();
            FloorFilter _onlyFloors = new FloorFilter();
            RefPlaneFilter _onlyRefs = new RefPlaneFilter();
            Reference floorref = _uidoc.Selection.PickObject(ObjectType.Element, _onlyFloors,"Select Floor:");
            Reference refref = _uidoc.Selection.PickObject(ObjectType.Element, _onlyRefs,"Select Reference Plane:");
            Floor testfloor = _doc.GetElement(floorref.ElementId) as Floor;
            ReferencePlane refplane = _doc.GetElement(refref.ElementId) as ReferencePlane;
            Sketch floorsketch = _doc.GetElement(testfloor.SketchId) as Sketch;
            int placeholder = 0;
            foreach (CurveArray curvarr in floorsketch.Profile)
            {
                foreach (Line test in curvarr)
                {
                    floorboundary.Add(test);
                }
            }
            //creating a line to instersect with from the ref plane
            XYZ refstart = new XYZ(refplane.BubbleEnd.X, refplane.BubbleEnd.Y, floorboundary.First().GetEndPoint(0).Z);
            XYZ refend = new XYZ(refplane.FreeEnd.X, refplane.FreeEnd.Y, floorboundary.First().GetEndPoint(0).Z);
            Line refline = Line.CreateBound(refstart, refend);
            XYZ firstintersection = null;
            IntersectionResultArray intarray = new IntersectionResultArray();
            for (int i = 0; i < floorboundary.Count(); i++)
            {
                SetComparisonResult result = floorboundary[i].Intersect(refline, out intarray);
                if (result.Equals(SetComparisonResult.Overlap))
                {
                    foreach (IntersectionResult intpoints in intarray)
                    {
                        firstintersection = intpoints.XYZPoint;
                        Line newline1 = Line.CreateBound(floorboundary[i].GetEndPoint(0), intpoints.XYZPoint);
                        Line newline2 = Line.CreateBound(intpoints.XYZPoint, floorboundary[i].GetEndPoint(1));
                        Floorpart1.Append(newline1);
                        Floorpart2.Append(newline2);
                    }
                    placeholder = i+1;
                    break;
                }
                else 
                { Floorpart1.Append(floorboundary[i]); }
            }
            intarray.Clear();
            for(int j = placeholder; j < floorboundary.Count(); j++)
            {
                SetComparisonResult result = floorboundary[j].Intersect(refline, out intarray);
                if (result.Equals(SetComparisonResult.Overlap))
                {
                    foreach (IntersectionResult intpoints in intarray)
                    {
                        Line newline2 = Line.CreateBound(floorboundary[j].GetEndPoint(0), intpoints.XYZPoint);
                        Line newline3 = Line.CreateBound(intpoints.XYZPoint, floorboundary[j].GetEndPoint(1));
                        Line newline4 = Line.CreateBound(intpoints.XYZPoint, firstintersection);
                        Line newline5 = newline4.CreateReversed() as Line;
                        Floorpart2.Append(newline2);
                        Floorpart2.Append(newline4);
                        Floorpart1.Append(newline5);
                        Floorpart1.Append(newline3);
                    }
                    placeholder = j + 1;
                    break;
                }
                else
                { Floorpart2.Append(floorboundary[j]); }
            }
            intarray.Clear();
            for(int k = placeholder; k < floorboundary.Count(); k++)
            {
                Floorpart1.Append(floorboundary[k]);
            }
            List<CurveLoop> FirstSide = new List<CurveLoop>() { Floorpart1 };
            List<CurveLoop> LastSide = new List<CurveLoop>() { Floorpart2 };
            //should probably test if the curveloops are open before continueing
            bool test1 = Floorpart1.IsOpen();
            bool test2 = Floorpart2.IsOpen();
            using (Transaction tr = new Transaction(_doc))
            {
                tr.Start("Create new floors");
                Floor.Create(_doc, FirstSide, testfloor.FloorType.Id, testfloor.LevelId);
                Floor.Create(_doc, LastSide, testfloor.FloorType.Id, testfloor.LevelId);
                _doc.Delete(testfloor.Id);
                tr.Commit();
            } 
            return Result.Succeeded;
        }
    }
    public class FloorFilter : ISelectionFilter
    {
        //filter for modeled elements
        public bool AllowElement(Element elem)
        {
            if (elem.Category != null)
            {
                if (elem.Category.Id.IntegerValue.Equals((int)BuiltInCategory.OST_Floors))
                {
                    return true;
                }
                else { return false; }
            }
            else { return false; }
        }
        public bool AllowReference(Reference refer, XYZ loc)
        {
            return true;
        }
    }
    public class RefPlaneFilter : ISelectionFilter
    {
        //filter for modeled elements
        public bool AllowElement(Element elem)
        {
            if (elem.Category != null)
            {
                if (elem.Category.Id.IntegerValue.Equals((int)BuiltInCategory.OST_CLines))
                {
                    return true;
                }
                else { return false; }
            }
            else { return false; }
        }
        public bool AllowReference(Reference refer, XYZ loc)
        {
            return true;
        }
    }
}

 

Message 7 of 7

Hello @ctm_mka ,

 

I tried the approach through topography, however it was not possible since for some geometries the topography triangulates the elements generating surfaces outside the original slab, additionally I see that you created the code with an API after 2019, I I was working on that version for this reason I did not have access to the SketchID parameter.

 

The procedure works perfectly, you are a legend bro. 😎

Kudos

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

Post to forums  

Forma Design Contest


Rail Community