Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symmetric difference does not create a hole #281

Closed
robbievanleeuwen opened this issue Jun 17, 2023 · 2 comments · Fixed by #297
Closed

Symmetric difference does not create a hole #281

robbievanleeuwen opened this issue Jun 17, 2023 · 2 comments · Fixed by #297
Assignees
Labels
bug Something isn't working

Comments

@robbievanleeuwen
Copy link
Owner

robbievanleeuwen commented Jun 17, 2023

Performing a symmetric difference operation does not create a hole where regions have been differenced away.

To reproduce:

from sectionproperties.pre.library.primitive_sections import circular_section
from sectionproperties.analysis.section import Section

circ1 = circular_section(d=100, n=64)
circ2 = circular_section(d=100, n=64).shift_section(x_offset=35)
geom = circ1 ^ circ2
geom.create_mesh(mesh_sizes=5)
Section(geometry=geom).plot_mesh()

Generates the following:

geometry-12 hires

Expected behaviour:
There should be a hole in the centre region - note the coarser mesh indicating the lack of a control point here.

Discussion:
If this is too difficult I wouldn't be against removing this functionality. I am having a hard time trying to come up with a real-life use case for this operation, but perhaps more creative minds can find a use!

@robbievanleeuwen robbievanleeuwen added the bug Something isn't working label Jun 17, 2023
@robbievanleeuwen
Copy link
Owner Author

See also newly documented example.

@connorferster
Copy link
Collaborator

Ok, I have spent a bit of time with this today and it might be a bit shifty of a fix.

The general strategy that I have been relying on in the pre-processing is that, when a hole is created, it is fully enclosed in a shapely Polygon and the hole is present in the .holes attribute of the shapely Polygon. This strategy is not robust enough because it does not handle the case where even shapely does not recognized a hole.

Example where symmetric difference is "manually" performed:

int_geom = circ1 & circ2
geom = (circ1 | circ2) - int_geom
geom.plot_geometry()

image

Shapely does not recognize a hole here because the resulting MultiPolygons are valid shapely Polygons that only touch at points. Therefore, when I perform the unary_union during the hole-finding algorithm on the MultiPolygon, I don't get a Polygon with a hole, I get the same original MultiPolygon.

To come up with a more robust algorithm, I would need to have some way of getting this MultiPolygon "converted" to a Polygon.

One possibility

I can change line no. 2387 to add a .buffer:

        unionized_poly = unary_union([geom.geom for geom in self.geoms]).buffer(1e-6)

This succeeds in converting the MultiPolygon into a Polygon and the hole is then recognized:

image

This could work because I am only performing the unary_union and .buffer to find holes. The result from these operations is not used in any way for the actual analysis geometry. But, this raises two questions:

  1. What is an appropriate buffer amount to be a very, very small quantity that would work at geometry at any scale?
  2. Will this cause unintended behaviour in other operations? I.e. the creation of holes that should not exist.

For the first question: Perhaps the scale could be based on the resulting area of the unary union: say, if the area was in the 1000 scale, then the buffer would be performed at the scale of 1000 * 1e-9 where 1e-9 is an arbitrary constant that we choose to find a buffer amount that is sufficiently small from the scale of the original geometry that it can be considered inconsequential.

For the second question: Well, all the tests pass ¯_(ツ)_/¯ So, without thinking of a specific scenario where the buffer could cause unwanted holes, then maybe that is good enough? I tested this 1e-9 constant to see at what (small) scale, this breaks down. It seems to work down to geometries at scales of 1e-7, after which the holes are not recognized.

image

FYI: At scales of 1e-11, shapely geometries themselves start to break down.

image

Conclusion

I think this could work well as a solution to this problem. However, @robbievanleeuwen brings up a good point that this kind of operation may be moot anyway since it leaves the modeller with a model with singularities and is not actually useful. I see this symmetric difference as possibly being an operation on the way to a final geometry but not necessarily the final geometry. Additionally, the result of the symmetric difference can be achieved with intersection and difference so I don't think that removing the functionality of symmetric difference will solve this problem.

So, I have included this proposed fix in #297 for review!

@robbievanleeuwen robbievanleeuwen linked a pull request Oct 1, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants