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: 

Element geometry not returning expected face count

12 REPLIES 12
SOLVED
Reply
Message 1 of 13
zrodgersTSSSU
907 Views, 12 Replies

Element geometry not returning expected face count

Hey Everyone, I am trying to get the surfaces between the beams on the bottom of the concrete deck(soffits). 

 

I create a union between the beams and slabs and then iterate though the faces  but it only gives me a face count of 13. It should be 16. I tested the same think in dynamo got the geometry, created a union, and then got the surfaces and the count was correct. 

 

Can someone guide me in the right direction on how to get the surfaces I am wanting?

 

CREATED UNION IN REVIT:

zrodgersTSSSU_2-1626438281368.png

 

 

 

DYNAMO COUNT:

zrodgersTSSSU_0-1626438099595.png

 

MY CODE:

                DirectShape ds = null;
                Solid union = null;
                
                foreach (ElementId elemId in selectedIds)
                {
                    Element elem = uidoc.Document.GetElement(elemId);

                    //get geometry
                    Options opts = new Options();
                    GeometryElement geoElem = elem.get_Geometry(opts);

                    //iterate goemetry elements
                    foreach (GeometryObject geoObj in geoElem)
                    {
                        Solid solid = geoObj as Solid;
                        if (solid != null && 0 < solid.Faces.Size)
                        {
                            if (null == union)
                            {
                                union = solid;
                                
                            }
                            else
                            {
                                union = BooleanOperationsUtils.ExecuteBooleanOperation(union, solid, BooleanOperationsType.Union);
                                
                            }
                        }
                    }

                }

                using (Transaction tx = new Transaction(doc))
                {


                    tx.Start("Generate Formwork");

                    //test to make sure you have correct union
                    ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
                    ds.ApplicationId = "Application id";
                    ds.ApplicationDataId = "Geometry object id";
                    ds.SetShape(new GeometryObject[] { union });

                    tx.Commit();

                }

                //gets the bottom soffits
                int faces = 0;
                List<PlanarFace> listFaces = new List<PlanarFace>();
                if (union != null)
                {
                    foreach (Face face in union.Faces)
                    {
                        testing++;

                        UV pt = new UV(0.5, 0.5);
                        double faceNormalZ = face.ComputeNormal(pt).Z;

                        //get slab
                        if(faceNormalZ == -1)
                        {
                            
                            TaskDialog.Show("revit", face.Area.ToString());
                            

                        }

                    }
                    
                }
  
            }
            TaskDialog.Show("Revit", string.Format("{0} surfaces counted", testing));

 

 

 

12 REPLIES 12
Message 2 of 13

I am not sure whether Dynamo uses the same geometry engine as Revit, and whether your Dynamo code performs the same analysis as the pure Revit API .NET code. Probably not. One thing I would suggest checking is whether the two faces that are missing have a positive Z normal instead of the negative one you are searching for. I agree that the negative Z is expected and desired, but I am not sure it is guaranteed to be so.

 

Oh, and another important thing: your are testing a real number, the Z coordinate, for exact equality with -1. 

 

That is guaranteed to fail!

 

Maybe not always, but sooner or later, for sure.

 

Add some fuzz to your comparison and maybe your woes are over, cf. this recent discussion:

 

https://forums.autodesk.com/t5/revit-api-forum/weird-double-value-that-suppose-to-be-0-but-isn-t/m-p...

 

This is due to the imprecision associated with real numbers in digital computers.

 

The 'weird' number that you see is not really weird at all.

 

It is just a very small number written in exponential or scientific notation.

   

Real numbers in computation: 

 

https://en.wikipedia.org/wiki/Real_number#In_computation

 

Scientific notation:

 

https://en.wikipedia.org/wiki/Scientific_notation

  

From your description, it was generated by adding an offset and subtracting it again. Both of the offsets were not represented precisely, e.g., because they were converted from metric to imperial units. The result was imprecise, almost exactly zero, but very slightly off. 

  

That is completely normal and must be taken into account whenever dealing with real numbers of a digital computer, for instance, in many operations, e.g., comparisons,  by adding some fuzz:

  

     

Translated to your case: 

 

The Z coordinate may be almost equal to -1, but not exactly. 

 

It may have just a very small difference from -1, and you need to take that into account by adding some fuzz, e.g., using an almost-equal-to method instead of exact equality.

 

Exact equality between real numbers does not existing on digital computers, ever.

  

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

