Circular Reference error while setting WallType

Circular Reference error while setting WallType

harilalmn
Advocate Advocate
512 Views
4 Replies
Message 1 of 5

Circular Reference error while setting WallType

harilalmn
Advocate
Advocate

Hi All,

I was working with Stacked Walls. It was a simple attempt to change the WallType of all wall instances to the default StackedWall. However this throws an error saying,


"There is a circular chain of references among the highlighted elements."

harilalmn_0-1686379692911.png

 

Here is my code (I am doing this in Macro environment)

 

public void Test()
{
    UIDocument uidoc = ActiveUIDocument;
    Document doc = uidoc.Document;
    WallType wt = null;
    ICollection<Element> wallTypesInThisDoc = new FilteredElementCollector(doc)
        .OfClass(typeof(WallType))
        .Where(
            w => w.get_Parameter(BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM).AsValueString().Equals("Stacked Wall")
        )
        .ToList();

    if (wallTypesInThisDoc.Count > 0)
    {
        wt = wallTypesInThisDoc.First() as WallType;
    }
    else
    {
        return;
    }

    List<Wall> allWalls = new FilteredElementCollector(doc).OfClass(typeof(Wall)).Select(w => (Wall)w).ToList();

    using (Transaction tr = new Transaction(doc, "Set Wall Type"))
    {
        try
        {
            tr.Start();
            foreach (Wall wall in allWalls)
            {
                wall.WallType = wt;
            }
            tr.Commit();
            uidoc.RefreshActiveView();
        }
        catch (Exception e)
        {
            TaskDialog.Show("Error", e.Message);
        }
    }
}

 

How to resolve this? 

 

0 Likes
513 Views
4 Replies
Replies (4)
Message 2 of 5

jeremy_tammik
Alumni
Alumni

Sorry, I don't know what might be the problem. 

 

However, you can very easily significantly shorten, simplify and optimise your code like this:

 

 

    public void Test(Document doc)
    {
      WallType wt 
        = new FilteredElementCollector(doc)
          .OfClass(typeof(WallType))
          .FirstElement() as WallType;

      FilteredElementCollector allWalls 
        = new FilteredElementCollector(doc)
          .OfClass(typeof(Wall));

      using (Transaction tr = new Transaction(doc))
      {
        try
        {
          tr.Start("Set Wall Type");
          foreach (Wall wall in allWalls)
          {
            wall.WallType = wt;
          }
          tr.Commit();
        }
        catch (Exception e)
        {
          TaskDialog.Show("Error", e.Message);
        }
      }
    }

 

 

Please avoid ToList and ToElements and suchlike when you can (which is almost always). It is a waste of processing time and memory both:

 

    

  

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

harilalmn
Advocate
Advocate
Thankyou Jeremy, for the concise and neat version of the code. I was, infact having a big round-about, LOL...!

Well, now, coming to the circular reference, I have some generic walls there. These are of family 'Basic Wall'. I am trying to change them to Stacked Walls. But I get the error saying;

"There is a circular chain of references among the highlighted elements"

0 Likes
Message 4 of 5

RPTHOMAS108
Mentor
Mentor

This is something an end user would occasionally face so there is an automated API solution along those lines I expect.

 

"There is a circular chain of references among the highlighted elements" when modifying a stacked wa...

 

It is rarely the case you can globally change multiple types without relationships between instances using those types being impacted. In this case it seems that it has something to do with the generation of the wall joins.

 

I'm unclear on if the existing wall type is stacked and/or the newly assigned one is. One suggestion is to (as an intermediate step) change them to basic walls first. Another is to unjoin walls which can be done in the API via the WallUtils class.

0 Likes
Message 5 of 5

harilalmn
Advocate
Advocate

Let me explain the scenario in detail.

I am trying to change these wall to stacked walls. There are many rooms in the file. Each room has stacked walls as boundary based on many design rules. These stacked wall types are brought in from a 'library of stacked walls' (a .rvt file). But before changing the wall types for a room, the walls which run continuously beyond the room boundary are to be split at joints. For that I have the following method;

 

 

