Adding connectors to obejct exported from AutoCAD.

Adding connectors to obejct exported from AutoCAD.

mhillis
Advocate Advocate
2,286 Views
13 Replies
Message 1 of 14

Adding connectors to obejct exported from AutoCAD.

mhillis
Advocate
Advocate

Hello everyone,

 

I've got a bit of a issue I'm trying to tackle and I'm looking for the simplest method of doing so.

 

I've got a simple VAV object that has been exported from AutoCAD, imported into a Mechanical Equipment family template, and then exploded.   I have attached the resulting Family to this post as VAVObj.rfa  

 

My issue is that I want to attach a Duct Connector to it, however; I cannot create connectors on the locations I would actually like to. (See VAV.png for reference).  It only allows me to put Duct Connectors in, relatively speaking, places that don't make much functional sense.

 

I imagine there is some rule in place here that prevents me from doing this.  Could anyone with a bit more experience on the matter elaborate what exactly restricts me from putting these duct connectors where I want them?  Also, is there anything that I can do that would allow me to put the duct connectors where I want them?

 

Thanks!

 

 

0 Likes
Accepted solutions (1)
2,287 Views
13 Replies
Replies (13)
Message 2 of 14

Mustafa.Salaheldin
Collaborator
Collaborator

You can add connectors by first clicking on Duct connector

01.PNG

Then select Work Plane

02.PNG

Choose Pick a plane, then select the faces that you want to add the connectors to them.

 

Good luck and if this answer satisfies your needs please mark this reply as an answer.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 3 of 14

mhillis
Advocate
Advocate

@Mustafa.Salaheldin wrote:

You can add connectors by first clicking on Duct connector

01.PNG

Then select Work Plane

02.PNG

Choose Pick a plane, then select the faces that you want to add the connectors to them.

 

Good luck and if this answer satisfies your needs please mark this reply as an answer.


Mustafa,

 

That seems to work using the UI, however; upon doing some research I can't quite find an API means of selecting the face as a work place?  Utilizing the API I did try the example posted by Jeremy here http://thebuildingcoder.typepad.com/blog/2011/02/dimension-walls-by-iterating-faces.html however; when I try using that the face isn't found.  I imagine for the same reason that the "Duct Connector" button doesn't find it.

 

So do you know of anywhere that has an API example of selecting that 'face' as a workplane?

 

Thanks!

0 Likes
Message 4 of 14

mhillis
Advocate
Advocate

A quick update to this.

 

I've figured out a way to get all the faces from these objects and get the face that I want using the Jeremy Tammik example that I posted earlier.  The issue is that these faces all have 'null' for their Reference values.

 

Knowing this, is there a known way to work around this issue with these particular faces to put connectors on them?

 

Thanks!

 

 

0 Likes
Message 5 of 14

Mustafa.Salaheldin
Collaborator
Collaborator

Ok if you want to add conectors to certain faces (even with API) then you will have to pick them by mouse clicking unless you have another idea to determine which faces do you need to assign connectors to them.

In case you want to pick faces by API then this is a piece of cake, if you have a criteria for automatic selection then provide them and I'll help.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 6 of 14

mhillis
Advocate
Advocate
Mustafa,

I won't be able to use mouse picking. This must be 100% automated.

The issue isn't selecting a face. I have the face I want by getting the closes face of an object via it's location. (i.e. the location of my connector). The issue is, with objects exported from AutoCAD, the face that I want to utiliize within the API has a 'null' value for it's "Reference".
0 Likes
Message 7 of 14

Mustafa.Salaheldin
Collaborator
Collaborator

Here you are the complete code:

#region Namespaces

using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;

using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;

//using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;

using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;

#endregion