@jeremy_tammik Thank you for the response i will update the script to use the almost-equal to method. But I count my faces before i filter by the face normal. So it should be giving me the full count of all the faces (i would think). Unless its not reading the union correctly and its still viewing the elements as separate instead of one piece of geometry. Do you have any ideas on how i could get the two bottom of slab sections as separate faces?

Message 4 of 13

Looking again at your union with my feeble fallible human eyes, I count:

 

  • 1 top face
  • 4 outside side faces
  • 8 inside side faces
  • 1 or 5 bottom faces?

 

Sum total 14 or 18.

 

So maybe I am wrong, or Dynamo, or your Revit API code, or all of us.

 

I would suggest graphically displaying each separate face to find out what is going on in some way, e.g., by drawing different colour model curve elements along their edges offset by a certain distance, for instance like I did here:

 

https://thebuildingcoder.typepad.com/blog/2010/05/curtain-wall-geometry.html

 

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 5 of 13

@jeremy_tammik So its not really combining my geometry into a solid when i create the union. its more of a group... 

 

The only face that joined is the bottom of beams the other faces of the elements are just counted as normal

 

10 - beam sides

1 - beam bottom

1 - bottom of slab

1 - top of slab

 

i dont really understand why this would be though if im creating a union from all those elements shouldnt each intersection create a new face? Did i create the union incorrectly?

 

zrodgersTSSSU_0-1626784539704.png

 

Message 6 of 13

The code creating the union looks ok to me.

 

I count 12 beam sides, not 10: 4 outside and 8 inside.

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 7 of 13

It appears to be counting the long beams on the inside faces as 1 face instead of 2. its not splitting the two long beams at where the middle one intersects them.

Message 8 of 13

Yes, of course, faces can consist of disjoint parts:

 

https://thebuildingcoder.typepad.com/blog/2017/10/disjunct-outer-loops-from-planar-face-with-separat...

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 9 of 13

I figured out a way that worked for me. Thank you for your guidance as it got me to the final solution.

 

Created took the floors geometry by itself and got its Z elevation. Then used a dictionary to store the collected data and then later compare the z elevations from the "Beam and  Floor Union" and the "Floor" by itself.  If the values are equal then its the bottom of the slab.

 

 

 //gets the bottom soffits
                Dictionary<Face, double> beamAndFloorDict = new Dictionary<Face, double>();
                Dictionary<Face, double> floorDict = new Dictionary<Face, double>();

                List<Face> matchingFaces = new List<Face>();

                List<Face> intersectingFaces = new List<Face>();
                if (floorAndBeamUnion != null && floorsUnion != null)
                {
                    foreach(Face face in floorAndBeamUnion.Faces)
                    {
                        

                        double normal = face.ComputeNormal(new UV(0.5, 0.5)).Z;
                        if (normal < 0)
                        {
                            double Z = face.Evaluate(new UV(0.5, 0.5)).Z;
                            
                            beamAndFloorDict[face] = Z;
                            
                            beamAndSlabFacesCount++;
                        }
                        

                    }
                    foreach (Face face in floorsUnion.Faces)
                    {
                        

                        double normal = face.ComputeNormal(new UV(0.5, 0.5)).Z;
                        if (normal < 0)
                        {
                            double Z = face.Evaluate(new UV(0.5, 0.5)).Z;

                            floorDict[face] = Z;

                            floorFacesCount++;
                        }


                    }
                    foreach(KeyValuePair<Face,double> entry in beamAndFloorDict)
                    {
                        
                        foreach(KeyValuePair<Face,double> floorEntry in floorDict)
                        {
                            if(entry.Value == floorEntry.Value)
                            {
                                matchingFaces.Add(entry.Key);
                            }
                        }
                    }

 

Message 10 of 13
Revitalizer
in reply to: zrodgersTSSSU

Hi,

 

when comparing double values, it is necessary to add some tolerance.

 

if(entry.Value == floorEntry.Value)

 

Values may differ by 0.000000001, for example.

 

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





Message 11 of 13
zrodgersTSSSU
in reply to: Revitalizer

I will add that in! Thank you for the response.

Message 12 of 13

That is actually also exactly what I pointed out in my very first initial answer above...

  

Rudi put it much more succinctly, though 🙂

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 13 of 13

@jeremy_tammik yup! I just didn't have it implemented in the last post. Thank  you both for your responses.

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

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community