I'm writing this in python, as it's a Dynamo node, but I figured since I'm only really using the API it would fit better here. Let me know if I need to move it to the Dynamo forums.
With that out of the way:
I have a number of fully annotated Floor Plans that I'd like to convert to Detail Views. Revit doesn't supply us with a way to do this, so I thought I'd create a half measure by using CopyElements(View, ICollection(ElementId), View, Transform, CopyPasteOptions). And, well, it works when copying the annotation from one floor plan to another, and it technically works copying annotation from a floor plan to a detail view, but it pastes it in the wrong location. As the title says.
I've tried creating an addition transformation by subtracting the source view origin from the destination view origin, but that just put it in a different, still wrong spot.
Here's my code:
# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit
# Import Revit elements & geometry conversion methods
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
#The System namespace at the root of .NET
import System
from System.Collections.Generic import List
# Import RevitAPI - This gives general access to Revit tools.
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
# Import DocumentManager & TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#Active Revit file
doc = DocumentManager.Instance.CurrentDBDocument
#UI level interfaces
uiapp = DocumentManager.Instance.CurrentUIApplication
#Current instance of Revit
app = uiapp.Application
#UI level interfaces of the current instance of Revit
uidoc = uiapp.ActiveUIDocument
# The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN
bic = Autodesk.Revit.DB.BuiltInCategory
#Functions
def collect_anno(view):
fil_cats = [bic.OST_CableTrayTags, bic.OST_ConduitTags, bic.OST_Dimensions, bic.OST_TextNotes]
fil_cats = List[BuiltInCategory](fil_cats)
cat_fil = ElementMulticategoryFilter(fil_cats)
return FilteredElementCollector(doc, view.Id).WherePasses(cat_fil).ToElements()
#Start Code
sel = uidoc.Selection.GetElementIds()
sel = [doc.GetElement(x) for x in sel]
sorted_views = sorted(sel, key=lambda x: x.Name)
split_idx = len(sorted_views)//2
source = sorted_views[:split_idx]
dest = sorted_views[split_idx:]
elems = [collect_anno(x) for x in source]
elems = [List[ElementId](y.Id for y in x) for x in elems]
z = zip(source,elems,dest)
op = CopyPasteOptions()
TransactionManager.Instance.EnsureInTransaction(doc)
copy = [ElementTransformUtils.CopyElements(s,e,d,None,op) for (s,e,d) in z]
TransactionManager.Instance.TransactionTaskDone()
OUT = copy
I know I could just manually copy and paste aligned, but I was hoping for a batch solution. I'm sort of stumped at what else to look at here. Thought I'd pop in and ask for some help.
Nice question, nice code! I don't see the transform in your code; looks like None to me. Don't know how the different views' origins are defined. Maybe the inverse transform of the one you mention? How about placing one single element, e.g., a circle, at the source origin and seeing where it ends up in the destination? That will at least tell you what is currently happening and what transformation is being applied.
Thank you! Quite the compliment coming from you - I use your blog all the time. 😉
I did the transform in a duplicate node where I was just messing around some, but it's probably smart to include that as well.
#Start Code
bic = Autodesk.Revit.DB.BuiltInCategory
#Functions
def collect_anno(view):
fil_cats = [bic.OST_CableTrayTags, bic.OST_ConduitTags, bic.OST_Dimensions, bic.OST_TextNotes]
fil_cats = List[BuiltInCategory](fil_cats) #Turn into iCollection
cat_fil = ElementMulticategoryFilter(fil_cats)
return FilteredElementCollector(doc, view.Id).WherePasses(cat_fil).ToElements()
s = doc.GetElement(ElementId(15395582))
#e_int = [17203939,17203943,17203946,17203951,17204059,17204147,17206809,17207797,17871734,17871772,17871973]
e_int = [17207797,17888933]
e = [ElementId(x) for x in e_int]
e = List[ElementId](e)
d = doc.GetElement(ElementId(17888660))
d2 = doc.GetElement(ElementId(17871980))
cop = CopyPasteOptions()
t = Transform
t1=t.CreateTranslation(s.Origin.Subtract(d.Origin))
TransactionManager.Instance.EnsureInTransaction(doc)
c = ElementTransformUtils.CopyElements(s,e,d,t1,cop)
TransactionManager.Instance.TransactionTaskDone()
OUT = c
Using the inverse transform didn't do much, but put it on the other side of the non-transformed paste location.
That said, I think I solved it. I was about to try what you suggested with placing a single element at the source origin, when I noticed something interesting - The origin for the destination view is almost, if not exactly, the distance away that it's pasting the annotation at.
The origin for the destination view is (93.675, 2.915, 108.333)
With that in mind, using the translation " t1=t.CreateTranslation(XYZ(0,0,d.Origin.Z).Subtract(d.Origin))" it seems to put that "Front" text note in the correct spot. It also worked for the rest of the annotation.
I'll try implementing this in the main code, and make sure it works for all views, not just this one.
Thank you for your appreciation and congratulations on the good progress!
Okay, I'm close. The script works - it copies all the annotation to the correct spot except my cable tray and conduit tags. I double checked and saw that they're not turned off in the view, and when I try to Select by ID, based on the output of the CopyElements method, Revit says it's an invalid ID.
I know the CopyElements method I'm using only works on view specific elements, but I'm pretty sure tags are included in that are they not?
Here's the code:
#Functions
bic = Autodesk.Revit.DB.BuiltInCategory
def collect_anno(view):
fil_cats = [bic.OST_CableTrayTags, bic.OST_ConduitTags, bic.OST_Dimensions, bic.OST_TextNotes]
fil_cats = List[BuiltInCategory](fil_cats) #Turn into iCollection
cat_fil = ElementMulticategoryFilter(fil_cats)
return FilteredElementCollector(doc, view.Id).WherePasses(cat_fil).ToElements()
def translation(view):
origin = XYZ(0,0,view.Origin.Z) #Get the project origin, adjusting to be on the same plane as the view.
tfm_vector = origin.Subtract(view.Origin) #Get the distance to the origin
t = Transform
return t.CreateTranslation(tfm_vector)
#Start Code
sel = uidoc.Selection.GetElementIds()
sel = [doc.GetElement(x) for x in sel]
if len(sel)%2 == 1:
OUT = 'Uneven view selection'
else:
sorted_views = sorted(sel, key=lambda x: x.Name)
split_idx = len(sorted_views)//2
source = sorted_views[:split_idx]
dest = sorted_views[split_idx:]
tfm = [translation(x) for x in dest]
elems = [collect_anno(x) for x in source]
elems = [List[ElementId](y.Id for y in x) for x in elems]
z = zip(source,elems,dest, tfm)
op = CopyPasteOptions()
TransactionManager.Instance.EnsureInTransaction(doc)
copy = [ElementTransformUtils.CopyElements(v1,e,v2,t,op) for (v1,e,v2,t) in z]
TransactionManager.Instance.TransactionTaskDone()
OUT = copy
Can't find what you're looking for? Ask the community or share your knowledge.