namespace RevitAddinCS2
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class ExtCmd : IExternalCommand
    {
        #region Cached Variables

        private static ExternalCommandData _cachedCmdData;

        public static UIApplication CachedUiApp
        {
            get
            {
                return _cachedCmdData.Application;
            }
        }

        public static RvtApplication CachedApp
        {
            get
            {
                return CachedUiApp.Application;
            }
        }

        public static RvtDocument CachedDoc
        {
            get
            {
                return CachedUiApp.ActiveUIDocument.Document;
            }
        }

        #endregion

        #region IExternalCommand Members

        public Result Execute(ExternalCommandData cmdData, ref string msg, ElementSet elemSet)
        {
            _cachedCmdData = cmdData;

            try
            {
                //TODO: add your code below.
                FilteredElementCollector importCAD = new FilteredElementCollector(CachedDoc).OfClass(typeof(FreeFormElement));

                Element modelText = importCAD.FirstElement();
                PaintModelTextFaces(modelText);

                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                msg = ex.ToString();
                return Result.Failed;
            }
        }

        public void PaintModelTextFaces(Element element)
        {
            // Before acquiring the geometry, make sure the detail level is set to 'Fine'
            Options geoOptions = CachedDoc.Application.Create.NewGeometryOptions();
            geoOptions.DetailLevel = ViewDetailLevel.Fine;
            geoOptions.ComputeReferences = true;
            // Obtain geometry for the given Wall element
            GeometryElement geoElem = element.get_Geometry(geoOptions);

            using (Transaction trans = new Transaction(CachedDoc))
            {
                IEnumerator<GeometryObject> geoObjectItor = geoElem.GetEnumerator();

                trans.Start("PaintModelTextFaces");

                while (geoObjectItor.MoveNext())
                {
                    // need to find a solid first
                    Solid theSolid = geoObjectItor.Current as Solid;
                    if (null != theSolid)
                    {
                        foreach (Face face in theSolid.Faces)
                        {
                            PlanarFace pf = face as PlanarFace;

                            if (pf != null)
                            {
                                Reference mReference = null;

                                using (SubTransaction st = new SubTransaction(CachedDoc))
                                {
                                    st.Start();

                                    Plane plane = new Plane(pf.FaceNormal, pf.Origin);

                                    List<Face> faces = face.GetRegions().ToList();

                                    SketchPlane sp = SketchPlane.Create(CachedDoc, plane);

                                    CachedDoc.ActiveView.SketchPlane = sp;
                                    mReference = sp.GetPlaneReference();

                                    //CachedDoc.ActiveView.ShowActiveWorkPlane();
                                    st.Commit();
                                }

                                using (SubTransaction st = new SubTransaction(CachedDoc))
                                {
                                    st.Start();
                                    ConnectorElement ce = ConnectorElement.CreateDuctConnector(CachedDoc, DuctSystemType.SupplyAir, ConnectorProfileType.Round, pf.Reference,pf.EdgeLoops.get_Item(0).get_Item(0));
                                    //ConnectorManager cm = new ConnectorManager();
                                    //cm.Connectors.Insert(ce.co);
                                    st.Commit();
                                }
                            }
                        }
                    }
                }
                trans.Commit();
            }
        }
        #endregion
    }
}

It will place a connector on every face so you have to make your modification to select the faces you want.

If this code help please don't forget to mark this reply as an answer.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 8 of 14

mhillis
Advocate
Advocate

Mustafa,

 

Thanks for another vallant attempt!

 

Unfortunately, I've hit a bit of a bump in the road implementing your solution.

 

When it comes to the routine placing connectors on the face that I want. I get a null exception at this line 

 

CachedDoc.ActiveView.SketchPlane = sp;

I can't quite seem to get it to work correctly at all.  In the interest of a more direct example of what I'm wanting this to do.  I've attached a few things.  First, I've attached a code sample of where my code is at on this matter.  If you test this code, BE SURE to use the .rfa file I've attached.  This particular instance corresponds to this particular Mech. Equip item. I've also attached a screenshot showing exactly which face this particular routine will attempt to place a connector onto.

 

Thanks!

0 Likes
Message 9 of 14

Mustafa.Salaheldin
Collaborator
Collaborator

I'm little confused. Please clarify where do you want to run the Addin (in the Family editor or in the Project file). In case you want to insert the family to the project then add the connector there then this will be another story. If you want to add the connectors in Family editor then the provided code is working perfectly. One last thing this code is written for Revit 2016 API so if you are targeting another version this may require some modification to the provided code.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 10 of 14

mhillis
Advocate
Advocate
The imported DWG will be imported and modified into a Family Document.
0 Likes
Message 11 of 14

Mustafa.Salaheldin
Collaborator
Collaborator

Ok so as I told you the code provided work perfectly in Revit 2016

 

To make the same approach from the Project document use the following code:

#region Namespaces

using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;

using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;

//using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;

using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;

#endregion

