compas-dev / compas-dev/compas
`DXF` reader enhancement
- Dominant language
- Python
- Stars
- 386
- Forks
- 122
- Avg merge
- 11d 46m
- Merged PRs (30d)
- 1
Description
# Feature Request
Currently, the `DXF` reader is not development. I had a bit of time looking at it and felt we could develop it. @tomvanmele
## Details
`.dxf` is a fairly complex format containing many many datatypes, but general datatype like `Line` and `Mesh` support might be helpful. I found that `ezdxf` package is pretty lightweight and can be very helpful. shall we introduce this package?
## Example code
```python
import ezdxf
class DXF(object):
"""Class for working with DXF files.
Parameters
----------
filepath : path string | file-like object
A path, a file-like object.
precision : str, optional
A precision specification.
Attributes
----------
reader : :class:`DXFReader`, read-only
A DXF file reader.
parser : :class:`DXFParser`, read-only
A DXF data parser.
References
----------
* https://en.wikipedia.org/wiki/AutoCAD_DXF
* http://paulbourke.net/dataformats/dxf/
* http://paulbourke.net/dataformats/dxf/min3d.html
* https://ezdxf.readthedocs.io/
"""
def __init__(self, filepath, precision=None):
self.filepath = filepath
self.precision = precision
self._is_parsed = False
self._reader = None
self._parser = None
def read(self):
"""Read and parse the contents of the file."""
self._reader = DXFReader(self.filepath)
self._parser = DXFParser(self._reader, precision=self.precision)
self._reader.open()
self._reader.read()
self._parser.parse()
self._is_parsed = True
@property
def reader(self):
if not self._is_parsed:
self.read()
return self._reader
@property
def parser(self):
if not self._is_parsed:
self.read()
return self._parser
@property
def vertices(self):
return self.parser.vertices
@property
def lines(self):
return self.parser.lines
@property
def points(self):
return self.parser.points
@property
def face3ds(self):
return self.parser.face3ds
@property
def meshes(self):
return self.parser.meshes
@property
def polyline2ds (self):
return self.parser.polyline2ds
@property
def polyline3ds (self):
return self.parser.polyline3ds
class DXFReader(object):
"""Class for reading data from DXF files.
Parameters
----------
filepath : path string | file-like object
A path, a file-like object.
"""
def __init__(self, filepath):
self.filepath = filepath
self.doc = None
self.content = None
self.lines = None
self.points = None
self.face3ds = None
self.meshes = None
self.polyline2ds = []
self.polyline3ds = []
def open(self):
"""Open the file and read its contents.
Returns
-------
None
"""
try:
self.content = ezdxf.readfile(self.filepath)
except IOError:
print(f"Not a DXF file or a generic I/O error.")
except ezdxf.DXFStructureError:
print(f"Invalid or corrupted DXF file.")
def read(self):
"""Read the contents of the file."""
self.lines = self.content.modelspace().query("LINE")
self.points = self.content.modelspace().query("POINT")
# The 3DFACE entity is real 3D solid filled triangle or quadrilateral.
self.face3ds = self.content.modelspace().query("3DFACE")
# Not yet implemented, example file needed
# self.meshes = self.content.modelspace().query("MESH")
polyline_entities = self.content.modelspace().query("POLYLINE")
for entity in polyline_entities:
if entity.is_2d_polyline:
self.polyline2ds.append(entity)
elif entity.is_3d_polyline:
self.polyline3ds.append(entity)
class DXFParser(object):
"""Class for parsing data from DXF files.
The parser converts the raw geometric data of the file
into corresponding geometry objects and data structures.
Parameters
----------
reader : :class:`DXFReader`
A DXF file reader.
precision : str
Precision specification for parsing geometric data.
"""
def __init__(self, reader, precision):
self.precision = precision
self.reader = reader
self.vertices = None
self.points = None
self.face3ds = None
self.lines = None
self.polylines = None
self.faces = None
self.groups = None
self.objects = None
self.meshes = None
self.polyline2ds = None
self.polyline3ds = None
def parse(self):
"""Parse the the data found by the reader."""
self.lines = [
[list(line.dxf.start), list(line.dxf.end)] for line in self.reader.lines
]
self.points = [list(point.dxf.location) for point in self.reader.points]
# self.meshes = [
# [mesh.MeshData.vertices, mesh.MeshData.faces] for mesh in self.reader.meshes
# ]
# self.parse_face3ds()
self.face3ds = [[v.xyz for v in face.wcs_vertices()] for face in self.reader.face3ds]
# The POLYLINE entity is very complex, it’s used to build 2D/3D polylines, 3D meshes and 3D polyfaces.
# TODO Convert OCS to WCS
self.polyline2ds = [[v.dxf.location for v in polyline.vertices] for polyline in self.reader.polyline2ds]
self.polyline3ds = [[v.dxf.location for v in polyline.vertices] for polyline in self.reader.polyline3ds]
def parse_face3ds_deprecated(self):
# Not used for now
# At the moment, the 3d faces are parsed as points
if self.face3ds is None:
self.face3ds = []
for face3d in self.reader.face3ds:
[f0, f1, f2, f3] = face3d.get_edges_visibility()
if f0:
self.face3ds.append([list(face3d.dxf.vtx0), list(face3d.dxf.vtx1)])
if f1:
self.face3ds.append([list(face3d.dxf.vtx1), list(face3d.dxf.vtx2)])
if f2:
self.face3ds.append([list(face3d.dxf.vtx2), list(face3d.dxf.vtx3)])
if f3:
self.face3ds.append([list(face3d.dxf.vtx3), list(face3d.dxf.vtx0)])
```
This code support:
| | .dxf |
| ----------------- | --------------------------- |
| unit | |
| coordinate system | |
| vertices | list[list[x,y,z]] |
| faces | list[int] |
| normals | |
| lines | list[[x0,y0,z0],[x1,y1,z1]] |
| polyline | list[list[x,y,z]] |
| points | list[x,y,z] |
| groups | |
| textures | |
Contributor guide
Assessment
This issue has not been assessed yet.