Geometry.from_dxf can only ever import one shape
See original GitHub issueDescribe 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)
Issue Analytics
- State:
- Created a year ago
- Comments:16 (7 by maintainers)
Top GitHub Comments
I added in dev branch of cad_to_shapely
utils.py
a functionfilter_polygons
that’ll return a list of inferred “topographical surfaces” from closed curves (shapely polygons with/without holes/both). This ought to address various quandaries bought up in this thread & others. Aim is to depreciatefind_holes
.Got some bits and bobs to sort out, then will try and tie in with section-properties
from_dxf
/load_dxf
and make pull request.@robbievanleeuwen At moment section-properties
load_dxf
callsc2s.utils.find_holes
. This island-finding stuff is kind of in hinterland “upstream” ofsection-properties
and “downstream” ofcad-to-shapely
(why it inc2s.utils
module)Also some complications arise when you consider if user wants to import multiple sections from a single dxf (list of
Geometry
objects; I recall someone wanting to do that), or a single compound section (CompoundGeometry
). I am going to aim at the latter in pull request on the grounds that if you want to import and analyse multiple sections user can sort it out ‘upstream’ in CAD package (multiple DXF files), or programmatically-by-yourself (somehow!)I’m not following about the hatches and so on.
I think using the logic in
.find_holes
iteratively, preserving multiple geometries (except those that are holes) makes closer sense. I might even be able to tackle that since you’ve already done the hard part of figuring out how to do it.