[spring nodes] alter node "FamilyInstance.ByGeometry

Hey folks,

the past few weeks I developed a workflow to get wall breakthroughs and wall slots into the revit model based on the Springs.FamilyInstance.ByGeometry node.
The short version of this workflow is:
1.) Convert the 3D .dwg filfe from the MEP planer (with the geometrical information of all wall breakthroughs and wall slots) to a .sat file
2.) Run the "Geometry.ImportFromSAT node
3.) Create the voids with Springs.FamilyInstance.ByGeometry node
4.) Cut all voids with walls, floors, etc. by HotGear “CutGeometry” node

The process works very well, BUT as a comfort function it would be nice to create a family instance containing a void AND a solid.
Why creating a solid? After execute step 4 CutGeometry there are only the walls, floors, etc. with cuts left and no more voids visible! For further coordination with the MEP planer a check system for every void is necesarry!
IF the family instance contains besides the void also a solid, a filter in revit can be used (with a extra parameter “checked” YES/NO), which tones every solid e.g. green(=YES) or red(=NO). In this way I can indicate the MEP planer, which voids must be altered.

Summary Question:
Is it possible to alter the python code of the node to create a family instance containing a void and a solid as well?

I tried to code it by myself by just adding one line to the original code, but it won´t work (and all other versions of my code neither).

Here the original code:

#Copyright(c) 2016, Dimitar Venkov
# @5devene, dimitar.ven@gmail.com

import clr
import System
from System.Collections.Generic import *

from itertools import repeat

pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
import sys
sys.path.append("%s\IronPython 2.7\Lib" %pf_path)
import traceback

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
from Autodesk.DesignScript.Geometry import Point as DynPoint

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import StructuralType

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

def output1(l1):
	if len(l1) == 1: return l1[0]
	else: return l1

def PadLists(lists):
	len1 = len(lists[0])
	for i in xrange(1,len(lists)):
		len2 = len(lists[i])
		if len2 == len1 : continue
		elif len2 > len1: lists[i] = lists[i][:len1]
		else : lists[i].extend(repeat(lists[i][-1],len1 - len2))
	return lists

class FamOpt1(IFamilyLoadOptions):
	def __init__(self):
		pass
	def OnFamilyFound(self,familyInUse, overwriteParameterValues):
		return True
	def OnSharedFamilyFound(self,familyInUse, source, overwriteParameterValues):
		return True

geom = tolist(IN[0])
fam_path = IN[1]
names = tolist(IN[2])
category = tolist(IN[3])
material = tolist(IN[4])
isVoid = tolist(IN[5])
subcat = tolist(IN[6])

units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
factor = UnitUtils.ConvertToInternalUnits(1,units)
acceptable_views = ("ThreeD", "FloorPlan", "EngineeringPlan", "CeilingPlan", "Elevation", "Section")
origin = XYZ(0,0,0)
str_typ = StructuralType.NonStructural
t1 = TransactionManager.Instance
temp_path = System.IO.Path.GetTempPath()
satOpt = SATImportOptions()
satOpt.Placement = ImportPlacement.Origin
satOpt.Unit = ImportUnit.Foot
opt1 = Options()
opt1.ComputeReferences = True
SaveAsOpt = SaveAsOptions()
SaveAsOpt.OverwriteExistingFile = True

