Description
Describe the bug
When importing shapes from a clean/well-formed DXF file, only one shape is ever found.
To Reproduce
Steps to reproduce the behaviour:
- Import shapes with Geometry.from_dxf()
- Notice that only one shape exists
Expected behaviour
When working with a clean/well-formed DXF file, .from_dxf() should be able to import multiple shapes for analysis.
Additional context
I have a clean DXF file with some closed polylines in it, and all other entities purged/removed. I can import this DXF into shapely. I can also import this file into the cad_to_shapely shim. In both cases, the multiple-shape geometry is preserved.
However, when I do the same via Geometry.from_dxf() only one shape ever comes back. Digging deeper into the code in from_dxf I found this:
my_dxf = c2s.dxf.DxfImporter(dxf_filepath)
my_dxf.process()
my_dxf.cleanup()
polygons = my_dxf.polygons
new_polygons = c2s.utils.find_holes(polygons)
if isinstance(new_polygons, MultiPolygon):
return CompoundGeometry(new_polygons)
elif isinstance(new_polygons, Polygon):
return Geometry(new_polygons)
else:
print(f"No shapely.Polygon objects found in file: {dxf_filepath}")
Looking into find_holes, we find that this behavior is by design:
def find_holes(polygons : List[sg.Polygon]) -> sg.Polygon:
"""
Construct single polygon from imported CAD goemetry.
Assumes there is only one parent shape (the one with the largest gross area.)
Access external perimeter with *polygon.exterior*
Access holes perimeter(s, if there are any) with *polygon.interiors*
Returns:
Shapely Polygon with holes
"""
for p in polygons:
if p.interiors:
return p
#sort by areas, largest first.
polygons.sort(key=lambda x: x.area, reverse=True)
parent = polygons.pop(0)
keepers = []
for p in polygons:
if p.within(parent):
valid = True
for q in polygons:
if (p.intersects(q) or p.within(q)) and p is not q:
valid = False
if valid: keepers.append(p)
new = sg.Polygon(parent,holes=keepers)
return new
So by using this utility function, sectionproperties can never support more than one shape in a DXF. This is not a "bug" in cad_to_shapely, per-se, as its the desired behavior, but by using this utility function, we're throwing out data.
I went through the analogous process by hand, NOT throwing out polygons and manually made a CompoundGeometry object, and it worked:
from cad_to_shapely.dxf import DxfImporter
from shapely.geometry.multipolygon import MultiPolygon
doc = DxfImporter('polylines.dxf')
doc.process()
doc.cleanup()
polys = MultiPolygon(doc.polygons)
g = CompoundGeometry(polys)
g.create_mesh(mesh_sizes=[0.1])
(this showed a little thumbnail of an object in a Jupyter Notebook)