Creating dimensions.

Creating dimensions.

will_smith3691215
Participant Participant
2,444 Views
3 Replies
Message 1 of 4

Creating dimensions.

will_smith3691215
Participant
Participant

Hi forum,

My task is to make the dimensions of the objects that I create with my plugin. I ran into a problem that I can't add references to a variable of the Reference Array type. I have been trying to solve this problem for 2 weeks now, but so far without results, there are many examples of adding dimensions on the Internet, but it seems to me that my case does not come across anywhere.
I'm new to revit api programming, so I'm asking you to help me with my problem. getting the dimensions starts with line 169

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

namespace DeVision.LegendWindows
{
    [Transaction(TransactionMode.Manual)]
    internal class LegendWindowsCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Document doc = uidoc.Document;

            using (Transaction trans = new Transaction(doc, "Create Legend Copy and Insert Windows"))
            {
                trans.Start();

                // legend
                View originalLegend = new FilteredElementCollector(doc)
                    .OfClass(typeof(View))
                    .Cast<View>()
                    .FirstOrDefault(view => view.ViewType == ViewType.Legend);

                if (originalLegend == null)
                {
                    message = "Failed to find legend.";
                    return Result.Failed;
                }

                // duplicate legend
                View duplicatedLegend = doc.GetElement(originalLegend.Duplicate(ViewDuplicateOption.WithDetailing)) as View;
                
                // unique name for legend, so multiple copies can be created
                string legendNameBase = "Window Legend";
                int counter = 2;
                string newLegendName = legendNameBase;

                while (new FilteredElementCollector(doc)
                   .OfClass(typeof(View))
                   .Cast<View>()
                   .Any(view => view.Name == newLegendName))
                {
                    newLegendName = $"{legendNameBase} {counter++}";
                }

                duplicatedLegend.Name = newLegendName;
                //duplicatedLegend.Name = "Window Legend";
                TaskDialog.Show("New Legend Created", $"Legend with name \"{duplicatedLegend.Name}\" and ID {duplicatedLegend.Id} was created.");

                if (duplicatedLegend == null)
                {
                    message = "Failed to create legend copy.";
                    return Result.Failed;
                }
                trans.Commit();

                trans.Start();
                // delete elements from new legend
                var elementsInLegend = new FilteredElementCollector(doc, duplicatedLegend.Id)
                    .WhereElementIsNotElementType()
                    .ToElementIds();

                foreach (ElementId elementId in elementsInLegend)
                {
                    Element element = doc.GetElement(elementId);

                    // delete elements that are not null, have a non-empty category, and whose category does not match the legend view (to avoid deleting the legend itself)
                    if (element != null && element.Category != null && element.Category.Id.IntegerValue != (int)BuiltInCategory.OST_Views) 
                    {
                        doc.Delete(elementId);
                    }
                }

                // all windows in the project
                FilteredElementCollector windowsCollector = new FilteredElementCollector(doc)
                    .OfCategory(BuiltInCategory.OST_Windows)
                    .WhereElementIsNotElementType();

                List<ElementId> windowIds = windowsCollector
                    .Select(window => window.Id)
                    .ToList();

                if (windowIds.Count == 0)
                {
                    message = "No windows found in the project.";
                    return Result.Failed;
                }

                // component from another legend
                Element legendComponent = new FilteredElementCollector(doc)
                    .OfCategory(BuiltInCategory.OST_LegendComponents)
                    .WhereElementIsNotElementType()
                    .FirstOrDefault();
                // id of legendComponent
                ElementId legendComponentId = legendComponent.Id;
                // id of the legend on which legendComponent is located
                ElementId ownerlegendComponent = legendComponent.OwnerViewId;
                // name of the legend on which legendComponent is located
                Element nameLegend = doc.GetElement(ownerlegendComponent);
                //
                View nameLegendView = doc.GetElement(nameLegend.Id) as View;

                // created a list and added one element (legendComponent) to it
                ICollection<ElementId> oneElem = new List<ElementId>();
                oneElem.Add(legendComponent.Id);


                if (legendComponent == null)
                {
                    message = "Failed to find legend component.";
                    return Result.Failed;
                }

                // for offset
                double offsetX = 7;
                double currentX = 0;

                foreach (ElementId windowId in windowIds)
                {
                    // get FamilySymbol of windows and their id (for changing object type)
                    FamilySymbol windowSymbol = (doc.GetElement(windowId) as FamilyInstance).Symbol;
                    ElementId windowsSymbolId = windowSymbol.Id;

                    Transform translation = Transform.CreateTranslation(new XYZ(currentX, 0, 0));
                    // copy element from the oneElem list
                    ICollection<ElementId> copiedElementIds = ElementTransformUtils.CopyElements(nameLegendView, oneElem, duplicatedLegend, translation, new CopyPasteOptions());
                    
                    foreach (ElementId copiedElementId in copiedElementIds)
                    {
                        Element copiedElement = doc.GetElement(copiedElementId);

                        //// text label
                        // first available text type
                        TextNoteType textNoteType = new FilteredElementCollector(doc)
                            .OfClass(typeof(TextNoteType))
                            .Cast<TextNoteType>()
                            .FirstOrDefault();
                        // horizontally centered
                        TextNoteOptions textNoteOptions = new TextNoteOptions(textNoteType.Id)
                        {
                            HorizontalAlignment = HorizontalTextAlignment.Center
                        };

                        FamilyInstance originalWindow = doc.GetElement(windowId) as FamilyInstance;
                        if (originalWindow != null)
                        {
                            // get the "ADSK_Mark" parameter from the original window
                            Parameter markParam = originalWindow.LookupParameter("ADSK_Марка");
                            string windowMark = markParam.AsString();

                            // create text
                            XYZ pointText = new XYZ((copiedElement.get_BoundingBox(duplicatedLegend).Min.X + copiedElement.get_BoundingBox(duplicatedLegend).Max.X) / 2,
                                                    copiedElement.get_BoundingBox(duplicatedLegend).Min.Y + 6.5,
                                                    0);
                            TextNote textNote = TextNote.Create(doc, duplicatedLegend.Id, pointText, windowMark, textNoteOptions);
                        }
                        // change object type
                        var parametertype = copiedElement.get_Parameter(BuiltInParameter.LEGEND_COMPONENT);
                        parametertype.Set(windowsSymbolId);
                        // change object facade
                        var parameterView = copiedElement.get_Parameter(BuiltInParameter.LEGEND_COMPONENT_VIEW);
                        parameterView.Set(-7);

                        //// dimensions

                        BoundingBoxXYZ boundingBox = copiedElement.get_BoundingBox(duplicatedLegend);
                        if (boundingBox == null) continue;
                        XYZ minPoint = new XYZ(boundingBox.Min.X, boundingBox.Min.Y, boundingBox.Min.Z);
                        XYZ maxPoint = new XYZ(boundingBox.Max.X, boundingBox.Min.Y, boundingBox.Min.Z);
                        Line line = Line.CreateBound(minPoint, maxPoint);

                        ReferenceArray array = new ReferenceArray();
                        Options options = new Options
                        {
                            View = duplicatedLegend,
                            ComputeReferences = true
                        };

                        GeometryElement geom = copiedElement.get_Geometry(options);
                        foreach (GeometryObject go in geom)
                        {
                            if (go is Solid solid)
                            {
                                // work directly with Solid
                                ProcessSolid(solid);
                            }
                            else if (go is GeometryInstance instance)
                            {
                                GeometryElement instanceGeometry = instance.GetInstanceGeometry();
                                foreach (GeometryObject nestedGo in instanceGeometry)
                                {
                                    if (nestedGo is Solid nestedSolid)
                                    {
                                        // work with Solid inside GeometryInstance
                                        ProcessSolid(nestedSolid);
                                    }
                                }
                            }
                        }

                        void ProcessSolid(Solid solid)
                        {
                            foreach (Face face in solid.Faces)
                            {
                                foreach (EdgeArray edgeArray in face.EdgeLoops)
                                {
                                    foreach (Edge edge in edgeArray)
                                    {
                                        if (edge.Reference != null &&
                                            edge.Reference.ElementReferenceType == ElementReferenceType.REFERENCE_TYPE_LINEAR)
                                        {
                                            array.Append(edge.Reference);
                                        }
                                    }
                                }
                            }
                        }
                        Dimension dimension = doc.Create.NewDimension(duplicatedLegend, line, array);
                    }
                    currentX += offsetX;
                    doc.Regenerate();
                }
                trans.Commit();
            }
            return Result.Succeeded;
        }
    }
}