def NewForm_background(s1, name1, cat1, isVoid1, mat1, subcat1):
	enable_mat = False if mat1 is None else True
	enable_subcat = False if subcat1 is None else True
	TransactionManager.ForceCloseTransaction(t1)
	famdoc = doc.Application.NewFamilyDocument(fam_path)
	message = None
	temp_path = System.IO.Path.GetTempPath()
	sat_path = "%s%s.sat" % (temp_path, name1)
	try:
		if factor != 1:
			s1 = s1.Scale(factor)
		vec1 = Vector.ByTwoPoints(BoundingBox.ByGeometry(s1).MinPoint, DynPoint.Origin())
		s1 = s1.Translate(vec1)
		sat1 = Geometry.ExportToSAT(s1, sat_path)
		view_fec = FilteredElementCollector(famdoc).OfClass(View)
		view1 = None
		for v in view_fec:
			if str(v.ViewType) in acceptable_views and not v.IsTemplate:
				view1 = v
				break
		t1.EnsureInTransaction(famdoc)
		satId = famdoc.Import(sat1, satOpt, view1)
		el1 = famdoc.GetElement(satId)
		geom1 = el1.get_Geometry(opt1)
		enum = geom1.GetEnumerator()
		enum.MoveNext()
		geom2 = enum.Current.GetInstanceGeometry()
		enum2 = geom2.GetEnumerator()
		enum2.MoveNext()
		s1 = enum2.Current
		famdoc.Delete(satId)
		System.IO.File.Delete(sat_path)
	except:
		message = (traceback.format_exc(),"")
		pass
	if message is None:
		try:
			save_path = "%s%s.rfa" % (temp_path, name1)
			try: #set the category
				fam_cat = famdoc.Settings.Categories.get_Item(cat1.Name)
				famdoc.OwnerFamily.FamilyCategory = fam_cat
			except: pass
			s2 = FreeFormElement.Create(famdoc,s1)
			if isVoid1:
				void_par = s2.get_Parameter(BuiltInParameter.ELEMENT_IS_CUTTING).Set(1)
				void_par2 = famdoc.OwnerFamily.get_Parameter(BuiltInParameter.FAMILY_ALLOW_CUT_WITH_VOIDS).Set(1)
			else: #voids do not have a material values or a sub-cateogry
				if enable_mat:
					try:
						mat_fec = FilteredElementCollector(famdoc).OfClass(Material)
						for m in mat_fec:
							if m.Name == mat1:
								fam_mat = m
								break
						mat_par = s2.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM).Set(fam_mat.Id)
					except: pass
				if enable_subcat: #create and assign the sub-category:
					try:
						current_fam_cat = famdoc.OwnerFamily.FamilyCategory
						new_subcat = famdoc.Settings.Categories.NewSubcategory(current_fam_cat, subcat1)
						s2.Subcategory = new_subcat
					except: pass
			TransactionManager.ForceCloseTransaction(t1)
			famdoc.SaveAs(save_path, SaveAsOpt)
			family1 = famdoc.LoadFamily(doc, FamOpt1() )
			famdoc.Close(False)
			System.IO.File.Delete(save_path)
			symbols = family1.GetFamilySymbolIds().GetEnumerator()
			symbols.MoveNext()
			symbol1 = doc.GetElement(symbols.Current)
			t1.EnsureInTransaction(doc)
			if not symbol1.IsActive: symbol1.Activate()
			inst1 = doc.Create.NewFamilyInstance(origin, symbol1, str_typ)
			ElementTransformUtils.MoveElement(doc,inst1.Id, vec1.Reverse().ToXyz() )
			TransactionManager.ForceCloseTransaction(t1)
			return inst1.ToDSType(False), family1.ToDSType(False)
		except:
			message = (traceback.format_exc(),"")
			return message
	else : return message

if len(geom) == len(names) == len(category) == len(isVoid) == len(material) == len(subcat):
	return1 = map(NewForm_background, geom, names, category, isVoid, material, subcat)
elif len(geom) == len(names):
	padded = PadLists([geom, category, isVoid, material,subcat])
	p_category, p_isVoid, p_material, p_subcat = padded[1], padded[2], padded[3], padded[4]
	return1 = map(NewForm_background, geom, names, p_category, p_isVoid, p_material, p_subcat)
else : return1 = [("Make sure that each geometry\nobject has a unique family name.", "")]
OUT = output1([i[0] for i in return1]), output1([i[1] for i in return1])
satOpt.Dispose()
opt1.Dispose()
SaveAsOpt.Dispose()

I tried to alter the following section NewForm_background by just adding one line:

def NewForm_background(s1, name1, cat1, isVoid1, mat1, subcat1):
	enable_mat = False if mat1 is None else True
	enable_subcat = False if subcat1 is None else True
	TransactionManager.ForceCloseTransaction(t1)
	famdoc = doc.Application.NewFamilyDocument(fam_path)
	message = None
	temp_path = System.IO.Path.GetTempPath()
	sat_path = "%s%s.sat" % (temp_path, name1)
	try:
		if factor != 1:
			s1 = s1.Scale(factor)
		vec1 = Vector.ByTwoPoints(BoundingBox.ByGeometry(s1).MinPoint, DynPoint.Origin())
		s1 = s1.Translate(vec1)
		sat1 = Geometry.ExportToSAT(s1, sat_path)
		view_fec = FilteredElementCollector(famdoc).OfClass(View)
		view1 = None
		for v in view_fec:
			if str(v.ViewType) in acceptable_views and not v.IsTemplate:
				view1 = v
				break
		t1.EnsureInTransaction(famdoc)
		satId = famdoc.Import(sat1, satOpt, view1)
		el1 = famdoc.GetElement(satId)
		geom1 = el1.get_Geometry(opt1)
		enum = geom1.GetEnumerator()
		enum.MoveNext()
		geom2 = enum.Current.GetInstanceGeometry()
		enum2 = geom2.GetEnumerator()
		enum2.MoveNext()
		s1 = enum2.Current
		famdoc.Delete(satId)
		System.IO.File.Delete(sat_path)
	except:
		message = (traceback.format_exc(),"")
		pass
	if message is None:
		try:
			save_path = "%s%s.rfa" % (temp_path, name1)
			try: #set the category
				fam_cat = famdoc.Settings.Categories.get_Item(cat1.Name)
				famdoc.OwnerFamily.FamilyCategory = fam_cat
			except: pass
			s2 = FreeFormElement.Create(famdoc,s1)
			s3 = FreeFormElement.Create(famdoc,s1)
				void_par = s2.get_Parameter(BuiltInParameter.ELEMENT_IS_CUTTING).Set(1)
				void_par2 = famdoc.OwnerFamily.get_Parameter(BuiltInParameter.FAMILY_ALLOW_CUT_WITH_VOIDS).Set(1)
			else: #voids do not have a material values or a sub-cateogry
				if enable_mat:
					try:
						mat_fec = FilteredElementCollector(famdoc).OfClass(Material)
						for m in mat_fec:
							if m.Name == mat1:
								fam_mat = m
								break
						mat_par = s2.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM).Set(fam_mat.Id)
					except: pass
				if enable_subcat: #create and assign the sub-category:
					try:
						current_fam_cat = famdoc.OwnerFamily.FamilyCategory
						new_subcat = famdoc.Settings.Categories.NewSubcategory(current_fam_cat, subcat1)
						s2.Subcategory = new_subcat
					except: pass
			TransactionManager.ForceCloseTransaction(t1)
			famdoc.SaveAs(save_path, SaveAsOpt)
			family1 = famdoc.LoadFamily(doc, FamOpt1() )
			famdoc.Close(False)
			System.IO.File.Delete(save_path)
			symbols = family1.GetFamilySymbolIds().GetEnumerator()
			symbols.MoveNext()
			symbol1 = doc.GetElement(symbols.Current)
			t1.EnsureInTransaction(doc)
			if not symbol1.IsActive: symbol1.Activate()
			inst1 = doc.Create.NewFamilyInstance(origin, symbol1, str_typ)
			ElementTransformUtils.MoveElement(doc,inst1.Id, vec1.Reverse().ToXyz() )
			TransactionManager.ForceCloseTransaction(t1)
			return inst1.ToDSType(False), family1.ToDSType(False)
		except:
			message = (traceback.format_exc(),"")
			return message
	else : return message

Basicly I just added one line:
s3 = FreeFormElement.Create(famdoc,s1)
My idea was simply to execute the FreeFormElement method two times in a row! One time as void, the other time as solid. I think the problem is, that the script can´t access the familydocument two times executing the FreeFormElement method. Apparently I have to open the same familydocument (with one void already created in it!) again and then create the solid by FreeFormElement.
I hope you can follow my steps and explanation of my poor coding sklils! :face_with_raised_eyebrow:

As a transitional solution I´ve separated the voids and solids in two nodes

Thanks for your help!
Christoph N.

BTW:
As soon as we have a proper solution, we will relaese a small dynamo package dealing with wall breakthroughs und wall slots! :slight_smile:

1 Like

You should just be able to pass in a list of {true, false} to the isVoid input in order to get the springs node to create one void instance and one solid instance.

1 Like

Don’t forget setting lacing to cross product! :smiley:

Hey guys,

thank you for your replies! Sorry for the long break, but it took me some time to come back dealing with this problem! Now I´m ready for this! :slightly_smiling_face:
Unfortunately I don´t understand, what you two mean exactly with your suggestions! :
I´ve attached the dynamo file below (with some improvements for the automatic naming scheme)
Can you guys (@jacob.small , @Nick_Boyts) show me, how to do it exactly? :wink:

Also I am interested in altering the original pythoncode, if someone can code it!

In advance…thank you for your help, I really appreciate your commitment!
dynamo_bsp.dyn (14.0 KB)

Another way of introducing such a request would be to paste the piece of code you are interested in, explaining what you tried and the researches you have made around. You will see here how to paste code in a post.

Thank you @Yna_Db for the link! :slight_smile:
I´ve edited my Code in the starting post and described my code idea!
Maybe my problem with the pytohn script is a bit clearer now! :wink:

See maybe if you can find useful entry points to this topic in the available documentation