New Loft from elements

New Loft from elements

Anonymous
Not applicable
5,052 Views
19 Replies
Message 1 of 20

New Loft from elements

Anonymous
Not applicable

Hi guys, I don’t know where to ask for help but I really need your help.
I’m trying to create a new loft using some cross sections.
I can place the cross section (basically a generic 2D object) exactly on reference points and orient this in the right direction.
My problem now is to extract the geometries to be able to create the loft. The NewLoftForm expect as input a ReferenceArrayArray but I’m really struggling to get this result.
Here the code for my command

 

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
#endregion

namespace TestCurves
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Application app = uiapp.Application;
            Document doc = uidoc.Document;

            // Access current selection

            Selection sel = uidoc.Selection;
            Reference reference = sel.PickObject(ObjectType.Element);
            FamilyInstance fi = doc.GetElement(reference) as FamilyInstance;
            FamilySymbol fs = fi.Symbol;

            FilteredElementCollector refPoints = new FilteredElementCollector(doc).OfClass(typeof(ReferencePoint));

            // Retrieve elements from database

            using (Transaction tx = new Transaction(doc))
            {
                ReferenceArrayArray refArAr = new ReferenceArrayArray();
                tx.Start("Transaction Name");
                foreach (ReferencePoint rp in refPoints)
                {
                    if (rp.get_Parameter(BuiltInParameter.ELEM_CATEGORY_PARAM).AsValueString() == "Reference Points")
                    {
                        try
                        {
                            XYZ origin = new XYZ(rp.Position.X, rp.Position.Y, rp.Position.Z);
                            XYZ normal = rp.GetCoordinateSystem().BasisY;
                            SketchPlane sp = SketchPlane.Create(doc, Plane.CreateByNormalAndOrigin(normal, origin));
                            FamilyInstance fInstance = doc.FamilyCreate.NewFamilyInstance(rp.GetCoordinatePlaneReferenceXZ(), origin, new XYZ(-1, 0, 0), fs);
                            refArAr.Append(GetObjects(doc, fi, sp));
                            //doc.FamilyCreate.NewLoftForm(true, refArAr);
                        }
                        catch { }
                    }
                }
                
                tx.Commit();
            }

            return Result.Succeeded;
        }

        public static ReferenceArray GetObjects(Document doc, FamilyInstance fi, SketchPlane sp)
        {
            ReferenceArray refAr = new ReferenceArray();

            GeometryElement ge = fi.get_Geometry(new Options { DetailLevel = ViewDetailLevel.Fine, ComputeReferences = true });
            foreach (GeometryObject go in ge)
            {
                GeometryInstance gi = go as GeometryInstance;
                GeometryElement gEl = gi.GetInstanceGeometry();
                foreach (GeometryObject gObj in gEl)
                {
                    if (gObj.GetType() == typeof(Arc))
                    {
                        try
                        {
                            Arc crv = gObj as Arc;
                            ModelCurve mc = doc.FamilyCreate.NewModelCurve(crv, sp);
                            TaskDialog.Show("aaa", crv.ToString());
                        }
                        catch (Exception ex)
                        { TaskDialog.Show("Error", ex.Message); }
                    }
                }
            }

            return refAr;
         }


    }
}

this is the actual result in Revit

2018-03-13 22_14_29-TestCurves - Microsoft Visual Studio.png

5,053 Views
19 Replies
Replies (19)
Message 2 of 20

jeremytammik
Autodesk
Autodesk

Dear Cesare,

 

The Revit SDK includes some samples demonstrating the use of NewLoftForm, e.g., ManipulateForm, NewForm and PointCurveCreation.

 

You can also check out The Building Coder article on the Revit Form Creation API:

 

http://thebuildingcoder.typepad.com/blog/2009/07/revit-form-creation-api.html

Maybe that will help?

 

Cheers,

 

Jeremy



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

0 Likes
Message 3 of 20

Anonymous
Not applicable

Well, thank you...

I 've something now but with two issues:

 - First the solid is not exactly aligned to the curves

- Second, the solid is made from model curves and, if I change the section (using type properties), the solid won't follow. 

 

