CopyElements Method can not rehost Elements.

CopyElements Method can not rehost Elements.

kimtox
Participant Participant
1,586 Views
16 Replies
Message 1 of 17

CopyElements Method can not rehost Elements.

kimtox
Participant
Participant

Hello,

 

I am trying to copy elements (FamilyInstance) from linked document to the host one (Exact at the same place).

First of all, I was able to copy them manually with pasting from the clipboard. The following option worked for me as expected.

Aligned_to_place.png

But the following snippet of code does not work as expected:

 

 

 

 

// Standard plugin staff including defining currrentDoc and Execute method

IEnumerable<RevitLinkInstance> linkInstances = new FilteredElementCollector(doc).OfClass(typeof(RevitLinkInstance)).Cast<RevitLinkInstance>();

Document linkedDoc = null;
Transform transform = null;
foreach (RevitLinkInstance inst in linkInstances)
    {
        if (inst.Name.Contains(nameLink))
        {
                    linkedDoc = inst.GetLinkDocument();
                    transform = inst.GetTransform();
         }
     }

// other staff including checking linkedDoc and transform for null values...

Reference selectedElement = uidoc.Selection.PickObject(ObjectType.LinkedElement, "Choose Family from the linked file");
ElementId linkedElementId = selectedElement.LinkedElementId;
FamilyInstance famInst = linkedDoc.GetElement(linkedElementId) as FamilyInstance;
View linkedView = new FilteredElementCollector(linkedDoc).OfClass(typeof(View)).Where(view => view.Name == viewName && linkedDoc.GetElement(view.GetTypeId()).Name  == viewFamilyName).Cast<View>().First();
ISet<ElementId> ids = new FilteredElementCollector(linkedDoc, linkedView.Id).OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>().Where(x => x.Symbol.Family.Id == famInst.Symbol.Family.Id).Select(x => x.Id).ToHashSet();

Transaction tx = new Transaction(doc);
tx.Start("copyFamily");
ElementTransformUtils.CopyElements(linkedDoc, ids, currrentDoc, transform, new CopyPasteOptions());
tx.Commit();

 

 

 

 

I get the following warning:

Exception.png

 

I have also tried view-specific form of the CopyElements method but the result was the same. I would highly appreciate any ideas of such behavior as well as any advice of using another methods for my purpose.

Accepted solutions (1)
1,587 Views
16 Replies
Replies (16)
Message 2 of 17

alanmlevy
Contributor
Contributor

Is that the correct transform to put the families in the correct spot in the other document? Have you tried it with a non-hosted family to confirm? I see there is a GetTotalTransform() method on the Instance, just wondering if that might work?

Message 3 of 17

kimtox
Participant
Participant
Hi ALan

Thanks for your teply.

Yes I have tried it with non-hosted family in the same documents. It works. I also tried the following option:
ElementTransformUtils.CopyElements(linkedDoc, ids, currrentDoc, null, null);

But the result is the same.
0 Likes
Message 4 of 17

kimtox
Participant
Participant

You pushed me to the right direction I suppose, and I found the possible root cause of such behavior.

So, the think is FamilyInstance inside my RevitLinkInstance has it's own Transform which is different from RevitLinkInstance Transform (BasisX and BasisY does not match).

But the thing is that these 2 options do not work too, and I still have the same error.

 

 

 

transform = (linkedDoc.GetElement(linkedElementId) as FamilyInstance).GetTotalTransform();

// THIS ONE DOES NOT WORK TOO

LocationPoint lp = (famInst.Location as LocationPoint).Point;
XYZ placePoint = (linkedDoc.GetElement(linkedElementId) as FamilyInstance).GetTotalTransform().OfPoint(lp);
transform = Transform.CreateTranslation(placePoint);

 

 

 

 I think that I should determine the difference between two Transforms and use the result Transformation in CopyElements method.

But now I'm trying to find the good option for doing it, and I still would appreciate any advice.

0 Likes
Message 5 of 17

alanmlevy
Contributor
Contributor

May that's it. Maybe you need to apply the transform for the element, and then apply the transform between the documents (or vice versa, I believe order is important here). Doing something similar to what you did, I looked at the transform of the linked element, and then the transform of the pasted element, and they are not equal, so it's likely you are missing a transform. For testing purposes, maybe try getting the transform of the pasted instance, and then try hard-coding that transform in, to see if you can paste an element in that way?

 

alanmlevy_0-1680622850620.png

 

0 Likes
Message 6 of 17

alanmlevy
Contributor
Contributor

That looks like it might be it, just did a quick test in Excel. The multiplication of the two basis matrices and then adding the origin columns produces the Transform of the copy almost exactly: 

alanmlevy_0-1680623840083.png

 

0 Likes
Message 7 of 17

kimtox
Participant
Participant

Thanks for nice idea!

But I have compared it already and even have this result (see screenshot below). But I still have the same error when use the bottom transform in CopyElements method. Is it possible that I need to pass another transformation in CopyElements method?

P.S. I have tried to apply both: linkDoc.GetTransform x  linkElem.GetTransform OR linkElem.GetTransform x  linkDoc.GetTransform

TransformComparing.png

0 Likes
Message 8 of 17

alanmlevy
Contributor
Contributor
Does GetTotalTransform() give you different numbers? Mine were based on total transform.
0 Likes
Message 9 of 17

kimtox
Participant
Participant
Sorry, my bad.
I used GetTotalTransform() but use shortcut version to fit label in screenshot.
0 Likes
Message 10 of 17

