Placing a new family on a face in Python

python
dynamo

#1

Hi,

I’m putting together a routine which places some bricks on a generic model face.

Currently I’m using Dynamo to place the face based ‘Brick’ family, but due to issues with Dynamo removing the ‘bricks’ from every time the routine is rerun I’m now looking at placing the families using Python.

The API has this method for placing face based families which looks like the one to use:

NewFamilyInstance(Face, XYZ, XYZ, FamilySymbol) Inserts a new instance of a family onto a face of an existing element, using a location, reference direction, and a type/symbol.

So I’ve put this code together to place using the above method:

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

# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

# Import Revit elements
from Revit.Elements import *

# Import DocumentManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager

import sys
sys.path.append('C:/Program Files (x86)/IronPython 2.7/Lib')

from math import atan2, radians, cos, sin, degrees, sqrt, ceil

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
adoc = doc.ActiveView

facetoplace 	= IN[0][0]
familysym		= IN[1]

#NewFamilyInstance(Face, XYZ, XYZ, FamilySymbol) Inserts a new instance of a family onto a face of an existing element, using a location, reference direction, and a type/symbol.

#Find family Symbol
familysymname = "Brick"
familysymtouse = 0

collector = FilteredElementCollector(doc).OfClass(FamilySymbol)
 
famtypeitr = collector.GetElementIdIterator()
famtypeitr.Reset()

for item in famtypeitr:
	getfelement = doc.GetElement(item)
	getffamily 	= getfelement.Family
	getfname 	= getffamily.Name 
	
	if getfname == familysym:
		familysymtouse = getfelement

b1 = doc.Create.NewFamilyInstance(facetoplace, XYZ(1,0,0), XYZ(0,0,0), familysymtouse)

OUT = [familysymtouse]

This is the Dynamo part:

When I run the code, it fails with the NewFamilyInstance with the error: TypeError: expected XYZ, got Surface.

I believe I’ve given the code all the correct inputs, so I can’t see why the code fails.

I’d appreciate some pointers.

Thanks.


#2

Try unwrapping the surface?


#3

Try to convert the surface to Revit type,

revitGeometryObject = dynamoGeometry.ToRevitType()

And add a transaction.


#4

Ok, I made the following amendments:

Added UnWrapElement - this had no effect, same error

Added a Ensure Transaction - this had no effect too, I don’t think its required as since updating to Dynamo 1.0 onwards I’ve not needed a Transaction for amending the database (guess it uses Dynamo’s transaction).

Added the convert to Revit type - this changed the error slightly to TypeError: expected XYZ, got List[GeometryObject], I guess what this tells me is that its the Surface which is being passed to the Create.NewFamilyInstance which is causing the issue.

I still don’t quite understand why the error is ‘expected XYZ’ when the API documentation clearly states that there is a method which starts with a face: NewFamilyInstance(Face, XYZ, XYZ, FamilySymbol)

Perhaps I’ll try getting the face in Python rather than using the Dynamo Select Face node…

I’ve attached the Dynamo file with the updated code:Python place family on face 03.dyn (5.8 KB)

Thanks for your responses!


#5

I added Topology.Faces to find the only face on the surface:

This definitely passes a face to the routine, but the error is now ‘TypeError: expected XYZ, got Face’

For some reason the method is still expecting an XYZ.

I there some sort of ‘switch’ to tell NewFamilyInstance that I’m using the Face, XYZ, XYZ, FamilySymbol method?


#6

Hi @Kevin.Bell

There are other OOTB nodes you can use to place family on a face.

And also there is a custom node from springs package called Family.InstanceByFacePoints


#7

Thanks Kulkul, that works, plus I also managed to get the OOTB nodes to work, but I was looking for a python solution.

Just cant work out why the Python solution gives the error.

Cheers.


#8

Hi Kevin,
There is some working python code in the Springs package: Springs.FamilyInstance.ByFacePoints that uses this method. I have pasted it below. Thanks to @Dimitar_Venkov

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

import clr

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

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)

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

def first(obj1):
	if hasattr(obj1,'__iter__'): return obj1[0]
	else: return obj1

host = first(IN[0])
ftype = UnwrapElement(first(IN[1]))
pts = tolist(IN[2])

OUT = []
ref1 = host.Tags.LookupTag("RevitFaceReference")
TransactionManager.Instance.EnsureInTransaction(doc)
for i in xrange(len(pts) ):
	uv1 = host.UVParameterAtPoint(pts[i])
	dir1 = host.TangentAtUParameter(uv1.U,uv1.V).ToXyz()
	try:
		inst1 = doc.Create.NewFamilyInstance(ref1,pts[i].ToXyz(),dir1,ftype)
		OUT.append(inst1.ToDSType(False) )
	except: OUT.append(None)
TransactionManager.Instance.TransactionTaskDone()

I have found that if i try to use NewFamilyInstance(Face, XYZ, XYZ, FamilySymbol) method in any other way (even the most basic single call) i also get a type error such as TypeError: expected XYZ, got Reference

@Dimitar_Venkov could you possibly explain why your call to this method above works, but mine (below) doesn’t? I’ve simplified it as much as possible removing any loops, and making each input a single item. Ideally i want to be able to call NewFamilyInstance within a my own class method. Also i’m curious how you found ref1 = face.Tags.LookupTag("RevitFaceReference")

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

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)
#----------------------------------------------------------INPUTS
familyType = IN[0]
face       = IN[1]
origin     = IN[2]
direction  = IN[3]
#----------------------------------------------------------DEFINITIONS

#----------------------------------------------------------MAIN

ref1 = face.Tags.LookupTag("RevitFaceReference")

TransactionManager.Instance.EnsureInTransaction(doc)
#create instance
e = doc.Create.NewFamilyInstance(ref1, origin.ToXyz(), direction.ToXyz(), familyType)

TransactionManager.Instance.TransactionTaskDone()

#----------------------------------------------------------OUT
OUT = e

#9

It seems like you’re not unwrapping the familyType anywhere. You’ll need to familyType.InternalElement or UnwrapElement(familyType) on it to get the underlying revit type.


#10

Thanks dimitar, I’ll try that when im back in the offfice. The exception message “TypeError: expected XYZ, got Reference” is quite misleading! Would be nice if it said “TypeError: expected FamilySymbol, got Wrapper”


#11

I guess the problem arises from the fact that this specific method has multiple overrides that take different input types. When the API can’t find an override that matches your input, most likely it just defaults to the first one in the list and you get an irrelevant error.

Another common problem is that some internal Revit classes and Dynamo’s equivalent wrapper class share the same name. Even tho they belong to different namespaces, the error would omit the namespace(i.e. we have an Autodesk.Geometry.Line and Autodesk.Revit.DB.Line) and you’d end up with a wtf error along the lines of “Warning expected Line, got Line” :slight_smile:

The team has tried to identify as many of these cases and rename them to prevent this (for example family types were up to recently called FamilySymbol in Dynamo too), however sometimes there is no logical alternative and the ambiguity is unavoidable.