Laybug Face3D .area error after duplicate()->to_dict()->from_dict()

Hi,

I am getting a funny error in some Ladybug geometry - this is the first time I’ve ever noticed this particular issue. This seems to occur with Ladybug Face3D elements with holes in them. I wonder if anyone has ever encountered this before and knows an easy fix?


Scenario:

I often duplicate models, rooms, geometry, etc. using the standard .duplicate() methods.

In a recent model, it appears that for some Face3Ds (the ones with holes inside them), if I use duplicate() and THEN use to_dict() and from_dict() on the Face3D, I get a weird result for the .area property (it appears to report a face area smaller than the actual area, by the size of the holes).


Example:

Using an example surface from my model with holes in it:

If I report the area of the brep in Rhino, it shows 82.64 :white_check_mark:

If I pull out the LBT Face3D and report the .area, it also shows 82.64 :white_check_mark:

If serialize the Face3D, and then de-serialize it, I still get 82.64 :white_check_mark:

BUT: if I duplicate the Face3D first, THEN serialize/de-serialize the duplicate Face3D, now I get an area of 82.01 :x:


(82.64 - 82.01 = 0.63, which is the area of the holes)

That seems odd, right? I have not dove into the Face3D duplicate method yet to see whats happening in there, but before I do I wonder if there is an easy known fix for this?


Environment:

  • Mac M1
  • MacOS Ventura 13.6.3
  • Rhino 8.6.24086.11002, 2024-03-26
  • Ladybug GH Tools 1.7.26

.GH File

Any advice or suggestions are appreciated, as always!
Thanks!
@edpmay

I was looking at this more closely, and I think I have identied the issue (possibly?)


When Face3D.__copy__() is called, it passes self.vertices in as the ‘boundary’ argument for the new Face3D.__init__().

def __copy__(self):
     _new_face = Face3D(self.vertices, self.plane) #<----- HERE
     self._transfer_properties(_new_face)
     _new_face._holes = self._holes
     _new_face._polygon2d = self._polygon2d
     _new_face._mesh2d = self._mesh2d
     _new_face._mesh3d = self._mesh3d
     return _new_face

I believe this is incorrect and source of the issue. As stated in the docstring for the vertices property:

Tuple of all vertices in this face.
Note that, in the case of a face with holes, some vertices will be repeated
since this property effectively traces out a single boundary around the
whole shape, winding inward to cut out the holes.

whereas the boundary argument is actually supposed to be only the outer boundary (without holes), according to its docstring:

Tuple of vertices on the boundary of this face.
For most Face3D objects, this will be identical to the vertices property.
However, when the Face3D has holes within it, this property stores
the outer boundary of the shape.

So it appears that when ‘duplicating’ a Face3D with holes in it, the holes area incorrectly included as part of the boundary which is passed in. Then, when the holes are set explicitly by the line _new_face._holes = self._holes they end up being doubled up?


Does that appear correct?

For my test case, I have found that by just adding the line _new_face._boundary = self._boundary to __copy__, the error outlined above in the .area property appears to go away and I get the result I was expecting. But I’m not sure if that is a good enough fix to resolve all the cases where this issue might occur? There are a lot of various polygon methods on Face3D that I have not looked at very closely.

@edpmay

1 Like