mozman / ezdxf

Python interface to DXF
https://ezdxf.mozman.at
MIT License
926 stars 190 forks source link

Get some coordinates like this "(0, 0, 0)" when converting INSERT type #910

Closed yoriyouriri closed 1 year ago

yoriyouriri commented 1 year ago

Describe the bug A clear and concise description of what the bug is. i'm trying to convert a dxf file which contains some INSERT entities, but i got some (0,0,0) coordinates in results To Reproduce Information and data needed to reproduce the error:

  1. A simplified Python script which triggers the error.
  2. Information about the ezdxf version and the OS. i'm using ezdxf 1.0.3 on windows 10
  3. If a 3rd party DXF file causes the error, add a simplified and zipped DXF file which still triggers the error.

Confidential Data If you have sensitive files that you can't disclose, try to remove everything sensitive from an existing file or create a brand new one which still triggers the error. Alternatively, you can send the zipped files to confidential@mozman.at, this data is only saved locally (no cloud upload of any kind) and the files are deleted after the error has been fixed.

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem. convert result : image

origin file: image

mozman commented 1 year ago

I need the DXF file, otherwise i can't help.

yoriyouriri commented 1 year ago

Thank you for your reply, here is my DXF file Drawing1.zip

mozman commented 1 year ago

I don't understand what you mean by:

i'm trying to convert a dxf file which contains some INSERT entities, but i got some (0,0,0) coordinates in results

This is shown by the view command of the current beta version of v1.1:

image

This doesn't get any better as this is a 3D model made of thickened 2D elements and the drawing add-on is not designed to render 3D objects.

Even BricsCAD has problems with this model:

image

and Trueview 2023 is not better:

image

yoriyouriri commented 1 year ago

Sorry, I didn't make my question clear. I'm using ezdxf to extract coordinates of elements in this DXF file (such as LINE, POLYLINE, CIRCLE, INSERT) ,but i‘ve got some abnormal coordinates. These abnormal coordinates are located at the origin of the coordinate system. Is this related to the 3D model thing you mentioned above?

mozman commented 1 year ago

I still don't know what you are doing, please post a script.

yoriyouriri commented 1 year ago

test_20230721.zip here is my script, and i've printed points of entities in get_vectors() , i have no idea why there are lots of [0,0,0]

` def readDXF(filepath, filename, png_path, length_unit=None):

if str(length_unit) == '0':
    length_fold = 1
elif str(length_unit) == '1':
    length_fold = 10
elif str(length_unit) == '2':
    length_fold = 1000
else:
    length_fold = 1

dxf_doc = ezdxf.readfile(filepath)
msp = dxf_doc.modelspace()

x_min = dxf_doc.header['$EXTMIN'][0]
y_min = dxf_doc.header['$EXTMIN'][1]
x_max = dxf_doc.header['$EXTMAX'][0]
y_max = dxf_doc.header['$EXTMAX'][1]
dxf_range = [x_min, y_min, x_max, y_max]

load(dxf_doc=dxf_doc, dxf_range=dxf_range)

`

` def resolve(entry, layer): if layer == '': layer = entry.dxf.layer else: layer = layer type = entry.DXFTYPE

if type == 'INSERT':
    if list(entry.virtual_entities()):
        for value in entry.virtual_entities():
            if value.DXFTYPE == 'INSERT':
                continue
            resolve(value, layer)
        return
elif type == 'POLYLINE' or type == 'LWPOLYLINE':
    if type == 'LWPOLYLINE' and entry.has_arc:
        bulge, vectors_l, vectors_t = [], [], []
        vectors_t = get_vectors(type, entry)
        vector_s = vectors_t[0:-1]
        vector_e = vectors_t[1:len(vectors_t)]
    else:
        vectors_t = get_vectors(type, entry)
        vector_s = vectors_t[0:-1]
        vector_e = vectors_t[1:len(vectors_t)]

`

