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: 

Getting neighboring curtain panel

10 REPLIES 10
Reply
Message 1 of 11
JOfford_13
1221 Views, 10 Replies

Getting neighboring curtain panel

Given a certain Panel element within a Wall element, how may I retrieve the adjacent Panel in a particular direction?  The code below works but only when the adjacent BoundingBoxes overlap the original BoundingBox, which is not guaranteed.  It feels like the answer should implement the Wall.CurtainGrid.GetPanel(uGridLineId, vGridLineId) method but I don't know a quick way of retrieving that the intersecting CurtainGridLine elements when simply provided a Panel element.

 

 

public static Panel GetAdjacentPanel(this Panel panel, AdjacentDirection direction)
{
    Panel result = null;

    if (panel.Host is Wall wall && wall.CurtainGrid != null)
    {
        // get the center of the panel's bounding box
        var panelBoundingBox = panel.get_BoundingBox(null);
        var panelCenter = (panelBoundingBox.Max + panelBoundingBox.Min) / 2;

        // determine the top of the wall
        var wallBoundingBox = wall.get_BoundingBox(null);
        var zMax = direction == AdjacentDirection.Above ? wallBoundingBox.Max.Z : wallBoundingBox.Min.Z;

        var collector = new FilteredElementCollector(panel.Document);

        // filter by a bounding box intersection
        var outline = new Outline(panelCenter, new XYZ(panelCenter.X, panelCenter.Y, zMax));
        var filter = new BoundingBoxIntersectsFilter(outline);
        collector.WherePasses(filter);

        // typical quick filters for curtain panels
        var categoryFilter = new ElementCategoryFilter(BuiltInCategory.OST_CurtainWallPanels);
        collector.WherePasses(categoryFilter);
        collector.OfClass(typeof(FamilyInstance));

        // select the closest panel that isn't the original panel
        result = collector
            .Where(e => e.Id != panel.Id)
            .OrderBy(e =>
            {
                var bb = e.get_BoundingBox(null);
                var ctr = (bb.Max + bb.Min) / 2;
                return ctr.DistanceTo(panelCenter);
            })
            .FirstOrDefault() as Panel;
    }

    return result;
}

 

 

 

10 REPLIES 10
Message 2 of 11
jeremytammik
in reply to: JOfford_13

Dear JOfford,

 

Thank you for your interesting query and nice implementation so far.

 

You say that the code you shared works when the adjacent BoundingBoxes overlap the original one.

 

Well, if they do not, how about simply enlarging them a bit?

 

Say, by half the original panel size in each direction?

 

I hope this helps.

 

Best regards,

 

Jeremy

 



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

Message 3 of 11
JOfford_13
in reply to: jeremytammik

Indeed I can fuss with the extents of the "slicer" BoundingBox, say increase the out-of-plane dimension to be vastly larger than anything realistic, but in the end it'll never be a fool proof solution because users can always create families of any configuration, including families that go beyond the host Cell extents, or completely ignore the host Cell constraints.

 

I was hoping there was a simple way to query a Panel element and have it return its row/column index, or rather its u/v CurtainGridLine indexes.  Then I could just increment the index by 1, or -1 depending on direction, then call Wall.CurtainGrid.GetPanel(row, column) to guarantee I have the true adjacent panel, irregardless of geometry (if solid geometry even exists).  The use-case for Wall.CurtainGrid.GetPanel() is very bizarre if something like this isn't easily accessible via the API.  GetPanel() expects two CurtainGridLine parameters but the documentation is unclear as to which two are required.

 

Fun side fact:  If you create a new Curtain Panel family and immediately load it into a project without creating any content, there is no way to select the panel element ever again in the UI view.  It is only selectable through the Project Browser > Select All Instances.  Another situation, although ridiculous, where the BoundingBox approach would fail.

Message 4 of 11
aignatovich
in reply to: JOfford_13

Hi, guys!

 

There is a solution, but it is not very simple.

 

Panel has GetRefGridLines method, that returns u and v grid lines ids as ref parameters. u grid line is at the bottom of your panel, v grid line is at the left of your panel, so u grid line id is ElementId.InvalidElementId for the bottom panel row and v grid line id is also ElementId.InvalidElementId for the left panel column. "Left" is when wall orientation points to your eye.

 

You can retrieve all u and v grid lines from the curtain wall, you can also retrieve a geometry line from your grid line, so you can order them. For example, you need to get top left panel. Get a preceding line from ordered v lines and next to your line from ordered u lines, then get a panel using CurtainGrid.GetPanel method.

 

By the way, you can select a panel pressing "tab" button in the UI

Message 5 of 11
aignatovich
in reply to: JOfford_13

It seems, that GetUGridLineIds and GetVGridLineIds methods return curtain grid lines already in right order, but I don't know exactly, and there is no such information in SDK help file. I suppose, only development teem can answer, is it true or not.

Message 6 of 11
JOfford_13
in reply to: aignatovich


 By the way, you can select a panel pressing "tab" button in the UI

 

