Recently, one of my good friends asked me if it is possible to get the Swept Profile and Path of an In-Place family. They had tried the FamilyInstance.GetSweptProfile, however, In-place families don’t support this (no idea why In-place families have such limited API access, I think only the Gods at Autodesk would know).

Before I start rambling about something unrelated, the answer of course is yes, it is totally possible to do this with an old hack I learnt many years ago while perusing through Jeremy Tammik’s – The Building Coder blog posts.

The Hack…

This may or may not be new to you, but there is a little hack to doing this and it will sound scary when I say it, but it’s all good… promise… 😉

You have to Delete the In-place family…

Yeah, you heard that right. But there is a way to do this in code only and not actually truly delete it. This is where Transactions come into play.

Using the Revit API transactions (not the Dynamo TransactionManager you might be used to), we can use the Rollback method. This allows us to undo whatever we did within that transaction… say deleting an Element in our case.

Also, the Document.Delete Method returns a list of ElementId’s that got deleted from Revit.

So with all these little tit-bits of information, the idea is as follows…

  • Start a new Transaction
  • Delete the In-place FamilyInstance (which returns all the ElementId’s that are deleted with the In-Place Family i.e. Sketches, ModelCurves etc etc)
  • Rollback the Transaction (undo the Delete)
  • Then use the Document.GetElement() method to get the Elements that were deleted but now not.

Does that all make sense…? Wanna see it in action…? Well, here is some python code showing how it is done…

The Code…

In-Place Family | GetSweep | Py

"""
Description: Get the paths and profiles for the given In-Place Families.
Author: Dan Woodcock
Website: https://danimosite.wordpress.com/
Licence: N/A
"""

###### Imports ######

import clr

# Import Revit Services as we need the doc and TransactionManager...
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc =  DocumentManager.Instance.CurrentDBDocument

# Import Revit Nodes and Geometry Extensions...
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

# Import the Revit API...
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

###### Definitions ######

"""
Ensures object is a List and not a singleton.
"""
def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

###### Inputs ######

elems = tolist(UnwrapElement(IN[0])) # The In-Place Elements...

###### Output ######

outList = [] # The output list...

###### Main ######

# Need to force close other transactions here to ensure new transaction can start...
TransactionManager.Instance.ForceCloseTransaction()	

for e in elems:	
	# Check if Family is In-Place...
	if not e.Symbol.Family.IsInPlace:
		outList.append(None)
		continue
		
	eIds = [] # Empty variable to store Deleted ElementIds...
		
	# Create a new Revit API Transaction...
	t = Transaction(doc, "Temp")
	# Start the Transaction...
	t.Start()	
	# Get deleted ElementIds...
	eIds = doc.Delete(e.Id)	
	
	# Rollback the transaction to undo the delete...
	t.RollBack()	
	
	# Try get the sweep Element from the In-Place family...
	sweep = next((doc.GetElement(id) for id in eIds if doc.GetElement(id).GetType() == Sweep or doc.GetElement(id).GetType() == SweptBlend),None)
	
	# Test if there was a sweep. If so, then get Path and Profiles...
	if sweep:
		# If the Sweep is a single profile sweep...
		if sweep.GetType() == Sweep:
			pathSketches = []
			profSketches = []
			
			# Get the Paths in the Sweep...
			for prof in sweep.PathSketch.Profile:
				pathSketches.append([l.ToProtoType() for l in prof])
			
			# Get the Profiles in the Sweep...
			for prof in sweep.ProfileSketch.Profile:
				profSketches.append([l.ToProtoType() for l in prof])
			
			# Add Sketches to output...
			outList.append([pathSketches, profSketches])
			
		#If the Sweep is a Swept Blend...
		if sweep.GetType() == SweptBlend:
			pathSketches = []
			profSketches = []
			
			# Get the Paths in the Sweep...
			for prof in sweep.PathSketch.Profile:
				pathSketches.append([l.ToProtoType() for l in prof])
				
			# Get the Profiles in the Sweep...
			profSketches.append([[l.ToProtoType() for l in sweep.BottomProfile],[l.ToProtoType() for l in sweep.TopProfile]])
			
			# Add Sketches to output...
			outList.append([pathSketches, profSketches])

###### Output ######

OUT = outList

Note: In the code I am Force Closing transactions with Dynamo’s Transaction Manager, this is because Revit can only open one Transaction at a time, this is ensuring that any open Transaction is well and truly closed before I open a new one.

Note: I also haven’t handled Loaded Profiles (i.e. not from In-Place sketching), this example is particularly focused on the In-Place Sketch Profile but is relatively easy get the Family Type for the Loaded Profile.

Bonus: You could also modify this code to do this for Floors, Roofs and other Sketch Based Elements that have limited API support should you wish.

Anyway, thanks for reading and hope you found this useful! 🙂