[PYTHON] DXF and turtle

Summary of this article

I roughly read the DXF file with python and displayed the contents. I tried it in Windows10 32bit Anaconda environment, but I think that it can be done in almost the same environment if python works.

What is DXF

It is an abbreviation for ** D ** ata e ** X ** change ** F ** ormat (I thought it was ... but it may have been different](http: // qiita. com / kazhida / items / 8a5fa68e8fa135614d5d)). It's a name like a JSON or XML rival or relative, but it's not something like that, it's like an intermediate / common format for exchanging ** drawing data ** between different CADs. The Internet has existed since the era when it was not fragile, and while each company has its own interpretations and extensions, the standards themselves have changed and expanded, making it difficult to get along with. Often, the DXF data spit out by other CAD cannot be read, and when I open the DXF read / write settings in CAD software, it is ** quite huge and I don't want to see it too much **. It is enough to handle 2D data of straight lines, circles and arcs for what I usually use (3D data is often exchanged with SAT, IGES, STEP, etc.), but DXF standards are free curves and free It seems that it has been expanded to handle curved lines, meshes, solids, etc.

So what is it?

There is an application that I am trying to make personally, and if there is a small drawing / drafting function that is not essential, the application is likely to expand. However, implementing such a drawing function is too difficult. Then how about importing the figures created by other CAD software? I looked it up and found a module called ** dxfgrabber ** in python, so why not give it a try? … That's this article (too long introductory). There is also software called pycam that should be able to read and write dxf, so I took a look at the source, but it didn't feel like I could see where and what I was doing, so the pycam hack route was left unattended.

dxfgrabber If you search with the keyword DXF on pypi, it will appear at the top. It doesn't seem to have anything to do with compilation or binaries, and installing it with pip in the Anaconda environment doesn't seem to cause any problems.

pip install dxfgrabber

Easy to use

File reading

>>> import dxfgrabber
>>> dxf=dxfgrabber.readfile("hogehoge.dxf")

Display of various headers

Various headers are stored in the dictionary by the above reading. I feel the darkness of DXF when there are only a few items or the items come out tightly depending on the CAD software that spit out DXF.

>>> print(dxf.header)
{'$ACADVER': 'AC1015', '$DWGCODEPAGE': 'ANSI_932', '$ACADMAINTVER': 6, '$INSBASE': (0.0, 0.0, 0.0), '$EXTMIN': (-250.0, -250.0, 0.0), '$EXTMAX': (250.0, 250.0, 0.0), '$LIMMIN': (-250.0, -250.0), '$LIMMAX': (250.0, 250.0), '$ORTHOMODE': 0, '$REGENMODE': 1, '$FILLMODE': 1, '$QTEXTMODE': 0, '$MIRRTEXT': 1, '$LTSCALE': 1.0, '$ATTMODE': 1, '$TEXTSIZE': 0.2, '$TRACEWID': 0.05, '$TEXTSTYLE': 'Standard', '$CLAYER': '0', '$CELTYPE': 'ByLayer', '$CECOLOR': 256, '$CELTSCALE': 1.0, '$DISPSILH': 0, '$DIMSCALE': 1.0, '$DIMASZ': 0.18, '$DIMEXO': 0.0625, '$DIMDLI': 0.38, '$DIMRND': 0.0, '$DIMDLE': 0.0, '$DIMEXE': 0.18, '$DIMTP': 0.0, '$DIMTM': 0.0, '$DIMTXT': 0.18, '$DIMCEN': 0.09, '$DIMTSZ': 0.0, '$DIMTOL': 0, '$DIMLIM': 0, '$DIMTIH': 1, '$DIMTOH': 1, '$DIMSE1': 0, '$DIMSE2': 0, '$DIMTAD': 0, '$DIMZIN': 0, '$DIMBLK': '', '$DIMASO': 1, '$DIMSHO': 1, '$DIMPOST': '', '$DIMAPOST': '', '$DIMALT': 0, '$DIMALTD': 2, '$DIMALTF': 25.4, '$DIMLFAC': 1.0, '$DIMTOFL': 0, '$DIMTVP': 0.0, '$DIMTIX': 0, '$DIMSOXD': 0, '$DIMSAH': 0, '$DIMBLK1': '', '$DIMBLK2': '', '$DIMSTYLE': 'Standard', '$DIMCLRD': 0, '$DIMCLRE': 0, '$DIMCLRT': 0, '$DIMTFAC': 1.0, '$DIMGAP': 0.09, '$DIMJUST': 0, '$DIMSD1': 0, '$DIMSD2': 0, '$DIMTOLJ': 1, '$DIMTZIN': 0, '$DIMALTZ': 0, '$DIMALTTZ': 0, '$DIMUPT': 0, '$DIMDEC': 4, '$DIMTDEC': 4, '$DIMALTU': 2, '$DIMALTTD': 2, '$DIMTXSTY': 'Standard', '$DIMAUNIT': 0, '$DIMADEC': 0, '$DIMALTRND': 0.0, '$DIMAZIN': 0, '$DIMDSEP': 46, '$DIMATFIT': 3, '$DIMFRAC': 0, '$DIMLDRBLK': '', '$DIMLUNIT': 2, '$DIMLWD': -2, '$DIMLWE': -2, '$DIMTMOVE': 0, '$LUNITS': 2, '$LUPREC': 4, '$SKETCHINC': 0.1, '$FILLETRAD': 0.5, '$AUNITS': 0, '$AUPREC': 0, '$MENU': '.', '$ELEVATION': 0.0, '$PELEVATION': 0.0, '$THICKNESS': 0.0, '$LIMCHECK': 0, '$CHAMFERA': 0.0, '$CHAMFERB': 0.0, '$CHAMFERC': 0.0, '$CHAMFERD': 0.0, '$SKPOLY': 0, '$TDCREATE': 2453737.5, '$TDUCREATE': 2453737.125, '$TDUPDATE': 2455188.75, '$TDUUPDATE': 2455188.375, '$TDINDWG': 1.16e-08, '$TDUSRTIMER': 1.16e-08, '$USRTIMER': 1, '$ANGBASE': 0.0, '$ANGDIR': 0, '$PDMODE': 0, '$PDSIZE': 0.0, '$PLINEWID': 0.0, '$SPLFRAME': 0, '$SPLINETYPE': 6, '$SPLINESEGS': 8, '$HANDSEED': '220', '$SURFTAB1': 6, '$SURFTAB2': 6, '$SURFTYPE': 6, '$SURFU': 6, '$SURFV': 6, '$UCSBASE': '', '$UCSNAME': '', '$UCSORG': (0.0, 0.0, 0.0), '$UCSXDIR': (1.0, 0.0, 0.0), '$UCSYDIR': (0.0, 1.0, 0.0), '$UCSORTHOREF': '', '$UCSORTHOVIEW': 0, '$UCSORGTOP': (0.0, 0.0, 0.0), '$UCSORGBOTTOM': (0.0, 0.0, 0.0), '$UCSORGLEFT': (0.0, 0.0, 0.0), '$UCSORGRIGHT': (0.0, 0.0, 0.0), '$UCSORGFRONT': (0.0, 0.0, 0.0), '$UCSORGBACK': (0.0, 0.0, 0.0), '$PUCSBASE': '', '$PUCSNAME': '', '$PUCSORG': (0.0, 0.0, 0.0), '$PUCSXDIR': (1.0, 0.0, 0.0), '$PUCSYDIR': (0.0, 1.0, 0.0), '$PUCSORTHOREF': '', '$PUCSORTHOVIEW': 0, '$PUCSORGTOP': (0.0, 0.0, 0.0), '$PUCSORGBOTTOM': (0.0, 0.0, 0.0), '$PUCSORGLEFT': (0.0, 0.0, 0.0), '$PUCSORGRIGHT': (0.0, 0.0, 0.0), '$PUCSORGFRONT': (0.0, 0.0, 0.0), '$PUCSORGBACK': (0.0, 0.0, 0.0), '$USERI1': 0, '$USERI2': 0, '$USERI3': 0, '$USERI4': 0, '$USERI5': 0, '$USERR1': 0.0, '$USERR2': 0.0, '$USERR3': 0.0, '$USERR4': 0.0, '$USERR5': 0.0, '$WORLDVIEW': 1, '$SHADEDGE': 3, '$SHADEDIF': 70, '$TILEMODE': 1, '$MAXACTVP': 64, '$PINSBASE': (0.0, 0.0, 0.0), '$PLIMCHECK': 0, '$PEXTMIN': (1e+20, 1e+20, 1e+20), '$PEXTMAX': (-1e+20, -1e+20, -1e+20), '$PLIMMIN': (0.0, 0.0), '$PLIMMAX': (12.0, 9.0), '$UNITMODE': 0, '$VISRETAIN': 1, '$PLINEGEN': 0, '$PSLTSCALE': 1, '$TREEDEPTH': 3020, '$CMLSTYLE': 'Standard', '$CMLJUST': 0, '$CMLSCALE': 1.0, '$PROXYGRAPHICS': 1, '$MEASUREMENT': 1, '$CELWEIGHT': -1, '$ENDCAPS': 0, '$JOINSTYLE': 0, '$LWDISPLAY': 0, '$INSUNITS': 4, '$HYPERLINKBASE': '', '$STYLESHEET': '', '$XEDIT': 1, '$CEPSNTYPE': 0, '$PSTYLEMODE': 1, '$FINGERPRINTGUID': '{3D9DC12F-B372-4948-9689-D346C0BFA940}', '$VERSIONGUID': '{FAEB1C32-E019-11D5-929B-00C0DF256EC4}', '$EXTNAMES': 1, '$PSVPSCALE': 0.0, '$OLESTARTUP': 0}

>>> dxf.header["$EXTMIN"]
(-250.0, -250.0, 0.0)

>>> dxf.header["$EXTMAX"]
(250.0, 250.0, 0.0)

I don't understand the meaning of various headers and I don't want to understand much, but it seems that the "\ $ EXTMIN" and "$ EXTMAX" keys contain the minimum and maximum coordinates of the drawn data.

Example of extracting straight lines, circles, and arcs

all_lines = [entity for entity in dxf.entities if entity.dxftype == 'LINE']
all_cirs = [entity for entity in dxf.entities if entity.dxftype == 'CIRCLE']
all_arcs = [entity for entity in dxf.entities if entity.dxftype == 'ARC']

According to the documentation, the entities property is "list like collection", which seems to be a little different from the list, but the details are unknown. If you do the above, you can retrieve the values like all_lines [0], all_lines [1], .... Further, more detailed graphic information can be extracted as follows.

all_lines[i].start[0] # (i+1)Start X coordinate of the second straight line
all_lines[i].start[1] # (i+1)Start Y coordinate of the second straight line
all_lines[i].end[0]   # (i+1)End X coordinate of the second straight line
all_lines[i].end[1]   # (i+1)End Y coordinate of the second straight line

all_arcs[i].start_angle # (i+1)Start angle of the second arc[deg]
all_arcs[i].end_angle   # (i+1)End angle of the second arc[deg]
all_arcs[i].radius      # (i+1)Radius of the second arc
all_arcs[i].center[0]   # (i+1)Center X coordinate of the second arc
all_arcs[i].center[1]   # (i+1)Center Y coordinate of the second arc

all_cirs[i].radius    # (i+1)Radius of the second circle
all_cirs[i].center[0] # (i+1)Center X coordinate of the second circle
all_cirs[i].center[1] # (i+1)Center Y coordinate of the second circle

It seems that I was able to retrieve the graphic information with dxfgrabber ... Let's draw and check it.

Try drawing to confirm that dxf is read correctly with dxfgrabber. I wonder if I should use Drawline () or DrawArc () with a nice library or GUI framework such as OpenCV, but for a little reason and idea, I decided to use python's ** turtle module **. I decided to use it. The document was surprisingly large, and I thought, "That? It's more annoying than I expected?", But it was easy to use. The movement of the circle and arc was a little difficult to understand,

turtle.circle(r,extent=deg)

The movement at that time is as shown in the figure below. When deg <0, draw an arc while retreating.

turtle_dir.png

Code to read DXF file and draw with turtle

dxf_turtle.py



# -*- coding: utf-8 -*-

import sys
import dxfgrabber as dg
import turtle as tut

from numpy import pi,cos,sin,arctan2,sqrt

fname=sys.argv[1]
dxf=dg.readfile(fname)
print(dxf.header)

def tut_dxfline(l):
    x0=l.start[0]
    y0=l.start[1]
    x1=l.end[0]
    y1=l.end[1]
    deg = arctan2(y1-y0,x1-x0) / pi*180
    dist = sqrt((x1-x0)**2+(y1-y0)**2)
    
    tut.penup()
    tut.setpos(x0,y0)
    tut.setheading(deg)
    tut.pendown()
    tut.forward(dist)
    
def tut_dxfarc(a):
    deg0=a.start_angle
    deg1=a.end_angle
    r=a.radius
    x0=a.center[0]
    y0=a.center[1]
    if deg0 > deg1:
        deg0 = deg0-360

    tut.penup()
    tut.setpos(x0+r*cos(deg0/180.*pi),y0+r*sin(deg0/180.*pi))
    tut.setheading(deg0+90)
    tut.pendown()
    tut.circle(r,extent=(deg1-deg0))

def tut_dxfcir(c):
    r=c.radius
    x0=c.center[0]
    y0=c.center[1]

    tut.penup()
    tut.setpos(x0+r,y0)
    tut.setheading(90)
    tut.pendown()
    tut.circle(r)

#tut.speed("fastest")
tut.speed("fast")

for entity in dxf.entities:
    if entity.dxftype == 'LINE':
        tut_dxfline(entity)
    elif entity.dxftype == 'CIRCLE':
        tut_dxfcir(entity)
    elif entity.dxftype == 'ARC':
        tut_dxfarc(entity)
    else:
        print(entity.dxftype)

print("Draw End")            
tut.mainloop()

Code above from the command line

 python dxf_turtle.py hogehoge.dxf

If you execute, the color, scale, line type, etc. are ignored, but the same figure as the original figure is drawn. I will post the result of the figure I drew as a trial, but it is sober. Looking only at the results, it's sober, but it's surprisingly fun to see how it moves and draws. Regarding the code, it seems that it is longer than drawing quickly with openCV etc. due to the set of the start position and orientation of the turtle and the extra code such as penup () / pendown (), but it is surprisingly fun to move. So it's okay (it's important, so don't do it twice).

hw-k.png

wheelhub.png

bug?

When I tried to draw various DXF data with turtle, some arcs were strange. For some reason, the position is reversed symmetrically with the Y axis. It seems that the positive and negative of the property center [0] are reversed and read. Is it a bug of dxfgrabber because it is a phenomenon that does not occur even if it is read by multiple CAD? I think. That said, I admire dxfgrabber and its authors. Thank you for making things that seem annoying easy.

Digression: Mysterious turtle pushing

I feel that there is a mysterious turtle guess in the neighborhood of computers and robots, such as the turtle module used here and the ROS turtlebot, but the origin is the LOGO language that is the basis of the python turtle module. Is it?

I also found this beachbot ([One of the sites covered in Japanese](http://makezine.jp/blog/2015/10/disney-turtle- robot-draws.html)). As far as I can see from the explanation of the site, it may happen that the turtle was adopted as a seaside creature regardless of ROS or LOGO ...

Recommended Posts

DXF and turtle
Rabbit and turtle algorithm
Turtle graphic
About _ and __