namespace RevitAddinCS2
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class ExtCmd : IExternalCommand
    {
        #region Cached Variables

        private static ExternalCommandData _cachedCmdData;

        public static UIApplication CachedUiApp
        {
            get
            {
                return _cachedCmdData.Application;
            }
        }

        public static RvtApplication CachedApp
        {
            get
            {
                return CachedUiApp.Application;
            }
        }

        public static RvtDocument CachedDoc
        {
            get
            {
                return CachedUiApp.ActiveUIDocument.Document;
            }
        }

        #endregion

        #region IExternalCommand Members

        public Result Execute(ExternalCommandData cmdData, ref string msg, ElementSet elemSet)
        {
            _cachedCmdData = cmdData;

            try
            {
                FilteredElementCollector fam = new FilteredElementCollector(CachedDoc).OfClass(typeof(Family));
                var fams = from family in fam
                           where (family.Name == "ImportedObj_corrected")
                           select family;

                List<Family> fa= fams.Cast<Family>().ToList();

                foreach (Family f in fa)
                {
                    if (f.IsEditable)
                    {
                        Document familyDoc = CachedDoc.EditFamily(f);
                        //TODO: add your code below.
                        FilteredElementCollector importCAD = new FilteredElementCollector(familyDoc).OfClass(typeof(ImportInstance));
                        ElementId ImportedObj = importCAD.FirstElementId();

                        if(AddConnectorToFamily(familyDoc, ImportedObj))
                        {
                            familyDoc.LoadFamily(CachedDoc,new FamilyOption());
                        }
                    }
                }

                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                msg = ex.ToString();
                return Result.Failed;
            }
        }

        public bool AddConnectorToFamily(Document famDoc, ElementId importedId)
        {
            //Connector Values.
            XYZ connNormal = new XYZ(0, 0, 1);
            XYZ connDirection = new XYZ(1, 0, 0);
            XYZ connOrigin = new XYZ(-24.022405238, -59.669659519, 9.583333333);

            PlanarFace pf = null;

            Options opts = famDoc.Application.Create.NewGeometryOptions();
            opts.DetailLevel = ViewDetailLevel.Fine;
            opts.ComputeReferences = true;

            //This is a little redundant.
            Element e = famDoc.GetElement(importedId);
            ImportInstance impInst = e as ImportInstance;

            //To access the Geometry of the Imported DWG block
            //We need to dig into it a little bit.
            GeometryElement impGeo = impInst.get_Geometry(opts);

            foreach (GeometryObject impGeoObj in impGeo)
            {
                GeometryInstance symInst = impGeoObj as GeometryInstance;
                GeometryElement symEle = symInst.SymbolGeometry;

                using (Transaction t = new Transaction(famDoc, "edit"))
                {
                    t.Start();
                    foreach (GeometryObject symObj in symEle)
                    {
                        Solid solid = symObj as Solid;

                        if (solid != null)
                        {
                            FaceArray fa = solid.Faces;
                            foreach (Face f in fa)
                            {
                                pf = f as PlanarFace;

                                if ((pf != null) && (IsParallel(connDirection, pf.FaceNormal)))
                                {
                                    if (pf.Origin.IsAlmostEqualTo(connOrigin))
                                    {
                                        using (SubTransaction refTrans = new SubTransaction(famDoc))
                                        {
                                            refTrans.Start();

                                            ConnectorElement.CreateDuctConnector(famDoc, DuctSystemType.Global, ConnectorProfileType.Round, pf.Reference, pf.EdgeLoops.get_Item(0).get_Item(0));

                                            refTrans.Commit();

                                        }
                                    }
                                }
                            }
                        }
                    }
                    t.Commit();
                }
            }

            famDoc.Save();
            return true;
        }

        const double _eps = 1.0e-9;

        /// <summary>
        /// Check whether two real numbers are equal
        /// </summary>
        static public bool IsEqual(double a, double b)
        {
            return Math.Abs(a - b) < _eps;
        }

        /// <summary>
        /// Check whether two vectors are parallel
        /// </summary>
        static public bool IsParallel(XYZ a, XYZ b)
        {
            double angle = a.AngleTo(b);
            return _eps > angle || IsEqual(angle, Math.PI);
        }
        #endregion
        class FamilyOption : IFamilyLoadOptions
        {


            bool IFamilyLoadOptions.OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
            {
                overwriteParameterValues = true;
                return true;
            }

            bool IFamilyLoadOptions.OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
            {
                source = FamilySource.Family;
                overwriteParameterValues = true;
                return true;
            }
        }
    }
}