I know that if I can make something from the interface, I can do it with API so. How I can directly select the profile and create a solid from the selected profile? Probably is something really stupid but I'm missing a detail.

 

 ReferenceArrayArray refArAr = new ReferenceArrayArray();
                        foreach (ReferencePoint refPoint in refPoints)
                        {
                            if (refPoint.get_Parameter(BuiltInParameter.ELEM_CATEGORY_PARAM).AsValueString() != "Adaptive Points")
                            {
                                FamilyInstance fiInternal = doc.FamilyCreate.NewFamilyInstance(refPoint.GetCoordinatePlaneReferenceYZ(), new XYZ(refPoint.Position.X, refPoint.Position.Y, refPoint.Position.Z), new XYZ(0, 1, 0), fsInternal);
                                //FamilyInstance fiExternal = doc.FamilyCreate.NewFamilyInstance(refPoint.GetCoordinatePlaneReferenceYZ(), new XYZ(refPoint.Position.X, refPoint.Position.Y, refPoint.Position.Z), new XYZ(0, 1, 0), fsExternal);
                                //externalSections.Add(fiExternal);
                                internalSections.Add(fiInternal);
                            }       
                        }
                        tx.Commit();

                        Transaction tx2 = new Transaction(doc);
                        tx2.Start("Create model curves");

                        foreach (FamilyInstance inSec in internalSections)
                        {
                            FamilySymbol fs = inSec.Symbol;
                            ReferenceArray refArInt = new ReferenceArray();

                            GeometryElement ge = inSec.get_Geometry(new Options { DetailLevel = ViewDetailLevel.Medium, ComputeReferences = true });
                            foreach (GeometryObject go in ge)
                            {
                                GeometryInstance gi = go as GeometryInstance;
                                GeometryElement gEl = gi.GetInstanceGeometry();
                                foreach (GeometryObject gObj in gEl)
                                {
                                    Type objType = gObj.GetType();
                                    if (objType == typeof(Line))
                                    {
                                        try
                                        {
                                            Line curve = gObj as Line;
                                            XYZ endPoint = curve.GetEndPoint(1);
                                            XYZ startPoint = curve.GetEndPoint(0);
                                            //XYZ thirdPoint = curve.Tessellate()[curve.Tessellate().Count / 2];
                                            Line line = Line.CreateBound(startPoint, endPoint);
                                            XYZ normal = startPoint.CrossProduct(endPoint);

                                            Plane p = Plane.CreateByNormalAndOrigin(normal, endPoint);
                                            SketchPlane sp = SketchPlane.Create(doc, p);
                                            ModelCurve mc = doc.FamilyCreate.NewModelCurve(line, sp);
                                            //TaskDialog.Show("Test", mc.Name);
                                            refArInt.Append(mc.GeometryCurve.Reference);
                                        }
                                        catch (Exception ex)
                                        { TaskDialog.Show("Error", ex.Message); }
                                    }
                                }
                            }
                            refArAr.Append(refArInt);

                        }
                        doc.FamilyCreate.NewLoftForm(true, refArAr);
                        tx2.Commit();

2018-03-14 10_52_52-Autodesk Revit 2018.2 - [3D View_ {3D} - Family1].jpg

0 Likes
Message 4 of 20

Anonymous
Not applicable

This is what I've achieved so far

https://knowledge.autodesk.com/community/screencast/5680349a-e880-44c4-bddf-e66b384554d9

 

And this is what I would like to achieve

https://knowledge.autodesk.com/community/screencast/c4c3c1ae-52ff-40a4-a9e5-0fa77c12e7a2

 

This is my code now

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Autodesk.AECC.Interop.Land;
using Autodesk.AECC.Interop.UiLand;
using Autodesk.AutoCAD.Interop;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Windows.Forms;
using CivilRevitConnection.Forms;
using CivilRevitConnection.DataTypes;
#endregion

namespace CivilRevitConnection
{
    [Transaction(TransactionMode.Manual)]
    public class CreateSolid : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            //Application app = uiapp.Application;
            Document doc = uidoc.Document;

