I have a problem with my plan creation dynamo schematic.
I require a node to feed a script levels. using the level node it works fine. But i’m creating plans on multiple levels so the level needs to change with the element that is used to create the plan. so I’m now using that element gettnig the level name and then the level id by thet name and feeding that to the custon node with the levels in a list (that same way the rooms which generate the plan are provided) but it’s not working.
That might not be a very clear description but the attached jpeg should be obvious. What am I missing?
It’s hard to help when we can’t see the entire code, but based on the error and at least the portion handling levels, it looks like your code is written for a single element and not a list. You have to write your code to loop through lists. It won’t handle them automatically like Dynamo nodes.
Thanks for the quick reply @Nick_Boyts
Yep that’s the problem isn’t it. I’ve copied the code below. It’s originally by Archi-labs with some modifications since then. I don’t really know how to go about setting it to loop but as it’s doing it for the rooms with this code
if isinstance(IN[0], list):
rooms = UnwrapElement(IN[0])
else:
rooms = [UnwrapElement(IN[0])]
Should I be doing something similar with the
if isinstance(IN[4], list):
levelId = UnwrapElement(IN[4]).Id
else:
levelId = [UnwrapElement(IN[4]).Id]
No doubt it’s more complex than that…Original code below
# Copyright(c) 2015, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)
# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
import System
from System import Array
from System.Collections.Generic import *
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN
if isinstance(IN[0], list):
rooms = UnwrapElement(IN[0])
else:
rooms = [UnwrapElement(IN[0])]
nameSuffix = IN[1]
bboxOffset = IN[2]
runMe = IN[3]
def OffsetBBox(bbox, offset):
bboxMinX = bbox.Min.X - offset
bboxMinY = bbox.Min.Y - offset
bboxMinZ = bbox.Min.Z - offset
bboxMaxX = bbox.Max.X + offset
bboxMaxY = bbox.Max.Y + offset
bboxMaxZ = bbox.Max.Z + offset
newBbox = BoundingBoxXYZ()
newBbox.Min = XYZ(bboxMinX, bboxMinY, bboxMinZ)
newBbox.Max = XYZ(bboxMaxX, bboxMaxY, bboxMaxZ)
return newBbox
try:
levelId = UnwrapElement(IN[4]).Id
errorReport = None
if runMe:
viewTypes = FilteredElementCollector(doc).OfClass(ViewFamilyType)
for i in viewTypes:
if i.ViewFamily == ViewFamily.FloorPlan:
viewTypeId = i.Id
break
else:
continue
existingPlans = FilteredElementCollector(doc).OfClass(View).ToElements()
existingPlanNames, existingPlanElements = [], []
for i in existingPlans:
if not i.IsTemplate:
if i.ViewType == ViewType.FloorPlan:
existingPlanNames.append(i.ToDSType(True).Name)
existingPlanElements.append(i)
# Start Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
floorPlans = []
for i in rooms:
#levelId = i.LevelId
bbox = i.BoundingBox[doc.ActiveView]
newBbox = OffsetBBox(bbox, bboxOffset)
viewName = i.get_Parameter(BuiltInParameter.ROOM_NUMBER).AsString() + " " + i.get_Parameter(BuiltInParameter.ROOM_NAME).AsString().Replace("-", " - ") + " - " + nameSuffix
if viewName in existingPlanNames:
view = existingPlanElements[existingPlanNames.index(viewName)]
view.CropBox = newBbox
view.CropBoxActive = True
view.CropBoxVisible = False
floorPlans.append(view)
else:
newView = ViewPlan.Create(doc, viewTypeId, levelId)
newView.Name = viewName
newView.CropBox = newBbox
newView.CropBoxActive = True
newView.CropBoxVisible = False
floorPlans.append(newView)
# End Transaction
TransactionManager.Instance.TransactionTaskDone()
else:
errorReport = "Run Me set to False"
except:
# if error accurs anywhere in the process catch it
import traceback
errorReport = traceback.format_exc()
#Assign your output to the OUT variable
if errorReport == None:
OUT = floorPlans
else:
OUT = errorReport
Close. The isinstance
portion of the code ensures that no matter how many levels you supply, the code will convert them to a single list so your structure is always the same. The parameter levelId
is what’s used to set the level on each view, but here we need to define the list of levels that will be used. Instead of setting these lists to levelId
, set them to something like levels
.
The second part is getting the code to loop through those levels properly. In the original code you have
levelId = UnwrapElement(IN[4]).Id
which is expecting a single level and just setting that as the parameter to use for all views. This whole line needs to be removed since we now have a list of levels to loop through. We’ll replace it in the next section. A little farther down you have
for i in rooms:
which is where the code loops through each room (i
) in the list of rooms (rooms
). Here is where you would also loop through each room’s respective level. Because each room is paired up with a specific level, we have to loop through the two lists simultaneously. This is done by zipping the lists together.
for i,j in zip(rooms,levels):
levelId = j.Id
This code ensures that our rooms and levels are looped through together, in their respective pairs. The second line is where we get the levelId
from each individual level as we loop through the list.
I think that’s all you’ll need to do at this point. Hopefully that explains it all.
Thanks @Nick_Boyts that’s really helpful
I’ll try that out when I get a break and get back to you.
Tried this morning. What you say makes sense to me but my Python execution is sadly lacking… I don’t really understand j.Id and I’m getting
Sorry I bodge my way through this stuff without a proper education in it but the results save me hundreds of hours. I’m utterly indebted to people like yourselves.
# Copyright(c) 2015, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)
# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
import System
from System import Array
from System.Collections.Generic import *
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN
if isinstance(IN[0], list):
rooms = UnwrapElement(IN[0])
else:
rooms = [UnwrapElement(IN[0])]
nameSuffix = IN[1]
bboxOffset = IN[2]
runMe = IN[3]
def OffsetBBox(bbox, offset):
bboxMinX = bbox.Min.X - offset
bboxMinY = bbox.Min.Y - offset
bboxMinZ = bbox.Min.Z - offset
bboxMaxX = bbox.Max.X + offset
bboxMaxY = bbox.Max.Y + offset
bboxMaxZ = bbox.Max.Z + offset
newBbox = BoundingBoxXYZ()
newBbox.Min = XYZ(bboxMinX, bboxMinY, bboxMinZ)
newBbox.Max = XYZ(bboxMaxX, bboxMaxY, bboxMaxZ)
return newBbox
levels = UnwrapElement(IN[4])
try:
errorReport = None
if runMe:
viewTypes = FilteredElementCollector(doc).OfClass(ViewFamilyType)
for i in viewTypes:
if i.ViewFamily == ViewFamily.FloorPlan:
viewTypeId = i.Id
break
else:
continue
existingPlans = FilteredElementCollector(doc).OfClass(View).ToElements()
existingPlanNames, existingPlanElements = [], []
for i in existingPlans:
if not i.IsTemplate:
if i.ViewType == ViewType.FloorPlan:
existingPlanNames.append(i.ToDSType(True).Name)
existingPlanElements.append(i)
# Start Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
floorPlans = []
for i,j in zip(rooms,levels):
levelId = j.Id
#levelId = i.LevelId
bbox = i.BoundingBox[doc.ActiveView]
newBbox = OffsetBBox(bbox, bboxOffset)
viewName = i.get_Parameter(BuiltInParameter.ROOM_NUMBER).AsString() + " " + i.get_Parameter(BuiltInParameter.ROOM_NAME).AsString().Replace("-", " - ") + " - " + nameSuffix
if viewName in existingPlanNames:
view = existingPlanElements[existingPlanNames.index(viewName)]
view.CropBox = newBbox
view.CropBoxActive = True
view.CropBoxVisible = False
floorPlans.append(view)
else:
newView = ViewPlan.Create(doc, viewTypeId, levelId)
newView.Name = viewName
newView.CropBox = newBbox
newView.CropBoxActive = True
newView.CropBoxVisible = False
floorPlans.append(newView)
# End Transaction
TransactionManager.Instance.TransactionTaskDone()
else:
errorReport = "Run Me set to False"
except:
# if error accurs anywhere in the process catch it
import traceback
errorReport = traceback.format_exc()
#Assign your output to the OUT variable
if errorReport == None:
OUT = floorPlans
else:
OUT = errorReport
No wait I got it working. so j is from the list and appending it with .Id tells it it’s an ID. It’s running!
@wildtypitch you are feeding your IN[4] a list of strings and not a list of levels, revert to the original snapshot you posted, it shows that its a list of levels, which will work in that case I suppose.