You can follow the modifications I made to adjust your code.

I also make some correction to the Family insertion point.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 12 of 14

Mustafa.Salaheldin
Collaborator
Collaborator
Accepted solution

For the Sketchplane code it won't be used in your case so you can ignore it.

 

For adding connector in the Family Editor I modified the code to match the updates you sent:

 

#region Namespaces

using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;

using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;

//using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;

using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;

#endregion

namespace RevitAddinCS2
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class ExtCmd : IExternalCommand
    {
        #region Cached Variables

        private static ExternalCommandData _cachedCmdData;

        public static UIApplication CachedUiApp
        {
            get
            {
                return _cachedCmdData.Application;
            }
        }

        public static RvtApplication CachedApp
        {
            get
            {
                return CachedUiApp.Application;
            }
        }

        public static RvtDocument CachedDoc
        {
            get
            {
                return CachedUiApp.ActiveUIDocument.Document;
            }
        }

        #endregion

        #region IExternalCommand Members

        public Result Execute(ExternalCommandData cmdData, ref string msg, ElementSet elemSet)
        {
            _cachedCmdData = cmdData;

            try
            {
                //TODO: add your code below.
                FilteredElementCollector importedElements = new FilteredElementCollector(CachedDoc).OfClass(typeof(ImportInstance));

                Element importedElement = importedElements.FirstElement();
                AddConnectorToFamily(importedElement);

                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                msg = ex.ToString();
                return Result.Failed;
            }
        }

        public void AddConnectorToFamily(Element element)
        {
            // Before acquiring the geometry, make sure the detail level is set to 'Fine'
            Options geoOptions = CachedDoc.Application.Create.NewGeometryOptions();
            geoOptions.DetailLevel = ViewDetailLevel.Fine;
            geoOptions.ComputeReferences = true;
            // Obtain geometry for the given Wall element
            GeometryElement geoElem = element.get_Geometry(geoOptions);

            using (Transaction trans = new Transaction(CachedDoc))
            {
                IEnumerator<GeometryObject> geoObjectItor = geoElem.GetEnumerator();

                trans.Start("InsertConnector");

                while (geoObjectItor.MoveNext())
                {
                    GeometryInstance symInst = geoObjectItor.Current as GeometryInstance;
                    GeometryElement symEle = symInst.SymbolGeometry;

                    foreach (GeometryObject symObj in symEle)
                    {
                        Solid theSolid = symObj as Solid;

                        if (null != theSolid)
                        {
                            foreach (Face face in theSolid.Faces)
                            {
                                PlanarFace pf = face as PlanarFace;

                                if (pf != null)
                                {
                                    XYZ connOrigin = new XYZ(-24.022405238, -59.669659519, 9.583333333);

                                    if (pf.Origin.IsAlmostEqualTo(connOrigin))
                                    {
                                        using (SubTransaction st = new SubTransaction(CachedDoc))
                                        {
                                            st.Start();
                                            ConnectorElement ce = ConnectorElement.CreateDuctConnector(CachedDoc, DuctSystemType.SupplyAir, ConnectorProfileType.Round, pf.Reference, pf.EdgeLoops.get_Item(0).get_Item(0));
                                            st.Commit();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                trans.Commit();
            }
        }
        #endregion
    }
}

You may need to replace the FaceNormal property with Normal property to match the Revit 2014 API.

Please notify me if everything is fine.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 13 of 14

mhillis
Advocate
Advocate
Mustafa,

That was it! Thank you very kindly for your assistance and guidance!!!
0 Likes
Message 14 of 14

JimJia
Alumni
Alumni

Dear Michael Hillis

 

Please find the attached .rfa which has macro inside, I generated and refined the macro based on code above; one connector can be created successfully after running the macro, please have a try.

 

some comments on your question and code:

. We can compute planar face normal via Face.ComputeNormal(new UV())

. The sketchplane works well without any problem in attached macro.

. You didn't commit sub transaction in earlier code, so the connector is not visible after code execute.


Jim Jia
Autodesk Forge Evangelist
https://forge.autodesk.com
Developer Technical Services
Autodesk Developer Network
Email: Jim.Jia@autodesk.com
0 Likes