` def load(dxf_doc, dxf_range): msp = dxf_doc.modelspace()

xmin = dxf_range[0]
ymin = dxf_range[1]
xmax = dxf_range[2]
ymax = dxf_range[3]
ll = tuple((xmin, ymin))  
ur = tuple((xmax, ymax))  
ul = (ll[0], ur[1])  
lr = (ur[0], ll[1])  
# xmin,ymin,xmax,ymax
dxf_coordinate_range = [ll[0], ll[1], ur[0], ur[1]]
extent = [[]]

extent[0].append(ul)
extent[0].append(ur)
extent[0].append(lr)
extent[0].append(ll)
extent[0].append(ul)

# print ("EXTMAX ", dxf_doc.header['$EXTMAX'])
# print ("EXTMIN ", dxf_doc.header['$EXTMIN'])
# print ("LIMMAX ", dxf_doc.header['$LIMMAX'])
# print ("LIMMIN ", dxf_doc.header['$LIMMIN'])
# print ("LTSCALE ", dxf_doc.header['$LTSCALE'])
# print ("LUNITS ", dxf_doc.header['$LUNITS'])

group = msp.groupby(dxfattrib='layer')
for layer_name, entities in group.items():
    layer = dxf_doc.layers.get(layer_name)

    if layer.is_off() or layer.color < 0:
        continue
    for entity in entities:
        resolve(entity, '')
    # print(self.text)
    # print(self.point)

def get_vectors(type, entry): vectors_t = [] if type == 'POLYLINE': vectors = list(entry.points()) else: vectors = list(entry.vertices_in_wcs()) if entry.is_closed: for v in vectors: if isinstance(v, tuple): vectors_t.append(v) else: vectors_t.append(v.xyz) vectors_t.append(vectors_t[0]) else: for v in vectors: if isinstance(v, tuple): vectors_t.append(v) else: vectors_t.append(v.xyz) print(vectors_t) return vectors_t`

mozman commented 1 year ago

So you want to write a DXF renderer, check out the drawing addon for how to do that and yes it's complicated: https://github.com/mozman/ezdxf/tree/master/src/ezdxf/addons/drawing

The POLYLINE entity can represents 2D and 3D lines, 3D polyfaces and 3D meshes. In your code you are processing a 3D polyface like a 2D/3D polyline, which does not work because some points are vertices and some points are face records, the points which have an insert location at (0, 0, 0).

This is the compact code how to exclude 3D POLYLINE entities from processing:

from ezdxf import query
...

def resolve(entry, layer):
    dxftype = entry.dxftype()
    if dxftype == "INSERT":
        entities = query.EntityQuery(entry.virtual_entities())
        for pline in entities.query("POLYLINE LWPOLYLINE"):
            resolve(pline, layer)
    elif dxftype == "POLYLINE" and entry.is_2d_polyline:
        get_vectors(dxftype, entry)
    elif dxftype == "LWPOLYLINE":
        get_vectors(dxftype, entry)
mozman commented 1 year ago

This is a simplified version of your code that uses the disassemble module to recursively decompose INSERT entities and the path module to create Path objects:

import ezdxf
from ezdxf import query, disassemble, path

def load(doc):
    msp = doc.modelspace()
    plines = query.EntityQuery(
        disassemble.recursive_decompose(msp), query="POLYLINE LWPOLYLINE"
    )
    for layer_name, entities in plines.groupby(dxfattrib="layer").items():
        try:
            layer = doc.layers.get(layer_name)
            if layer.is_off():
                continue
        except ezdxf.DXFTableEntryError:
            # a layer table entry is not mandatory and may not exist
            pass
        for entity in entities:
            try:
                entity_path = path.make_path(entity)
            except TypeError:  # ignore unsupported types: polyface and polymesh
                continue
            print(str(entity))
            print(list(p.xyz for p in entity_path.control_vertices()))

if __name__ == "__main__":
    doc = ezdxf.readfile("910.dxf")
    load(doc)
yoriyouriri commented 1 year ago

I don't quite understand the meaning of control_vertices, are they releated to the actual location of the points which have an insert location at (0, 0, 0) ?

yoriyouriri commented 1 year ago

For 3D POLYLINE entities, is there something i can do to handle them as 2d entities? I don't mind losing the value of z

mozman commented 1 year ago

The Path class represents ARC and ELLIPSE entities as approximated cubic Bézier-curves. The control vertices define the geometry of Bézier curve: https://pomax.github.io/bezierinfo/

For linear elements the control vertices are the points of the line e.g. LWPOLYLINE without bulge values.

The projection onto the xy-plane is easy:

list((p.x, p.y) for p in entity_path.control_vertices())
yoriyouriri commented 1 year ago

thank u for your reply, it helps me a lot