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).
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
Solved! Go to Solution.
Solved by ctm_mka. Go to Solution.
@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.
@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.
@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.
@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?
@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:
Here's how it works:
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;
}
}
}
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.