            FilteredElementCollector refPoints = new FilteredElementCollector(doc).OfClass(typeof(ReferencePoint)).WhereElementIsNotElementType();
            //refPoints.ToElementIds();
            Transaction tx = new Transaction(doc, "Create Solid");
            tx.Start();
            if (refPoints.GetElementCount() > 0)
            {
                List<FamilyInstance> externalSections = new List<FamilyInstance>();
                List<FamilyInstance> internalSections = new List<FamilyInstance>();
                try
                {
                    
                    CreateSolidForm solidForm = new CreateSolidForm(doc, tx);
                    if (solidForm.ShowDialog() == DialogResult.OK)
                    {
                        FamilySymbol fsInternal = solidForm._InternalSection;
                        FamilySymbol fsExternal = solidForm._ExternalSection;
                        fsExternal.Activate();
                        fsInternal.Activate();
                        int counter = 0;
                        foreach (ReferencePoint refPoint in refPoints)
                        {
                            if (refPoint.get_Parameter(BuiltInParameter.ELEM_CATEGORY_PARAM).AsValueString() != "Adaptive Points")
                            {
                                counter++;
                                FamilyInstance fiInternal = doc.FamilyCreate.NewFamilyInstance(refPoint.GetCoordinatePlaneReferenceYZ(), new XYZ(refPoint.Position.X, refPoint.Position.Y, refPoint.Position.Z), new XYZ(0, 1, 0), fsInternal);
                                FamilyInstance fiExternal = doc.FamilyCreate.NewFamilyInstance(refPoint.GetCoordinatePlaneReferenceYZ(), new XYZ(refPoint.Position.X, refPoint.Position.Y, refPoint.Position.Z), new XYZ(0, 1, 0), fsExternal);
                                externalSections.Add(fiExternal);
                                internalSections.Add(fiInternal);
                            }       
                        }
                        tx.Commit();

                        Transaction tx2 = new Transaction(doc);
                        tx2.Start("Create model curves");

                        ReferenceArrayArray internalProfiles = new ReferenceArrayArray();
                        ReferenceArrayArray externalProfiles = new ReferenceArrayArray();

                        for (int i = 0; i < counter; i++)
                        {
                            ReferenceArray intSec = GetObjectElements(doc, internalSections[i]);
                            internalProfiles.Append(intSec);
                            ReferenceArray extSec = GetObjectElements(doc, externalSections[i]);
                            externalProfiles.Append(extSec);
                        }

                        SubTransaction createSolid = new SubTransaction(doc);
                        createSolid.Start();
                        Autodesk.Revit.DB.Form solidEl = doc.FamilyCreate.NewLoftForm(true, externalProfiles);
                        createSolid.Commit();

                        SubTransaction createVoid = new SubTransaction(doc);
                        createVoid.Start();
                        Autodesk.Revit.DB.Form voidEl = doc.FamilyCreate.NewLoftForm(false, internalProfiles);
                        createVoid.Commit();

                        try
                        {
                            //SolidSolidCutUtils.AddCutBetweenSolids(doc, solidEl, voidEl);
                            //InstanceVoidCutUtils.CanBeCutWithVoid(solidEl);
                            if (InstanceVoidCutUtils.IsVoidInstanceCuttingElement(voidEl))
                            {
                                InstanceVoidCutUtils.AddInstanceVoidCut(doc, solidEl, voidEl);
                            }
                            
                        }
                        catch (Exception ex)
                        {
                            TaskDialog.Show("Error", ex.Message);
                        }
                        
                        tx2.Commit();
                    }

                }
                catch (Exception ex)
                {
                    TaskDialog.Show("Error", ex.Message);
                    tx.RollBack();
                }
            }
            else
            {
                TaskDialog.Show("Error", "Create the 3D alignment first!");
            }
            return Result.Succeeded;
        }

        public ReferenceArray GetObjectElements(Document doc, FamilyInstance sec)
        {
            ReferenceArray refAr = new ReferenceArray();
            List<XYZ> planePoints = new List<XYZ>();
            List<Curve> curves = new List<Curve>();
            GeometryElement ge = sec.get_Geometry(new Options { DetailLevel = ViewDetailLevel.Medium, ComputeReferences = true });
            foreach (GeometryObject go in ge)
            {
                GeometryInstance gi = go as GeometryInstance;
                GeometryElement gEl = gi.GetInstanceGeometry();
                foreach (GeometryObject gObj in gEl)
                {
                    Type objType = gObj.GetType();
                    if (objType == typeof(Line))
                    {
                        Line curve = gObj as Line;
                        XYZ endPoint = curve.GetEndPoint(1);
                        XYZ startPoint = curve.GetEndPoint(0);
                        planePoints.Add(startPoint);
                        Line line = Line.CreateBound(startPoint, endPoint);
                        curves.Add(line);
                    }
                }
            }

            SketchPlane sp = SketchPlane.Create(doc, Plane.CreateByThreePoints(planePoints[0], planePoints[1], planePoints[2]));
            foreach (Curve c in curves)
            {
                ModelCurve mc = doc.FamilyCreate.NewModelCurve(c, sp);
                refAr.Append(mc.GeometryCurve.Reference);
            }
            //doc.Delete(sec.Id);
            return refAr;
        }
    }
}
0 Likes
Message 5 of 20