0 Likes
2,445 Views
3 Replies
Replies (3)
Message 2 of 4

jeremy_tammik
Alumni
Alumni

Welcome to Revit API programming! Sorry, but your code is very long and does many different things. I find it hard to understand the entire process. I would suggest splitting it up into separate parts to make it more manageable and easier to understand. That will probably also help narrow down the problem a bit. Especially for a newbie to Revit API programming I would suggest taking things step by step, and making small steps to begin with. Good luck and have fun!

  

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

TripleM-Dev.net
Advisor
Advisor

Hi @will_smith3691215 ,

 

My advise, split the code and first start with trying to dimension a existing legend component.

 

Also first dimension a legend component in the UI and then work you're way back by inspecting the created dimension references with RevitLookup.

 

Legend components aren't normal 3D geometry and may not be possible to dimension them at all, if you can't dimension it manually, it's also not possible in the API.

 

I've seen addins creating a window overview with legendcomponents, they added detail lines on the extends of the window to dimension it. (they could only dim the outer width and height)

 

- Michel

 

Ps. And keep the code split, each action could be a seperate function. Like duplicating a legend (could already do wrong if empty I think), one for creating the Legend Components etc etc.

Message 4 of 4

will_smith3691215
Participant
Participant

Hi M, thank you for the answer.
unfortunately, splitting the code did not help solve the problem (although you did not say that it would solve the problem), it became much more convenient to read the code.
the answer to your question is whether dimensions are created in the user interface - yes, they are created, so it can be done using the API.
I didn't mention this earlier, but the variable "array" (line 177) I have eventually returns "null", which leads to an error in the "NewDimension" method. now I'm looking for a way to fix this bug.

0 Likes