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:
DYNAMO COUNT:
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));
Solved! Go to Solution.
Solved by zrodgersTSSSU. Go to Solution.
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:
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 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?
Looking again at your union with my feeble fallible human eyes, I count:
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 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?
The code creating the union looks ok to me.
I count 12 beam sides, not 10: 4 outside and 8 inside.
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.
Yes, of course, faces can consist of disjoint parts:
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);
}
}
}
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
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 yup! I just didn't have it implemented in the last post. Thank you both for your responses.