Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Edge reference of a family instance

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
Anonymous
4960 Views, 11 Replies

Edge reference of a family instance

I'm trying to create a tool that will host an adaptive component on an edge in the project environment. The tool should prompt for an adaptive family to host and then for the edge to host it on.

 

I created the following helper method:

 

 

        Function HostOnEdge(document As Document, familyTohost As FamilySymbol, edgeReference As Reference, normalizedParameter As Double) As FamilyInstance

            'create a family instance and get it's adaptive points
            Dim familyInstance As FamilyInstance = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(document, familyTohost)
            Dim placementPoints As IList(Of ElementId) = AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(familyInstance)

            'create reference point
            Dim location As New PointLocationOnCurve(PointOnCurveMeasurementType.NormalizedCurveParameter, normalizedParameter, PointOnCurveMeasureFrom.Beginning)
            Dim pointOnEdge As PointOnEdge = document.Application.Create.NewPointOnEdge(edgeReference, location)

            'attach first adaptive point to ref point
            Dim firstPoint As ReferencePoint = TryCast(document.GetElement(placementPoints(0)), ReferencePoint)
            firstPoint.SetPointElementReference(pointOnEdge)

            Return familyInstance

        End Function

 

 

I then call it from an external command as follows:

 

            ....

'select family to host Dim selectedElementId As ElementId = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, "Select Element To Host:").ElementId 'select edge Dim selectedEdge As Reference = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Edge) 'get the selected elements family symbol Dim familyInstance As FamilyInstance = TryCast(Document.GetElement(selectedElementId), FamilyInstance) Dim familySymbol As FamilySymbol = familyInstance.Symbol 'create hosted element HostOnEdge(Document, familySymbol, selectedEdge, 0.5)

....

 

If I select a system family like a floor edge or a wall edge as the host it works as expected. (see attached GIF) If i select an edge of a loaded family such as a beam an internal exception is thrown (snippet attached). What is the correct way to create a pointOnEdge on an edge of a family instance?

11 REPLIES 11
Message 2 of 12
FAIR59
in reply to: Anonymous

the cause of your problem, Revit has 2 ways of calculating solids.

see my answer to https://forums.autodesk.com/t5/revit-api-forum/incorrect-face-normal/td-p/7108787

 

In this case, I think it's an error in the Revit-API.  The perfect valid EdgeReference ( of the "SolidInstance") apparently can't be used in the  firstPoint.SetPointElementReference() method. I say perfect valid, because you can use the EdgeReference for the creation of a Dimension without a problem.

 

If you have a familyInstance that is cut, joined (or Coped) your code will work.

 

 

 

 

 

 

Message 3 of 12
Anonymous
in reply to: FAIR59

Thanks for the tip. As suggested if the "host" family is joined/cut/copped by another element it works as expected. If not the exception is thrown.

Message 4 of 12
jeremytammik
in reply to: Anonymous

Dear Brett,

 

Thank you for your query, and many thanks to Fair59 for his many invaluable answers!

 

This and related issues have come up a number of times in the past, besides the thread that Fair59 points to on the incorrect face normal:

 

 

The question is whether the family instance geometry is returned in local or world coordinates, in LCS or WCS.

 

Another example:

 

 

Some related blog posts:

 

 

Have you tried using the `GetInstanceGeometry` method taking a `Transform` argument, and passing in an identity transform to it?

 

 

The docs recommend avoiding it in general, but you might call that as an alternative if you hit your exception.

 

I hope this helps.

 

Please do let us know how you solve this, since a blog post summarising the situation and providing the ultimate answer is long overdue.

 

Thank you!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 12
jeremytammik
in reply to: jeremytammik

By the way, The Building Code never discussed the AdaptiveComponentInstanceUtils class... can you say something about what it is, how you use it, in what way you find it useful, what functionality it provides not found elsewhere, SDK samples demonstrating its use, etc.?

 

Thank you!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 6 of 12
FAIR59
in reply to: jeremytammik