Anonymous
Not applicable

No guess?

I couldn't find a better way to do that...is that probably not available in the API?

0 Likes
Message 6 of 20

jeremytammik
Autodesk
Autodesk

Dear Cesare,

 

Thank you for your extensive code samples and recordings.

 

Congratulations on making progress.

 

I am somewhat overwhelmed by both.

 

In the case of the recordings, the high screen resolution makes it hard for me to understand anything.

 

I don't really see much difference between the two.

 

I don't quite understand your problem or needs.

 

Are you sure you need a loft form?

 

Would a sweep be of any use to you?

 

Would you like to describe your problem in simple words from scratch, for amateurs?

 

Thank you.

 

Cheers,

 

Jeremy



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

0 Likes
Message 7 of 20

Anonymous
Not applicable
Hi Jeremy, I’ll try to do my best to explain.
First of all I’ve a list of reference point that represent the location where I need to place a family (the family instance is a generic model of a closed loop of lines drew in the reference plane).
The step of placing the family instances perpendicular to the reference plane is pretty simple and I’ve already solved this.
The second step is to create a loft (or a sweep) using these sections. My problem now is that I need to convert these family instances into model curves to be able to create the loft. The final result is correct but I lose the “connection” with the shape of the family instance so, if I change the dimensions (type parameters) of the section, the tunnel doesn’t change (because is related to the model curves).

On the other side, if I select from the view all the section manually and then I click the button to create a form, the shape is parametric and changes together if the shape changes.

My question is: is there a way to avoid the use of model curves and use directly the family instance to create the form? I can’t find a solution because the newform method is asking for a curve reference that I can’t extract from the family instance without converting that into a model curve.

I hope it’s clear now 🙂
0 Likes
Message 8 of 20

jeremytammik
Autodesk
Autodesk

Dear Cesare,

 

Thank you for the explanation. I see your point.

 

You ask:

 

My question is: is there a way to avoid the use of model curves and use directly the family instance to create the form?

 

Maybe there are several different ways to go.

 

I do not quite understand why you use family instances to represent the cross section, and why they contain a generic model.

 

Could you not use some other kind of cross section parametrisation that generates the required curve loop right out of the box?

 

You own parametrisation?

 

A profile definition, c.f.:

 

https://forums.autodesk.com/t5/revit-api-forum/load-profile-for-newsweep-command/td-p/5117864

 

Alternatively:

 

I am not a modelling expert, but I would guess that it is possible to add constraints to the model lines to force them to follow the family instance edges or faces, if that helps.

 

Constraints can be added programmatically as well.

 

Can you try to find a manual user interface method to achieve that?

 

If you can, then it should be achievable programmatically as well, I think.

 

I hope this helps.

 

Cheers,

 

Jeremy



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

0 Likes
Message 9 of 20

Anonymous
Not applicable

Hi Jeremy

- Hi need to use family instances because I'm using an adaptive component generic model and I can't use a profile family.