public static void SplitWalls(Room room)
        {
            if (room != null)
            {
                List<Wall> roomWalls = room.GetWalls();
                Queue<Wall> walls = new Queue<Wall>();
                roomWalls.ForEach(w => walls.Enqueue(w));

                using (Transaction t = new Transaction(GlobalSettings.Doc, "wall spliting"))
                {
                    t.SetFailureHandlingOptions(t.GetFailureHandlingOptions().SetFailuresPreprocessor(new AutoDetachOrDeleteFailurePreprocessor()));
                    t.Start();

                    while (walls.Count > 0)
                    {
                        Wall currentWall = walls.Dequeue();
                        
                        //Get all walls intersecting this wall  
                        List<XYZ> intersectionPoints = new List<XYZ>() { currentWall.StartPoint(), currentWall.EndPoint() };
                        intersectionPoints.AddRange(currentWall.GetIntersections());
                    
                        // order intersection points by distance from start point
                        intersectionPoints = intersectionPoints.OrderBy(p => currentWall.StartPoint().DistanceTo(p)).ToList();

                        // Remove points that are closer by tolerence or overlpping
                        List<XYZ> overlappingPoints = new List<XYZ>();

                        for (int i = 0; i < intersectionPoints.Count - 1; i++)
                        {
                            XYZ p1 = intersectionPoints[i];
                            XYZ p2 = intersectionPoints[i + 1];

                            if (p1.DistanceTo(p2) <= GlobalSettings.Doc.Application.ShortCurveTolerance)
                            {
                                overlappingPoints.Add(p2);
                            }
                        }

                        intersectionPoints.RemoveAll(p => overlappingPoints.Contains(p));

                        List<Wall> newWalls = new List<Wall>() { currentWall };

                        // create copies of currentWall
                        for (int i = 0; i < intersectionPoints.Count - 2; i++)
                        {

                            Wall newWall = GlobalSettings.Doc.GetElement(ElementTransformUtils.CopyElement(
                                GlobalSettings.Doc,
                                currentWall.Id,
                                new XYZ(0, 0, 0)
                            ).FirstOrDefault()) as Wall;

                            newWalls.Add(newWall);
                        }

                        // set wall curves
                        for (int i = 0; i < intersectionPoints.Count - 1; i++)
                        {
                            Wall thisWall = newWalls[i];
                            XYZ sp = intersectionPoints[i];
                            XYZ ep = intersectionPoints[i + 1];
                            Curve wallCurve = thisWall.GetCurve();
                            double p1 = wallCurve.Project(sp).Parameter;
                            double p2 = wallCurve.Project(ep).Parameter;
                            wallCurve.MakeBound(p1, p2);
                            thisWall.SetCurve(wallCurve);
                            if (i > 0) walls.Enqueue(thisWall);
                        }
                    }
                    t.Commit();
                }
            }
        }

 

 

Here is the set curve method:

 

 

public static void SetCurve(this Wall wall, Curve curve)
{
    if (wall.IsValidObject == false || wall == null || curve == null) return;


    using (Transaction t = new Transaction(wall.Document, "Set Curve"))
    {
        if (RRAPGlobal.Doc.IsModifiable == false)
        {
            t.Start();
        }
        try
        {
            WallUtils.DisallowWallJoinAtEnd(wall, 0);
            WallUtils.DisallowWallJoinAtEnd(wall, 1);
            ((LocationCurve)wall.Location).Curve = curve;
            WallUtils.AllowWallJoinAtEnd(wall, 0);
            WallUtils.AllowWallJoinAtEnd(wall, 1);
        }
        catch (Exception e)
        {
            TaskDialog.Show("Error", e.Message);
        }

        if (t.HasStarted())
        {
            t.Commit();
        }
    }
}

 

 

Here is the set wall type method;


private void _setWallType(WallType wallType)
{
    // Check if wall is valid
    if (this.Wall == null) return;
    if (wallType == null) return;
    
    using (Transaction t = new Transaction(GlobalSettings.Doc, "Set wall type"))
    {
        t.Start();

        FailureHandlingOptions options = t.GetFailureHandlingOptions();
        options.SetFailuresPreprocessor(new CircularChainWarningSwallower());
        t.SetFailureHandlingOptions(options);

        try
        {
            if (wallType.IsValidObject)
            {
                try
                {
                    WallType wallTypeToSet = new FilteredElementCollector(GlobalSettings.Doc)
                    .OfClass(typeof(WallType))
                    .Cast<WallType>()
                    .Where(x => x.Name == wallType.Name)
                    .FirstOrDefault();

                    if (wallTypeToSet == null)
                    {
                        if (DocumentQueries.ElementTypeExists<WallType>(GlobalSettings.WallLibrary, wallType.Name) == false)
                        {
                            return;
                        }

                        CopyPasteOptions copyPasteOptions = new CopyPasteOptions();
                        copyPasteOptions.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler());

                        wallTypeToSet = GlobalSettings.Doc.GetElement(
                        ElementTransformUtils.CopyElements(
                            GlobalSettings.WallLibrary,
                            new List<ElementId>() { wallType.Id },
                            GlobalSettings.Doc,
                            Transform.Identity,
                            copyPasteOptions
                        ).First()) as WallType;
                    }
                    if (wallTypeToSet != null)
                    {
                        this.WallType = wallTypeToSet;
                        this.Wall.WallType = this.WallType;
                    }
                }
                catch (Exception er)
                {
                }
            }
            else
            {
            }
        }
        catch (Exception e)
        {
            TaskDialog.Show("Error setting wall type", e.Message);
        }
        t.Commit();
    }
}

 

Following are the errors

001.png002.png

 

 

 

 

0 Likes