If the Panel family has no content/geometry there is nothing to tab to, yet the Panel element does exist.

Message 7 of 11
jeremytammik
in reply to: JOfford_13

Dear JOfford,

 

Another thought that came to mind:

 

Have you had a look at the relevant SDK samples, e.g., CurtainWallGrid, Massing/MeasurePanelArea and  Massing/PanelEdgeLengthAngle?

 

I searched for 'gridline'... there may be other, more relevant search terms otoo.

 

I hope this helps.

 

Best regards,

 

Jeremy

 



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

Message 8 of 11
JOfford_13
in reply to: jeremytammik

I believe I found a solution that works for basic grid systems, basic meaning all CurtainGridLines do not have any segments "removed".  When non-continuous CurtainGridLines are introduced the results vary (see attachment).  Dealing with these odd configurations, although rare, will be a problem.  When CurtainGrid.GetPanel(u,v) returns a Panel with no area, a.k.a. a false result, I don't know how to retrieve the true physical Panel occupying that space. 

 

This solution is also dependent on the fact that CurtainGrid.GetUGridLineIds() always returns them in vertical order (lowest Z at index 0).   The SDK documentation states CurtainGrid.GetUGridLineIds() "Gets all ElementIds of grid lines in the U direction", although I didn't see a way to have the CurtainGrid indicate which direction U is pointing, nor did I find a way to manually set the U direction using the UI.  Is it true that U is ALWAYS upwards for Wall elements?

  

public static Panel GetAdjacentPanel(this Panel panel, AdjacentDirection direction)
{
    Panel result = null;

    if (panel.Host is Wall wall && wall.CurtainGrid != null)
    {
        ElementId uGridLineId = null;
        ElementId vGridLineId = null;

        panel.GetRefGridLines(ref uGridLineId, ref vGridLineId);

        var uGridLineIds = wall.CurtainGrid.GetUGridLineIds();

        ElementId previousId = null;

        if (direction == AdjacentDirection.Above)
        {
            foreach (var id in uGridLineIds)
            {
                if (uGridLineId == ElementId.InvalidElementId || previousId == uGridLineId)
                {
                    var adjacent = wall.CurtainGrid.GetPanel(id, vGridLineId);

                    if (adjacent.HasArea())
                    {
                        result = adjacent;
                        break;
                    }
                }

                previousId = id;
            }
        }
        else if (uGridLineId != ElementId.InvalidElementId)
        {
            previousId = ElementId.InvalidElementId;

            foreach (var id in uGridLineIds)
            {
                if (uGridLineId == id)
                {
                    result = wall.CurtainGrid.GetPanel(previousId, vGridLineId);
                    break;
                }

                var current = wall.CurtainGrid.GetPanel(id, vGridLineId);

                if (current.HasArea())
                {
                    previousId = id;
                }
            }
        }
    }

return result; }
public static bool HasArea(this Panel panel)
{
    var parameter = panel.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED);
    return parameter.HasValue;
}

 

 

Message 9 of 11
Ning_Zhou
in reply to: JOfford_13

i cannot find AdjacentDirection definition in Revit API even in 2018 version, did i miss something simple?

Message 10 of 11
RPTHOMAS108
in reply to: Ning_Zhou

I'm assuming 'AdjacentDirection' is a Enum created by the developer and is not part of the API i.e. they want an option looking above or below but that will fall apart when the curtain system is part of a roof etc.

 

There is another post floating about somewhere that provides a more universal solution I think this post may be older than that one. I'm not sure if the features were updated in-between.

 

Essentially you have CurtainGrid.GetCurtainCells I assume they are returned in some order of the grid (would have to check).

CurtainGrid.NumPanels

CurtainGrid.NumULines
CurtainGrid.NumVLines

 

So you can set up an indexing system to use with the collection of cells from CurtainGrid.GetCurtainCells.

 

Obviously cells are not always panels.

 

For merged panels it is harder but I believe you can always find the merged panels indirectly via their borders i.e. the difference between:

CurtainGridLine.AllSegmentCurves
CurtainGridLine.ExistingSegmentCurves

for each grid.

 

So I think you would set out to find the locations of the merged panels first then when you create your 2d indexing system you can have two or more slots pointed towards the same panel. Essentially if you are iterating the grids to build the collection you know the current panel id is the same as the neighbour if the segment in either of the two directions of items already iterated is  missing.

 

If all else fails you have the outlines of the panels but again for complex systems it is probably best to avoid assumptions about geometrical relationships.

 

If you add a cut-profile to the wall then that likely adds even more complications. I always find this with Revit: it is the perfect solution until the user starts using another feature that needs accounting for.

 

Message 11 of 11
Ning_Zhou
in reply to: JOfford_13

right, thanks RPTHOMAS108 for the detailed description, in fact, i try to number panels (anti)clockwise to avoid error in manual way, since multiple curtain walls are involved, i may have to sort curtain walls first before numbering curtain panels

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

Post to forums  

Autodesk Customer Advisory Groups


Rail Community