alanmlevy
Contributor
Contributor
what were the actual basis vectors for linkDoc and linkElem?
0 Likes
Message 11 of 17

alanmlevy
Contributor
Contributor
If that transform doesn't work, I'm not sure what the issue is. I realized that the multiplication looks correct since those numbers are essentially 0, it's just not displaying correctly. I assume you used WriteLine on the XYZ objects to get that output - it looks like it won't print the doubles correctly unless you reference them directly from the XYZ, instead of using ToString on the XYZ object.

If you place a non-hosted element with that Transform, will it show up where you would expect it to?
0 Likes
Message 12 of 17

kimtox
Participant
Participant

I used formatted string, that's why it was rounded that way 😉

 

Yes, it will show up as expected. Moreover, I have hosted FamilyInstances and they can be copied with my script. But they have the identical Transformation Basis axes with RevitLinkInstance they placed in. 

TransformComparing_2.png

 

0 Likes
Message 13 of 17

kimtox
Participant
Participant

By the way my LinkedElement is FamilyInstance with one more nested FamilyInstance.

I am not sure that it's important but I don't know what else could be worth here...  

0 Likes
Message 14 of 17

Balaji_07
Advocate
Advocate

Is it possible to set the host for the copied hosted elements using Rehost API?

https://www.revitapidocs.com/2023/a222958c-4b12-075b-ade4-d78642c40d90.htm

0 Likes
Message 15 of 17

kimtox
Participant
Participant

Hello, Balaji,

 

Yes, I can do this for elements which were copied manually through the UI.

Message 16 of 17

RPTHOMAS108
Mentor
Mentor

This is all getting a bit complicated.

 

Yes the element in the link document has a transform but that defines its location in the linked document.

 

"Copies are placed at their respective original locations or locations specified by the optional transformation"

 

I read that to mean the element has a location defined (the location in the link measured from the origin in the link) a transform provided will then transform the element from that original location in the link (but represented as measured from the origin of the document that hosts the link). So how do you get the same position in the document that hosts the link? Provide the transform related to the link instance. As an opposite example if you provide an identity transform then you should find the copied element has the same relationship to the document origin it is copied into as the original linked element had to the origin of the linked document (it is not the case that it is an absolute and ends up sitting directly on the origin of the main document hosting the linked document).

 

I suggest trying a simple test with non hosted elements to see if supplying the transform from RevitLinkInstance.GetTransform is landing the non-hosted element where you expect it to be. 

 

"This method performs rehosting of elements where applicable."

 

This above statement from the RevitAPI.chm is a bit vague since there are two possibilities:

 

1) A host element is copied to destination as a new element and hosted elements are copied with it as new hosted elements.

2) Hosted elements are copied to a destination without their host where a new host already exists and they become hosted on it.

 

I think you are now at the stage where you should be producing a minimal reproducible case so that even if you got it wrong you can be given instructions on how to get it right. Alternatively perhaps you got it right to start with and this is just not working as it should in comparison to the UI. It is hard to make judgements without a project file example for these kind of things.

 

Instance.GetTotalTransform

"Gets the total transform, which includes the true north transform for instances like import instances."

 

I believe this is used with respect to shared coords since true north is a feature of those.

 

"For most of other instances, it simply returns the inherent transform."

 

Message 17 of 17

kimtox
Participant
Participant
Accepted solution

Hello, Richard,

 

Thanks a lot for such detailed analysis of my problem and revit API docs.

Sure, I am going to provide my simple project and simplified (but working) code of my Addin.

But actually we modified the family a few minutes ago and now it works. The thing was in that small detail line orienting the array. 

kimtox_0-1680802693784.png

Old family was an array of family instances and this line was hidden in 3D view. So, during my selection I could not select the whole array but could select only it's separate member and copying was not working. But confusing thing was that if I select the same separate member on the same view from the UI It works like a charm (the whole array was copied).

 

Addin code

using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;

namespace _25_CopyFamilyInstances
{
    [Transaction(TransactionMode.Manual)]
    internal class Class1 : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Document currrentDoc = uidoc.Document;

            Selection sel = uidoc.Selection;

            IEnumerable<RevitLinkInstance> linkInstances = new FilteredElementCollector(currrentDoc).OfClass(typeof(RevitLinkInstance)).Cast<RevitLinkInstance>();

            Document linkedDoc = null;
            Transform transform = null;
            string nameLink = "00_LinkedFile";
            foreach (RevitLinkInstance inst in linkInstances)
            {
                if (inst.Name.Contains(nameLink))
                {
                    linkedDoc = inst.GetLinkDocument();
                    transform = inst.GetTransform();
                }
            }

            Reference selectedElement = uidoc.Selection.PickObject(ObjectType.LinkedElement, "Choose Family from the linked file");
            ElementId linkedElementId = selectedElement.LinkedElementId;
            FamilyInstance famInst = linkedDoc.GetElement(linkedElementId) as FamilyInstance;
            ISet<ElementId> ids = new FilteredElementCollector(linkedDoc).OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>().Where(x => x.Symbol.Family.Id == famInst.Symbol.Family.Id).Select(x => x.Id).ToHashSet();

            Transaction tx = new Transaction(currrentDoc);
            tx.Start("copyFamily");
            ElementTransformUtils.CopyElements(linkedDoc, ids, currrentDoc, transform, new CopyPasteOptions());
            tx.Commit();
            return Result.Succeeded;
        }
    }
}

 

0 Likes