- The answer if I can do it manually from the interface is yes (that's what I showed in one of the video)

 

P.S.: I'm still Cesare with the company account 🙂

 

0 Likes
Message 10 of 20

jeremytammik
Autodesk
Autodesk

Dear Cesare,

 

Thank you for your clarification.

 

Well, the API should be able to support what you can do in the UI, so I would like to pass on to the development team for further analysis.

 

Can you put together a complete minimal reproducible case for them, please?

 

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

 

They will need to be able to execute whatever code you share in order to analyse its behaviour, so presumably the cross section family definition will also be needed.

 

Thank you!

 

Best regards,

 

Jeremy



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

0 Likes
Message 11 of 20

HWehnerSSFIng
Explorer
Explorer

Hey Cesare,

 

I had the same problem in bridge construction. I couldn't find a way to get the References of the ModelLines in the "Profile Family" (doesn't matter if it is an adaptive component family or not) to work for the NewLoftForm Method. So I changed all my ModelLines in the Profile Family (which is just a Generic Model in my Case) into ReferenceLines and then I did this:

 

FamilyInstance famInstance = doc.FamilyCreate.NewFamilyInstance(refPlane, placement, direction, familySymbol);
IList<Reference> refList = famInstance.GetReferences(FamilyInstanceReferenceType.WeakReference);
                ReferenceArray profileArr = new ReferenceArray();
                foreach (Reference refListElem in refList)
                {
                    
                    if (refListElem.ElementReferenceType == ElementReferenceType.REFERENCE_TYPE_LINEAR)
                    {
                        profileArr.Append(refListElem);
                    }
                }

Then you can change the LoftForm in the Family by changing the Pramatervalues of the ProfileFamily. It only works if you doesn't have any other ReferenceLines in the Profile Family of course.

 

Hope this helps.

0 Likes
Message 12 of 20

AndrewButenko
Advocate
Advocate

Hi Cesare!

 

Did you do it?

 

 

 

0 Likes
Message 13 of 20

adam.hunt6BDMZ
Explorer
Explorer

Hi @jeremytammik, @Anonymous,

 

I know this post was from some time ago, but I am trying to achieve exactly what was in this thread. Did you pass this on to the development team and get a response?

 

Thanks,

 

Adam

Message 14 of 20

jeremy_tammik
Alumni
Alumni

Maybe this other recent thread demonstrating the use of the CreateLoftGeometry method will help you solve your task?

    

https://forums.autodesk.com/t5/revit-api-forum/creating-a-mass-between-two-toposurfaces/m-p/11363154

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 15 of 20

adam.hunt6BDMZ
Explorer
Explorer

Hi Jeremy, 

Thanks for that. I have no issues generating the the form using createloftgeometry and directshape. The issue lies with me wanting to be able to alter the parameters of the faces which then dynamically adjusts the form. Any ideas? As this is a relatively simple process within the application, I figured it should be able to be done through the API. It can also be achieved through dynamo quite easily.

 

Thanks, Adam

0 Likes
Message 16 of 20

jeremy_tammik
Alumni
Alumni

So you are working with a profile form? Can you be more specific describing your context, problem and requirement? A minimal reproducible case might help, and a picture or two.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 17 of 20

adam.hunt6BDMZ
Explorer
Explorer

So I created a family which is the profile that I want the loft to pass through. I have created this as both a generic mass and generic model adaptive and both seem to work fine.

In the code I do the following:

  1. Insert the family multiple times (see image 1)
  2. I then collect the points from the lines that make up the family and create a curve loop that represents the geometry of each familyinstance profile
  3. then use the list of curve loops in createloftgeometry
  4. finally I use the geometry in direct shape (see image 2)

So it works fine up until this point. Now if I modify the parameters of any of the profiles the form to does not adapt to the change. If run my code to generate the profiles, then select the profiles manually and use 'create form' I can then modify the parameters of the profiles which changes the form (see image 3)

0 Likes
Message 18 of 20

HWehnerSSFIng
Explorer
Explorer

I think the problem you have is the same I had a time ago.
The thing is, that your curve loops are not referenced to the model lines of your profile, because you create the curves with the endpoints, as I understand.
When you use the function in Revit directly the program accesses the References of each Model Line in the Nested Profile Family. You can achieve this via API also. What you need is to create your curve loops only from the reference of the Model Lines of your Nested Profile Family.

Message 19 of 20

adam.hunt6BDMZ
Explorer
Explorer

@HWehnerSSFIng@jeremy_tammik. Ok so this makes sense. I have spent some time researching how to do this and didn't find a workable option. Are you able to point me in the right direction?

Message 20 of 20

enramiz
Contributor
Contributor

Hi @Anonymous 

I solve this problem by using Dynamo and "BirMohareb_2023" pakege