@jeremytammik nice collection of documentation / information. Sadly this problem has to do with hosting a Adaptive Point on a "real" Edge, so work arounds creating clones won't solve the issue.

 

I still think it's a bug in the Reference.SetPointElementReference(pointOnEdge) method. Looks to me there is a transformation to world coordinates missing somewhere.

 

  • For a Element with "in situ" geometry (element cut, joined or coped), Brett's code works. 
  • Reworking the code to get a reference to a Voodoo-magic Edge (no transformation required) , the code works.

Only in the circumstances of the query does the code fail. (transformation to world coordinates required)

 

Having said that, we can trick Revit into placing the adaptive family in the right place.

These are the steps:

 

  1. Find start and End point of selected Edge.
  2. Place a dummy Element
  3. Join the dummy with the FamilyInstance containing the selected Edge, creating "in situ " geometry.
  4. Find the new Edge Reference of the "in Situ" geometry  with correct start and End point.
  5. Place adaptive Family on that Edge.
  6. Delete dummy element.
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]

    public class Fa_HostAdaptiveFamilyOnEdge_Trick : IExternalCommand
    {
        public Result Execute(ExternalCommandData revit, ref string message, ElementSet elements)
        {
            Document doc = revit.Application.ActiveUIDocument.Document;
            var selectedElementId = ElementId.InvalidElementId;
            //select family to host
            try
            {
              selectedElementId   = revit.Application.ActiveUIDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, "Select Element To Host:").ElementId;
            }
            catch
            {
                return Result.Failed;
            }
            //get the selected family symbol to host on edge
            var familyInstance = doc.GetElement(selectedElementId) as FamilyInstance;
            if (familyInstance == null) return Result.Failed;
            var familySymbol = familyInstance.Symbol;

            //select edge
            Reference selectedEdge = null;
            try
            {
                selectedEdge = revit.Application.ActiveUIDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Edge);
            }
            catch
            {
                return Result.Failed;
            }
            if (selectedEdge == null) return Result.Succeeded;

            bool SolidInstance =  selectedEdge.ConvertToStableRepresentation(doc).Contains("INSTANCE");
            Element el = null;
            FamilySymbol dummy = null;
            XYZ startPt = XYZ.Zero;
            XYZ endPt = XYZ.Zero;
            if (SolidInstance)
            {
                el  = doc.GetElement(selectedEdge.ElementId);
                dummy  = (el as FamilyInstance).Symbol;
                Transform elTrans = (el as FamilyInstance).GetTransform();

                GeometryObject geoobj = el.GetGeometryObjectFromReference(selectedEdge);
                Edge selEdge = geoobj as Edge;
                startPt = elTrans.OfPoint( selEdge.AsCurve().GetEndPoint(0));
                endPt = elTrans.OfPoint( selEdge.AsCurve().GetEndPoint(1));
            }
    
            using (Transaction t = new Transaction(doc, "create adaptive family"))
            {
                t.Start();
                    FamilyInstance dummyInstance = null;
                    if (SolidInstance)
                    {
                        using (SubTransaction st = new SubTransaction(doc))
                        {
                            st.Start();
                            dummyInstance = doc.Create.NewFamilyInstance(new XYZ(double.MinValue, double.MinValue, 0), dummy, XYZ.BasisX, null, StructuralType.NonStructural);
                            st.Commit();
                        }
                        using (SubTransaction st = new SubTransaction(doc))
                        {
                            st.Start();
                            JoinGeometryUtils.JoinGeometry(doc, dummyInstance, el);
                            st.Commit();
                        }
                        Options options = new Options();
                        options.ComputeReferences = true;
                        options.View = doc.ActiveView;
                        GeometryElement geoElem = el.get_Geometry(options);
                        bool quitloop = false;
                        // find the "InSitu:" Edge
                        foreach (GeometryObject geoobj in geoElem)
                        {
                            Solid solid = geoobj as Solid;
                            if (solid == null) continue;
                            foreach (Edge e in solid.Edges)
                            {
                                XYZ pt1 = e.AsCurve().GetEndPoint(0);
                                XYZ pt2 = e.AsCurve().GetEndPoint(1);
                                if (pt1.IsAlmostEqualTo(startPt) || pt1.IsAlmostEqualTo(endPt))
                                {
                                    if (pt2.IsAlmostEqualTo(startPt) || pt2.IsAlmostEqualTo(endPt))
                                    {
                                        selectedEdge = e.Reference;
                                        quitloop = true;
                                        break;
                                    }
                                }
                            }
                            if (quitloop) break;
                        }
                    }

                    FamilyInstance famInst = null;
                    ReferencePoint firstPoint = null;
                    using (SubTransaction st = new SubTransaction(doc))
                    {
                        try
                        {
                            st.Start();
                            //create a family instance and get it's adaptive points
                            famInst = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(doc, familySymbol);
                            List<ElementId> placementPoints = AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(famInst).ToList();

                            //create reference point
                            PointLocationOnCurve location = new PointLocationOnCurve(PointOnCurveMeasurementType.NormalizedCurveParameter, 0.5, PointOnCurveMeasureFrom.Beginning);
                            PointOnEdge pointOnEdge = doc.Application.Create.NewPointOnEdge(selectedEdge, location);

                            //attach first adaptive point to ref point
                            firstPoint = doc.GetElement(placementPoints.FirstOrDefault()) as ReferencePoint;
                            firstPoint.SetPointElementReference(pointOnEdge);
                            st.Commit();
                        }
                        catch { 
                                                TaskDialog.Show("catch","Failed to place on selectededge  "+selectedEdge.ConvertToStableRepresentation(doc));
                        }
                    }
                    if (SolidInstance)
                    {
                        using (SubTransaction st = new SubTransaction(doc))
                        {
                            st.Start();
                            doc.Delete(dummyInstance.Id);
                            st.Commit();
                        }
                    }
                t.Commit();
            }
            return Result.Succeeded;
        }
    }
