It’s been a very, very long time since I posted anything. I’ve been spending most my time focusing on Unity, Dynamo View Extensions and XR (and of course my studies and work) which I hope to start posting more about. However, I thought I would revisit the topic of splitting Elements.
As it happens, this is a hot topic as it something we all hate doing and takes ages and for the life of me, I don’t understand why this is not a Built-In function in Revit. But, this is exactly the reason I learned the Revit API in the first place. So, without further ado, here is some code to split columns at Levels.
This code was taken from my original post Split Walls and Columns By Level… and re-inspired by that posts comments (Thanks Brandon) and by a variation by Matteo Cominetti (teocomi) who does some really great stuff which you should check out.
This variation is much more efficient than my previous post – which had a tendency to crash with large amounts of columns. Also, this takes the inputs of Structural Columns and Levels so you can do your own logic for selecting which Columns/Levels you wish to split by (similar to teocomi’s variation). Also also, this allows you to specify an offset for how much above or below the Level you want to split – this is more ideal for steel columns as these tend to be spliced about 1m above Level.
import clr clr.AddReference('ProtoGeometry') from Autodesk.DesignScript import Geometry as geom clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager doc = DocumentManager.Instance.CurrentDBDocument clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.Elements) clr.ImportExtensions(Revit.GeometryConversion) clr.AddReference("RevitAPI") from Autodesk.Revit.DB import * # Convert to list if not list... def tolist(obj1): if hasattr(obj1,"__iter__"): return obj1 else: return [obj1] # Gets the centreline of the column... def GetColumnCentreline(e): crv = None fs = e.Symbol fm = fs.Family if not fm.GetType() == DirectShape: if not fm.IsInPlace: if e.IsSlantedColumn: try: crv = e.Location.Curve.ToProtoType() except: return else: loc = e.Location.Point.ToPoint() bLev = (e.Document.GetElement(e.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).AsElementId()).Elevation + e.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).AsDouble()) * ft2mm tLev = (e.Document.GetElement(e.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).AsElementId()).Elevation + e.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).AsDouble()) * ft2mm crv = geom.Line.ByStartPointEndPoint(geom.Point.ByCoordinates(loc.X,loc.Y,bLev), geom.Point.ByCoordinates(loc.X,loc.Y,tLev)) return crv # Convert Level to DS Plane... def LevelToPlane(l): pt = geom.Point.ByCoordinates(0,0,round(l.Elevation * ft2mm,0)+offset) n = geom.Vector.ZAxis() return geom.Plane.ByOriginNormal(pt,n) # Get the nearest Level above in list of Levels to gien Elevation... def NearestLevelAbove(elev,lvls): lvlAbv = None for l in lvls: if round(l.Elevation*ft2mm,0) > elev: lvlAbv = l break return lvlAbv # Get the nearest Level in list of Levels to gien Elevation... def NearestLevel(elev, lvls): return min(lvls, key=lambda x:abs(round(x.Elevation*ft2mm,0)-elev)) # Calculates the location to split column with parameterised column length (between 0 & 1)... def CalculateSplitParameter(col,lvls): # Switch out for mathematical method to speed up computation... # abs(NewColBaseElevation-LevelAboveElevation)/NewCol length should give split parameter... if col: crv = GetColumnCentreline(col) elev = round(crv.StartPoint.Z,0) l = NearestLevelAbove(elev,lvls) x = geom.Geometry.Intersect(crv,LevelToPlane(l)) if x: x = x return geom.Curve.ParameterAtPoint(crv,x) return None # IN Variables... run = tolist(IN) columns = tolist(UnwrapElement(IN)) lvls = tolist(UnwrapElement(IN)) offset = 0 if IN: offset = tolist(IN) # OUT Variables... outList =  # Main Body... if run: ft2mm = 304.8 lvls = sorted(lvls, key=lambda x: x.Elevation) # Start Transaction to allow for Document modification... TransactionManager.Instance.EnsureInTransaction(doc) for c in columns: # Ensure only Structural Columns are used (modify if Arch Columns category is required) if c.Category.Name == Category.GetCategory(doc,BuiltInCategory.OST_StructuralColumns).Name: arr =  arr.append(c) newCol = None crv = GetColumnCentreline(c) bLvlIndex = lvls.index(NearestLevel(crv.StartPoint.Z,lvls)) tLvlIndex = lvls.index(NearestLevel(crv.EndPoint.Z,lvls)) i = bLvlIndex while i <= tLvlIndex: try: if not newCol: p = CalculateSplitParameter(c,lvls) newCol = doc.GetElement(c.Split(p)) arr.append(newCol) else: p = CalculateSplitParameter(newCol,lvls) newCol = doc.GetElement(newCol.Split(p)) arr.append(newCol) except Exception, ex: arr.append(ex.message) i = i+1 outList.append(arr) else: outList.append("Element is not of Category Structural Column") TransactionManager.Instance.TransactionTaskDone() OUT = outList else: OUT = "Please set Run to True"
Due to the pre-formatting on wordpress, sometimes copying and pasting code doesn’t work as you lose the indentation e.t.c. I have added a link to the working graph which you can download here.
I have tested this a fair amount and only took about 5 minutes (Note: I do have a super computer) to split 400 columns by 10 Levels.
I hope this helps you in your time of need…and you can now just go grab a coffee, put your feet up and let the code do it’s thing. 🙂
September 18, 2018 at 9:10 pm
September 18, 2018 at 9:45 pm
LikeLiked by 1 person
November 21, 2018 at 2:09 am
THANKS VERY MUCH
LikeLiked by 1 person