Find Centroid of wall in Revit API

Find Centroid of wall in Revit API

BBKENGJS
Contributor Contributor
1,435 Views
7 Replies
Message 1 of 8

Find Centroid of wall in Revit API

BBKENGJS
Contributor
Contributor

Please help me with this code. I can get some elements selected in Revit, filter only wall, but I have problem to move from selected ID's of walls to solid so I can use the ComputeCentroid. 

 

<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>

Public Class GCentroid

    Implements IExternalCommand

    Dim _uiApp As UIApplication

    Dim _uiDoc As UIDocument

    Dim _doc As Document

    Public Function Execute(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Autodesk.Revit.UI.Result Implements IExternalCommand.Execute

 

        Try

            ' Select some elements in Revit before invoking this command

            Dim uidoc As UIDocument = commandData.Application.ActiveUIDocument

            Dim selection As Selection = uidoc.Selection

            Dim selectedIds As ICollection(Of ElementId) = uidoc.Selection.GetElementIds()

 

            If 0 = selectedIds.Count Then

                TaskDialog.Show("Revit", "You haven't selected any elements.")

            Else

                Dim info As [String] = "Ids of selected elements in the document are: "

                For Each id As ElementId In selectedIds

                    info += vbLf & vbTab + id.ToString

                Next

 

                TaskDialog.Show("Revit", info)

                ChangeSelection(uidoc)

 

            End If

        Catch e As Exception

            message = e.Message

            Return Autodesk.Revit.UI.Result.Failed

        End Try

 

        Return Autodesk.Revit.UI.Result.Succeeded

    End Function

 

 

    Private Sub ChangeSelection(uidoc As UIDocument)

        ' Get selected elements from current document.

        Dim selectedIds As ICollection(Of ElementId) = uidoc.Selection.GetElementIds()

        Dim opt As Options = New Options

        Dim element As Element = Nothing

        Dim solid As Solid = Nothing

        Dim obj As GeometryObject

 

 

        opt.DetailLevel = ViewDetailLevel.Coarse

 

        TaskDialog.Show("Revit", "Number of selected elements: " + selectedIds.Count.ToString())

 

        ' Go through the selected items and filter out walls only.

        Dim selectedWallIds As ICollection(Of ElementId) = New List(Of ElementId)()

 

        For Each x As ElementId In selectedIds

            element = uidoc.Document.GetElement(x)

            If TypeOf element Is Wall Then

                selectedWallIds.Add(x)

 

                obj = element.Geometry(opt)

 

                MessageBox.Show(obj.Id.ToString)

 

                solid = TryCast(obj, Solid)

 

                'HERE I'M RUNING INTO PROBLEM - CAN'T USE ACTUALY THE "solid" TO ComputeCentroid

 

             

                'Dim centre As XYZ = solid.ComputeCentroid

                'MsgBox(centre.X, centre.Y, centre.Z)

 

            End If

        Next

 

    End Sub

 

End Class

0 Likes
1,436 Views
7 Replies
Replies (7)
Message 2 of 8

jeremy_tammik
Alumni
Alumni

So, once you have obtained the solid from the wall geometry, you will be able to call the ComputeCentroid method and all will be well.

 

In that case, a better title for this post would be "how to get a solid from a wall element".

 

I asked ChatGPT for you:

  

Jeremy: How can I obtain a solid geometry element from a Revit database element using the Revit API?

  

ChatGPT: To obtain a solid geometry element from a Revit database element using the Revit API, you can use the following steps:

  

  1. Get the element's geometry element using the GeometryObject property of the element.
  2. Check if the geometry object is a Solid using the IsSolid property of the GeometryObject class.
  3. If the geometry object is a solid, cast it to a Solid object and use its properties and methods to manipulate the solid geometry.

  

Here is an example code snippet in C#:

 

 

// Assume "element" is the Revit database element you want to get the solid geometry from
GeometryElement geometryElement = element.get_Geometry(new Options());
foreach (GeometryObject geomObj in geometryElement)
{
    if (geomObj is Solid solid)
    {
        // Use the solid geometry
        // For example, you can get the volume of the solid:
        double volume = solid.Volume;
    }
}

 

  

Note that not all elements in Revit have solid geometry, so you need to check if the geometry object is a solid before casting it to a Solid object.

  

chatgpt_get_solid_from_element.png

     

This answer is correct, to the best of my knowledge. The official Revit SDK samples, The Building Coder and numerous other sources provide hundreds of samples illustrating the proper steps, so you should be able to manage on your own from here.

  

Actually, here is one of them that does exactly what you seek and more:
  
   
Please note that generic questions such as this are more efficiently answered by searching the Internet yourself rather than waiting for me or other humans to address this thread and answer you by hand.
  
Good luck and have fun!
  
Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 8

BBKENGJS
Contributor
Contributor

Thank You for the quick reply. However I'm still can not make it work.

Please help me to fix the cod.

 

Private Sub ChangeSelection(uidoc As UIDocument)

        ' Get selected elements from current document.

        Dim selectedIds As ICollection(Of ElementId) = uidoc.Selection.GetElementIds()

        Dim opt As Options = New Options

        Dim element As Element = Nothing

        Dim solid As Solid = Nothing

        Dim obj As GeometryObject

 

        opt.DetailLevel = ViewDetailLevel.Coarse

 

        TaskDialog.Show("Revit", "Number of selected elements: " + selectedIds.Count.ToString())

 

        ' Go through the selected items and filter out walls only.

        Dim selectedWallIds As ICollection(Of ElementId) = New List(Of ElementId)()

 

        For Each x As ElementId In selectedIds

            element = uidoc.Document.GetElement(x)

            If TypeOf element Is Wall Then

                selectedWallIds.Add(x)

 

                obj = element.Geometry(opt)

 

                MessageBox.Show(obj.Id.ToString)

                If obj Is solid Then

                    solid = TryCast(obj, Solid)

                End If

 

                'HERE I'M GETTING ERROR IN REVIT:

                 ' "Object reference not set to an instance of an object."

 

                Dim centre As XYZ = solid.ComputeCentroid

                MsgBox(centre.X, centre.Y, centre.Z)

 

            End If

        Next

 

    End Sub

 

Getting error in Revit : "Object reference not set to an instance of an object."

 

When I'm trying to use the "get_Geometry" Getting also error in M Visual Studio:

"get_Geometry is not a member of "Element" and I couldn’t find “get_Geometry” method in revitapidocs. 

 

Private Sub Test()
Dim element As Autodesk.Revit.DB.Element = Nothing
Dim geometryElement As GeometryElement = element.get_Geometry(New Options())
Dim solid As Solid = Nothing

For Each geomObj As GeometryObject In geometryElement

If geomObj Is solid Then
solid = TryCast(geomObj, Solid)

Dim volume As Double = solid.Volume
End If
Next
End Sub

0 Likes
Message 4 of 8

RPTHOMAS108
Mentor
Mentor

Regarding your first error in red your 'solid' variable is not assigned anything because the first thing you will encounter from Element.Geometry will be a GeometryElement (collection object) containing solids not the solids themselves.

 

If obj Is solid Then
    solid = TryCast(obj, Solid)
End If

 

That isn't how you do a type comparison, you got that right higher up in your code using 'TypeOf'. Here 'obj' is a GeometryElement and 'solid' is nothing. Although you know it isn't a solid at that point so you don't need to do that check.

 

Element.get_Geometry is only used in C# in VB.Net it is just Element.Geometry.

 

There is a basic example in RevitAPI.chm under Element.Geometry property:

Geometry Property (revitapidocs.com)

0 Likes
Message 5 of 8

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @BBKENGJS , 


Have you tried exploring the element using the RevitLookup tool?

One suggestion is to explore the element using the RevitLookup tool. RevitLookup tool will help you to find out the value you need to set for your geometry option to get the required solid.

 

I think the problem lies in this line element.get_Geometry(New Options()).(Sometimes using "New Options()" won't work)

 

Instead of New Options(), please use app.Create.NewGeometryOptions().
Here is the sample code

Options options = app.Create.NewGeometryOptions();
options.ComputeReferences = true;
options.IncludeNonVisibleObjects = true;
options.View = doc.ActiveView;
GeometryElement geometryElement = element.get_Geometry(options);

(Please set appropriate values to the geometry options properties, You can get this value from the RevitLookup tool)


Please take a look at the below link.
https://forums.autodesk.com/t5/revit-api-forum/identifying-the-light-source-geometry/td-p/9343436 


Here is the link to download the RevitLookup tool.

https://github.com/jeremytammik/RevitLookup 



Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes
Message 6 of 8

jeremy_tammik
Alumni
Alumni

Dear Naveen and @BBKENGJS ,

  

ComputeReferences is only needed if you require references to the geometry, e.g., for dimensioning purposes. Furthermore, it adds computational effort. Therefore you should not set it to true unless needed:

 

 

Look at this recent benchmark result:

 

 

IncludeNonVisibleObjects is only required for certain supplementary graphical elements, e.g., for curtain walls:

  

  

I am pretty sure that it is never needed for such basic element geometry as solids.

 

So, I would leave both of those turned off in this case, set to their default value of false.

 

Furthermore, I very much doubt that there is any different between using `new Options` and `app.Create.NewGeometryOptions`. 

  

However, specifying a view argument in the options will definitely make a difference, depending on the view you supply. That can be achieved using both `new Options` and `app.Create.NewGeometryOptions`. 

  

I hope this clarifies.

  

Cheers

  

Jeremy

  

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

BBKENGJS
Contributor
Contributor

Thank you all for any suggestions. I managed to fix the cod. That was my first contact with Revit API, after my project required hundreds centroids for finding Lifting locations. It's a big shame that for the many we paid for Revit we can not get such basic  things like e.g. centroid. So we have to spend another thousands to buy extensions or try to dig into Revit API.  Now I have to add some extra cod which allow me to insert an instance of 3D family into centroid and add this point to assembly.

 

Here is my fixed cod:

 

Private Sub ChangeSelection(uidoc As UIDocument)

        ' Get selected elements from current document.

        Dim selectedIds As ICollection(Of ElementId) = uidoc.Selection.GetElementIds()

        Dim element As Element = Nothing

        Dim solid As Solid = Nothing

 

        TaskDialog.Show("Revit", "Number of selected elements: " + selectedIds.Count.ToString())

 

        ' Go through the selected items and filter out walls only.

        Dim selectedWallIds As ICollection(Of ElementId) = New List(Of ElementId)()

 

        For Each x As ElementId In selectedIds

            element = uidoc.Document.GetElement(x)

            If TypeOf element Is Wall Then

 

                selectedWallIds.Add(x)

 

                Dim wall As Wall = TryCast(uidoc.Document.GetElement(element.Id), Wall)

                Dim wallGeo As GeometryElement = wall.Geometry(New Options())

 

                For Each y As Solid In wallGeo

 

                    Dim centre As XYZ = y.ComputeCentroid

                    MessageBox.Show("CENTROID of WALL  " & element.Id.ToString & vbCrLf & vbCrLf & "  X= " & centre.X & "  Y= " & centre.Y & "  Z= " & centre.Z)

                Next

            End If

        Next

 

        ' Set the created element set as current select element set.

        uidoc.Selection.SetElementIds(selectedWallIds)

 

        If 0 <> selectedWallIds.Count Then

            TaskDialog.Show("Revit", selectedWallIds.Count.ToString() + " Walls are selected!")

        Else

            TaskDialog.Show("Revit", "No Walls have been selected!")

        End If

    End Sub

 

0 Likes
Message 8 of 8

RPTHOMAS108
Mentor
Mentor

That code can be simplified slightly

 

 If TypeOf element Is Wall Then

 

Then

 

Dim wall As Wall = TryCast(uidoc.Document.GetElement(element.Id), Wall)

 

Still a wall (no need for TryCast depending on your options set). 

Plus you have already got 'element' no sense in getting the same thing twice from Revit. The below would work fine with the default VB.net options:

 

Dim Wall as Wall = element
'or even
Dim Wall = element

 

When iterating the GeometryObject items in the GeometryElement you should check each is a Solid rather than just assuming.

 

In general the geometry exposed by the API is a by-product of that used by Revit itself for graphical representation. Gravitational centroid is a bit problematic in general unless your wall is quite simple otherwise it could be affected by:

 

Differing densities of materials used in wall layers.

Wall sweeps/reveals defined on the types or hosted separately.

 

 

 

0 Likes