Message 7 of 12
Anonymous
in reply to: FAIR59

dusting of this old thread...

 

Seems like an easy enough workaround. Before i go and code it up, after the dummy element is deleted does the hosted element remain? is it still hosted?

 

I tried this approach manually, (see attached) when the dummy element is deleted the hosted element gets deleted also. 

 

 

Message 8 of 12
FAIR59
in reply to: Anonymous

You placed the adaptive family on a edge that was formed by cutting the 2 elements. When you remove the cutting element, you also remove the edge ( the host for the adaptive family)

 

Because I place the dummy element way off, there is no interaction with the selected edge, and the edge doesn't change as the dummy element is

removed.

Message 9 of 12
Anonymous
in reply to: FAIR59

ok i see that now will give it a go.

 

The way Revit works never fails to surprise me.

 

Thank you for you help.

Message 10 of 12
minet.axel
in reply to: Anonymous

Hello everyone,

I come to the news about this problem. I did some tests and I noticed that it only works with family categories that can be joined/cut/copped (example category: Walls, Framing, Generic Models). But I want to perform this operation on the specialized equipment category but it does not work. Would you have a solution for categories that can not be joined/cut/copped?

Message 11 of 12
minet.axel
in reply to: minet.axel

I found!

 

I replaced this part of code with

 using (SubTransaction st = new SubTransaction(doc))
 {
    st.Start();
    JoinGeometryUtils.JoinGeometry(doc, dummyInstance, el);
    st.Commit();
 }

by

 using (SubTransaction st = new SubTransaction(doc))
 {
    st.Start();
    SolidSolidCutUtils.AddCutBetweenSolids(doc, dummyInstance, el);
    st.Commit();
}

Works with "Speciality Equipment"

Message 12 of 12
jeremytammik
in reply to: minet.axel

Congratulations! Could you provide am minimal reproducible case that demonstrates the usage of your solution?

 

https://thebuildingcoder.typepad.com/blog/about-the-author.html#1b

 

This seems to be a pretty tricky issue with some high level research and discussion invested, so it would be really nice to summarise in a conclusive blog post...

  

Thank you! 

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


Autodesk Design & Make Report