1966 lines
76 KiB
Python
1966 lines
76 KiB
Python
# -*- coding: utf8 -*-
|
|
|
|
#***************************************************************************
|
|
#* *
|
|
#* Copyright (c) 2009 Yorik van Havre <yorik@uncreated.net> *
|
|
#* *
|
|
#* This program is free software; you can redistribute it and/or modify *
|
|
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
#* as published by the Free Software Foundation; either version 2 of *
|
|
#* the License, or (at your option) any later version. *
|
|
#* for detail see the LICENCE text file. *
|
|
#* *
|
|
#* This program is distributed in the hope that it will be useful, *
|
|
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
#* GNU Library General Public License for more details. *
|
|
#* *
|
|
#* You should have received a copy of the GNU Library General Public *
|
|
#* License along with this program; if not, write to the Free Software *
|
|
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
#* USA *
|
|
#* *
|
|
#***************************************************************************
|
|
|
|
__title__="FreeCAD Draft Workbench - DXF importer/exporter"
|
|
__author__ = "Yorik van Havre <yorik@uncreated.net>"
|
|
__url__ = ["http://www.freecadweb.org"]
|
|
|
|
'''
|
|
This script uses a DXF-parsing library created by Stani,
|
|
Kitsu and Migius for Blender
|
|
|
|
imports:
|
|
line, polylines, lwpolylines, arcs, circles, texts,
|
|
mtexts, layers (as groups), colors
|
|
|
|
exports:
|
|
lines, polylines, lwpolylines, circles, arcs,
|
|
texts, colors,layers (from groups)
|
|
'''
|
|
|
|
TEXTSCALING = 1.35 # scaling factor between autocad font sizes and coin font sizes
|
|
CURRENTDXFLIB = 1.38 # the minimal version of the dxfLibrary needed to run
|
|
|
|
import sys, FreeCAD, os, Part, math, re, string, Mesh, Draft, DraftVecUtils, DraftGeomUtils
|
|
from Draft import _Dimension, _ViewProviderDimension
|
|
from FreeCAD import Vector
|
|
|
|
gui = FreeCAD.GuiUp
|
|
draftui = None
|
|
if gui:
|
|
import FreeCADGui
|
|
try:
|
|
draftui = FreeCADGui.draftToolBar
|
|
except AttributeError:
|
|
pass
|
|
|
|
def errorDXFLib(gui):
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
|
|
dxfAllowDownload = p.GetBool("dxfAllowDownload",False)
|
|
if dxfAllowDownload:
|
|
files = ['dxfColorMap.py','dxfImportObjects.py','dxfLibrary.py','dxfReader.py']
|
|
baseurl = 'https://raw.githubusercontent.com/yorikvanhavre/Draft-dxf-importer/'+str(CURRENTDXFLIB)+"/"
|
|
import ArchCommands
|
|
from FreeCAD import Base
|
|
progressbar = Base.ProgressIndicator()
|
|
progressbar.start("Downloading files...",4)
|
|
for f in files:
|
|
progressbar.next()
|
|
p = None
|
|
p = ArchCommands.download(baseurl+f,force=True)
|
|
if not p:
|
|
if gui:
|
|
from PySide import QtGui, QtCore
|
|
from DraftTools import translate
|
|
message = translate("Draft","""Download of dxf libraries failed.
|
|
Please download them manually from:
|
|
https://github.com/yorikvanhavre/Draft-dxf-importer
|
|
and place them in your macros folder.""")
|
|
QtGui.QMessageBox.information(None,"",message)
|
|
else:
|
|
FreeCAD.Console.PrintWarning("The DXF import/export libraries needed by FreeCAD to handle the DXF format are not installed.\n")
|
|
FreeCAD.Console.PrintWarning("Please check https://github.com/yorikvanhavre/Draft-dxf-importer\n")
|
|
break
|
|
progressbar.stop()
|
|
sys.path.append(FreeCAD.ConfigGet("UserAppData"))
|
|
else:
|
|
if gui:
|
|
from PySide import QtGui, QtCore
|
|
from DraftTools import translate
|
|
message = translate('draft',"""The DXF import/export libraries needed by FreeCAD to handle
|
|
the DXF format were not found on this system.
|
|
Please either enable FreeCAD to download these libraries:
|
|
1 - Load Draft workbench
|
|
2 - Menu Edit > Preferences > Import-Export > DXF > Enable downloads
|
|
Or download these libraries manually, as explained on
|
|
https://github.com/yorikvanhavre/Draft-dxf-importer
|
|
To enabled FreeCAD to download these libraries, answer Yes.""")
|
|
reply = QtGui.QMessageBox.question(None,"",message,
|
|
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
|
|
if reply == QtGui.QMessageBox.Yes:
|
|
p.SetBool("dxfAllowDownload",True)
|
|
errorDXFLib(gui)
|
|
if reply == QtGui.QMessageBox.No:
|
|
pass
|
|
else:
|
|
FreeCAD.Console.PrintWarning("The DXF import/export libraries needed by FreeCAD to handle the DXF format are not installed.\n")
|
|
FreeCAD.Console.PrintWarning("Please check https://github.com/yorikvanhavre/Draft-dxf-importer\n")
|
|
|
|
# check dxfLibrary version
|
|
try:
|
|
if FreeCAD.ConfigGet("UserAppData") not in sys.path:
|
|
sys.path.append(FreeCAD.ConfigGet("UserAppData"))
|
|
import dxfLibrary
|
|
import dxfColorMap
|
|
import dxfReader
|
|
except ImportError:
|
|
libsok = False
|
|
FreeCAD.Console.PrintWarning("DXF libraries not found. Trying to download...\n")
|
|
else:
|
|
if "v"+str(CURRENTDXFLIB) in dxfLibrary.__version__:
|
|
libsok = True
|
|
else:
|
|
FreeCAD.Console.PrintWarning("DXF libraries need to be updated. Trying to download...\n")
|
|
libsok = False
|
|
if not libsok:
|
|
errorDXFLib(gui)
|
|
try:
|
|
import dxfColorMap, dxfLibrary, dxfReader
|
|
except ImportError:
|
|
dxfReader = None
|
|
dxfLibrary = None
|
|
|
|
if open.__module__ == '__builtin__':
|
|
pythonopen = open # to distinguish python built-in open function from the one declared here
|
|
|
|
def prec():
|
|
"returns the current Draft precision level"
|
|
return Draft.getParam("precision",6)
|
|
|
|
def decodeName(name):
|
|
"decodes encoded strings"
|
|
try:
|
|
decodedName = (name.decode("utf8"))
|
|
except UnicodeDecodeError:
|
|
try:
|
|
decodedName = (name.decode("latin1"))
|
|
except UnicodeDecodeError:
|
|
print("dxf: error: couldn't determine character encoding")
|
|
decodedName = name
|
|
return decodedName
|
|
|
|
def deformat(text):
|
|
"removes weird formats in texts and wipes UTF characters"
|
|
# remove ACAD string formatation
|
|
#t = re.sub('{([^!}]([^}]|\n)*)}', '', text)
|
|
#print("input text: ",text)
|
|
t = text.strip("{}")
|
|
t = re.sub("\\\.*?;","",t)
|
|
# replace UTF codes by utf chars
|
|
sts = re.split("\\\\(U\+....)",t)
|
|
ns = u""
|
|
for ss in sts:
|
|
#print(ss, type(ss))
|
|
if ss.startswith("U+"):
|
|
ucode = "0x"+ss[2:]
|
|
ns += unichr(eval(ucode)) #Python3 - unichr doesn't exist anymore
|
|
else:
|
|
try:
|
|
ns += ss.decode("utf8")
|
|
except UnicodeError:
|
|
try:
|
|
ns += ss.decode("latin1")
|
|
except UnicodeError:
|
|
print("unable to decode text: ",text)
|
|
t = ns
|
|
# replace degrees, diameters chars
|
|
t = re.sub('%%d',u'°',t)
|
|
t = re.sub('%%c',u'Ø',t)
|
|
t = re.sub('%%D',u'°',t)
|
|
t = re.sub('%%C',u'Ø',t)
|
|
#print("output text: ",t)
|
|
return t
|
|
|
|
def locateLayer(wantedLayer,color=None):
|
|
"returns layer group and creates it if needed"
|
|
wantedLayerName = decodeName(wantedLayer)
|
|
for l in layers:
|
|
if wantedLayerName==l.Label:
|
|
return l
|
|
if dxfUseDraftVisGroups:
|
|
newLayer = Draft.makeVisGroup(name=wantedLayer)
|
|
else:
|
|
newLayer = doc.addObject("App::DocumentObjectGroup",wantedLayer)
|
|
newLayer.Label = wantedLayerName
|
|
layers.append(newLayer)
|
|
return newLayer
|
|
|
|
def getdimheight(style):
|
|
"returns the dimension text height from the given dimstyle"
|
|
for t in drawing.tables.data:
|
|
if t.name == 'dimstyle':
|
|
for a in t.data:
|
|
if hasattr(a,"type"):
|
|
if a.type == "dimstyle":
|
|
if rawValue(a,2) == style:
|
|
return rawValue(a,140)
|
|
return 1
|
|
|
|
def calcBulge(v1,bulge,v2):
|
|
'''
|
|
calculates intermediary vertex for curved segments.
|
|
algorithm from http://www.afralisp.net/lisp/Bulges1.htm
|
|
'''
|
|
chord = v2.sub(v1)
|
|
sagitta = (bulge * chord.Length)/2
|
|
perp = chord.cross(Vector(0,0,1))
|
|
startpoint = v1.add(chord.multiply(0.5))
|
|
if not DraftVecUtils.isNull(perp): perp.normalize()
|
|
endpoint = perp.multiply(sagitta)
|
|
return startpoint.add(endpoint)
|
|
|
|
def getGroup(ob):
|
|
"checks if the object is part of a group"
|
|
for i in FreeCAD.ActiveDocument.Objects:
|
|
if i.isDerivedFrom("App::DocumentObjectGroup"):
|
|
for j in i.Group:
|
|
if (j == ob):
|
|
return i.Label
|
|
return "0"
|
|
|
|
def getACI(ob,text=False):
|
|
"gets the ACI color closest to the objects color"
|
|
if not gui:
|
|
return 0
|
|
else:
|
|
if text:
|
|
col=ob.ViewObject.TextColor
|
|
else:
|
|
col=ob.ViewObject.LineColor
|
|
aci=[0,442]
|
|
for i in range (255,-1,-1):
|
|
ref=dxfColorMap.color_map[i]
|
|
dist=((ref[0]-col[0])**2 + (ref[1]-col[1])**2 + (ref[2]-col[2])**2)
|
|
if (dist <= aci[1]): aci=[i,dist]
|
|
return aci[0]
|
|
|
|
def rawValue(entity,code):
|
|
"returns the value of a DXF code in an entity section"
|
|
value = None
|
|
for pair in entity.data:
|
|
if pair[0] == code:
|
|
value = pair[1]
|
|
return value
|
|
|
|
def getMultiplePoints(entity):
|
|
"scans the given entity for multiple points (paths, leaders, etc)"
|
|
pts = []
|
|
for d in entity.data:
|
|
if d[0] == 10:
|
|
pts.append([d[1]])
|
|
elif d[0] in [20,30]:
|
|
pts[-1].append(d[1])
|
|
pts.reverse()
|
|
points = []
|
|
for p in pts:
|
|
if len(p) == 3:
|
|
points.append(Vector(p[0],p[1],p[2]))
|
|
else:
|
|
points.append(Vector(p[0],p[1],0))
|
|
return points
|
|
|
|
def isBrightBackground():
|
|
"checks if the current viewport background is bright"
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
|
|
if p.GetBool("Gradient"):
|
|
c1 = p.GetUnsigned("BackgroundColor2")
|
|
c2 = p.GetUnsigned("BackgroundColor3")
|
|
r1 = float((c1>>24)&0xFF)
|
|
g1 = float((c1>>16)&0xFF)
|
|
b1 = float((c1>>8)&0xFF)
|
|
r2 = float((c2>>24)&0xFF)
|
|
g2 = float((c2>>16)&0xFF)
|
|
b2 = float((c2>>8)&0xFF)
|
|
v1 = FreeCAD.Vector(r1,g1,b1)
|
|
v2 = FreeCAD.Vector(r2,g2,b2)
|
|
v = v2.sub(v1)
|
|
v.multiply(0.5)
|
|
cv = v1.add(v)
|
|
else:
|
|
c1 = p.GetUnsigned("BackgroundColor")
|
|
r1 = float((c1>>24)&0xFF)
|
|
g1 = float((c1>>16)&0xFF)
|
|
b1 = float((c1>>8)&0xFF)
|
|
cv = FreeCAD.Vector(r1,g1,b1)
|
|
value = cv.x*.3 + cv.y*.59 + cv.z*.11
|
|
if value < 128:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def getGroupColor(dxfobj,index=False):
|
|
"get color of bylayer stuff"
|
|
name = dxfobj.layer
|
|
for table in drawing.tables.get_type("table"):
|
|
if table.name == "layer":
|
|
for l in table.get_type("layer"):
|
|
if l.name == name:
|
|
if index:
|
|
return l.color
|
|
else:
|
|
if (l.color == 7) and dxfBrightBackground:
|
|
return [0.0,0.0,0.0]
|
|
else:
|
|
if isinstance(l.color,int):
|
|
if l.color > 0:
|
|
return dxfColorMap.color_map[l.color]
|
|
return [0.0,0.0,0.0]
|
|
|
|
def getColor():
|
|
if gui and draftui:
|
|
r = float(draftui.color.red()/255.0)
|
|
g = float(draftui.color.green()/255.0)
|
|
b = float(draftui.color.blue()/255.0)
|
|
return (r,g,b,0.0)
|
|
else:
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
|
|
c = p.GetUnsigned("DefaultShapeLineColor",0)
|
|
r = float(((c>>24)&0xFF)/255)
|
|
g = float(((c>>16)&0xFF)/255)
|
|
b = float(((c>>8)&0xFF)/255)
|
|
return (r,g,b,0.0)
|
|
|
|
def formatObject(obj,dxfobj=None):
|
|
"applies color and linetype to objects"
|
|
if dxfGetColors and dxfobj and hasattr(dxfobj,"color_index"):
|
|
if hasattr(obj.ViewObject,"TextColor"):
|
|
if dxfobj.color_index == 256:
|
|
cm = getGroupColor(dxfobj)[:3]
|
|
else:
|
|
cm = dxfColorMap.color_map[dxfobj.color_index]
|
|
obj.ViewObject.TextColor = (cm[0],cm[1],cm[2])
|
|
elif hasattr(obj.ViewObject,"LineColor"):
|
|
if dxfobj.color_index == 256:
|
|
cm = getGroupColor(dxfobj)
|
|
elif (dxfobj.color_index == 7) and dxfBrightBackground:
|
|
cm = [0.0,0.0,0.0]
|
|
else:
|
|
cm = dxfColorMap.color_map[dxfobj.color_index]
|
|
obj.ViewObject.LineColor = (cm[0],cm[1],cm[2],0.0)
|
|
else:
|
|
if hasattr(obj.ViewObject,"TextColor"):
|
|
obj.ViewObject.TextColor = dxfDefaultColor
|
|
elif hasattr(obj.ViewObject,"LineColor"):
|
|
obj.ViewObject.LineColor = dxfDefaultColor
|
|
|
|
def vec(pt):
|
|
"returns a rounded Vector from a dxf point"
|
|
return FreeCAD.Vector(round(pt[0],prec()),round(pt[1],prec()),round(pt[2],prec()))
|
|
|
|
def drawLine(line,forceShape=False):
|
|
"returns a Part shape from a dxf line"
|
|
if (len(line.points) > 1):
|
|
v1=vec(line.points[0])
|
|
v2=vec(line.points[1])
|
|
if not DraftVecUtils.equals(v1,v2):
|
|
try:
|
|
if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
|
|
return Draft.makeWire([v1,v2])
|
|
else:
|
|
return Part.Line(v1,v2).toShape()
|
|
except Part.OCCError:
|
|
warn(line)
|
|
return None
|
|
|
|
def drawPolyline(polyline,forceShape=False,num=None):
|
|
"returns a Part shape from a dxf polyline"
|
|
if (len(polyline.points) > 1):
|
|
edges = []
|
|
curves = False
|
|
verts = []
|
|
for p in range(len(polyline.points)-1):
|
|
p1 = polyline.points[p]
|
|
p2 = polyline.points[p+1]
|
|
v1 = vec(p1)
|
|
v2 = vec(p2)
|
|
verts.append(v1)
|
|
if not DraftVecUtils.equals(v1,v2):
|
|
if polyline.points[p].bulge:
|
|
curves = True
|
|
cv = calcBulge(v1,polyline.points[p].bulge,v2)
|
|
if DraftVecUtils.isColinear([v1,cv,v2]):
|
|
try: edges.append(Part.Line(v1,v2).toShape())
|
|
except Part.OCCError: warn(polyline,num)
|
|
else:
|
|
try: edges.append(Part.Arc(v1,cv,v2).toShape())
|
|
except Part.OCCError: warn(polyline,num)
|
|
else:
|
|
try: edges.append(Part.Line(v1,v2).toShape())
|
|
except Part.OCCError: warn(polyline,num)
|
|
verts.append(v2)
|
|
if polyline.closed:
|
|
p1 = polyline.points[len(polyline.points)-1]
|
|
p2 = polyline.points[0]
|
|
v1 = vec(p1)
|
|
v2 = vec(p2)
|
|
cv = calcBulge(v1,polyline.points[-1].bulge,v2)
|
|
if not DraftVecUtils.equals(v1,v2):
|
|
if DraftVecUtils.isColinear([v1,cv,v2]):
|
|
try:
|
|
edges.append(Part.Line(v1,v2).toShape())
|
|
except Part.OCCError:
|
|
warn(polyline,num)
|
|
else:
|
|
try:
|
|
edges.append(Part.Arc(v1,cv,v2).toShape())
|
|
except Part.OCCError:
|
|
warn(polyline,num)
|
|
if edges:
|
|
try:
|
|
width = rawValue(polyline,43)
|
|
if width and dxfRenderPolylineWidth:
|
|
w = Part.Wire(edges)
|
|
w1 = w.makeOffset(width/2)
|
|
if polyline.closed:
|
|
w2 = w.makeOffset(-width/2)
|
|
w1 = Part.Face(w1)
|
|
w2 = Part.Face(w2)
|
|
if w1.BoundBox.DiagonalLength > w2.BoundBox.DiagonalLength:
|
|
return w1.cut(w2)
|
|
else:
|
|
return w2.cut(w1)
|
|
else:
|
|
return Part.Face(w1)
|
|
elif (dxfCreateDraft or dxfCreateSketch) and (not curves) and (not forceShape):
|
|
ob = Draft.makeWire(verts)
|
|
ob.Closed = polyline.closed
|
|
return ob
|
|
else:
|
|
if polyline.closed and dxfFillMode:
|
|
w = Part.Wire(edges)
|
|
return(Part.Face(w))
|
|
else:
|
|
return Part.Wire(edges)
|
|
except Part.OCCError:
|
|
warn(polyline,num)
|
|
return None
|
|
|
|
def drawArc(arc,forceShape=False):
|
|
"returns a Part shape from a dxf arc"
|
|
v=vec(arc.loc)
|
|
firstangle=round(arc.start_angle,prec())
|
|
lastangle=round(arc.end_angle,prec())
|
|
circle=Part.Circle()
|
|
circle.Center=v
|
|
circle.Radius=round(arc.radius,prec())
|
|
try:
|
|
if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
|
|
pl = FreeCAD.Placement()
|
|
pl.move(v)
|
|
return Draft.makeCircle(arc.radius,pl,False,firstangle,lastangle)
|
|
else:
|
|
return circle.toShape(math.radians(firstangle),math.radians(lastangle))
|
|
except Part.OCCError:
|
|
warn(arc)
|
|
return None
|
|
|
|
def drawCircle(circle,forceShape=False):
|
|
"returns a Part shape from a dxf circle"
|
|
v = vec(circle.loc)
|
|
curve = Part.Circle()
|
|
curve.Radius = round(circle.radius,prec())
|
|
curve.Center = v
|
|
try:
|
|
if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
|
|
pl = FreeCAD.Placement()
|
|
pl.move(v)
|
|
return Draft.makeCircle(circle.radius,pl)
|
|
else:
|
|
return curve.toShape()
|
|
except Part.OCCError:
|
|
warn(circle)
|
|
return None
|
|
|
|
def drawEllipse(ellipse):
|
|
"returns a Part shape from a dxf arc"
|
|
try:
|
|
c = vec(ellipse.loc)
|
|
start = round(ellipse.start_angle,prec())
|
|
end = round(ellipse.end_angle,prec())
|
|
majv = vec(ellipse.major)
|
|
majr = majv.Length
|
|
minr = majr*ellipse.ratio
|
|
el = Part.Ellipse(vec((0,0,0)),majr,minr)
|
|
x = majv.normalize()
|
|
z = vec(ellipse.extrusion).normalize()
|
|
y = z.cross(x)
|
|
m = DraftVecUtils.getPlaneRotation(x,y,z)
|
|
pl = FreeCAD.Placement(m)
|
|
pl.move(c)
|
|
if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
|
|
if (start != 0.0) or ((end != 0.0) or (end != round(math.pi/2,prec()))):
|
|
shape = el.toShape(start,end)
|
|
shape.Placement = pl
|
|
return shape
|
|
else:
|
|
return Draft.makeEllipse(majr,minr,pl)
|
|
else:
|
|
shape = el.toShape(start,end)
|
|
shape.Placement = pl
|
|
return shape
|
|
except Part.OCCError:
|
|
warn(arc)
|
|
return None
|
|
|
|
def drawFace(face):
|
|
"returns a Part face from a list of points"
|
|
pl = []
|
|
for p in face.points:
|
|
pl.append(vec(p))
|
|
p1 = face.points[0]
|
|
pl.append(vec(p1))
|
|
try:
|
|
pol = Part.makePolygon(pl)
|
|
return Part.Face(pol)
|
|
except Part.OCCError:
|
|
warn(face)
|
|
return None
|
|
|
|
def drawMesh(mesh):
|
|
"returns a Mesh from a dxf mesh"
|
|
md = []
|
|
if mesh.flags == 16:
|
|
pts = mesh.points
|
|
udim = rawValue(mesh,71)
|
|
vdim = rawValue(mesh,72)
|
|
for u in range(udim-1):
|
|
for v in range(vdim-1):
|
|
b = u+v*udim
|
|
p1 = pts[b]
|
|
p2 = pts[b+1]
|
|
p3 = pts[b+udim]
|
|
p4 = pts[b+udim+1]
|
|
md.append([p1,p2,p4])
|
|
md.append([p1,p4,p3])
|
|
elif mesh.flags == 64:
|
|
pts = []
|
|
fcs = []
|
|
for p in mesh.points:
|
|
if p.flags == 192:
|
|
pts.append(p)
|
|
elif p.flags == 128:
|
|
fcs.append(p)
|
|
for f in fcs:
|
|
p1 = pts[rawValue(f,71)-1]
|
|
p2 = pts[rawValue(f,72)-1]
|
|
p3 = pts[rawValue(f,73)-1]
|
|
md.append([p1,p2,p3])
|
|
if rawValue(f,74) != None:
|
|
p4 = pts[rawValue(f,74)-1]
|
|
md.append([p1,p3,p4])
|
|
try:
|
|
return Mesh.Mesh(md)
|
|
except FreeCAD.Base.FreeCADError:
|
|
warn(mesh)
|
|
return None
|
|
|
|
def drawSolid(solid):
|
|
"returns a Part shape from a dxf solid"
|
|
p4 = None
|
|
p1x = rawValue(solid,10)
|
|
p1y = rawValue(solid,20)
|
|
p1z = rawValue(solid,30) or 0
|
|
p2x = rawValue(solid,11)
|
|
p2y = rawValue(solid,21)
|
|
p2z = rawValue(solid,31) or p1z
|
|
p3x = rawValue(solid,12)
|
|
p3y = rawValue(solid,22)
|
|
p3z = rawValue(solid,32) or p1z
|
|
p4x = rawValue(solid,13)
|
|
p4y = rawValue(solid,23)
|
|
p4z = rawValue(solid,33) or p1z
|
|
p1 = FreeCAD.Vector(p1x,p1y,p1z)
|
|
p2 = FreeCAD.Vector(p2x,p2y,p2z)
|
|
p3 = FreeCAD.Vector(p3x,p3y,p3z)
|
|
if p4x != None: p4 = FreeCAD.Vector(p4x,p4y,p4z)
|
|
if p4 and (p4 != p3) and (p4 != p2) and (p4 != p1):
|
|
try:
|
|
return Part.Face(Part.makePolygon([p1,p2,p4,p3,p1]))
|
|
except Part.OCCError:
|
|
warn(solid)
|
|
else:
|
|
try:
|
|
return Part.Face(Part.makePolygon([p1,p2,p3,p1]))
|
|
except Part.OCCError:
|
|
warn(solid)
|
|
return None
|
|
|
|
def drawSplineIterpolation(verts,closed=False,forceShape=False,\
|
|
alwaysDiscretize=False):
|
|
if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
|
|
if dxfDiscretizeCurves or alwaysDiscretize:
|
|
ob = Draft.makeWire(verts)
|
|
else:
|
|
ob = Draft.makeBSpline(verts)
|
|
ob.Closed = closed
|
|
return ob
|
|
else:
|
|
if dxfDiscretizeCurves or alwaysDiscretize:
|
|
sh = Part.makePolygon(verts+[verts[0]])
|
|
else:
|
|
sp = Part.BSplineCurve()
|
|
# print(knots)
|
|
sp.interpolate(verts)
|
|
sh = Part.Wire(sp.toShape())
|
|
if closed and dxfFillMode:
|
|
return Part.Face(sh)
|
|
else:
|
|
return sh
|
|
|
|
def drawSplineOld(spline,forceShape=False):
|
|
"returns a Part Shape from a dxf spline"
|
|
flag = rawValue(spline,70)
|
|
if flag == 1:
|
|
closed = True
|
|
else:
|
|
closed = False
|
|
verts = []
|
|
knots = []
|
|
for dline in spline.data:
|
|
if dline[0] == 10:
|
|
cp = [dline[1]]
|
|
elif dline[0] == 20:
|
|
cp.append(dline[1])
|
|
elif dline[0] == 30:
|
|
cp.append(dline[1])
|
|
pt = Vector(cp[0],cp[1],cp[2])
|
|
if verts:
|
|
if pt != verts[-1]:
|
|
verts.append(pt)
|
|
else:
|
|
verts.append(pt)
|
|
elif dline[0] == 40:
|
|
knots.append(dline[1])
|
|
try:
|
|
return drawSplineIterpolation(verts,closed,forceShape)
|
|
except Part.OCCError:
|
|
warn(spline)
|
|
return None
|
|
|
|
def drawSpline(spline,forceShape=False):
|
|
"""returns a Part Shape from a dxf spline
|
|
as there is currently no Draft premitive to handle splines the result is a
|
|
non-parametric curve"""
|
|
flags = rawValue(spline,70)
|
|
closed = (flags & 1) != 0
|
|
periodic = (flags & 2) != 0 and False # workaround
|
|
rational = (flags & 4) != 0
|
|
planar = (flags & 8) != 0
|
|
linear = (flags & 16) != 0
|
|
degree = rawValue(spline,71)
|
|
nbknots = rawValue(spline,72) or 0
|
|
nbcontrolp = rawValue(spline,73) or 0
|
|
nbfitp = rawValue(spline,74) or 0
|
|
knots = []
|
|
weights = []
|
|
controlpoints = []
|
|
fitpoints = []
|
|
# parse the knots and points
|
|
dataremain = spline.data[:]
|
|
while len(dataremain) >0:
|
|
groupnumber = dataremain[0][0]
|
|
if groupnumber == 40: #knot
|
|
knots.append(dataremain[0][1])
|
|
dataremain = dataremain[1:]
|
|
elif groupnumber == 41: #weight
|
|
weights.append(dataremain[0][1])
|
|
dataremain = dataremain[1:]
|
|
elif groupnumber in (10,11): # control or fit point
|
|
x = dataremain[0][1]
|
|
if dataremain[1][0] in (20,21):
|
|
y=dataremain[1][1]
|
|
if dataremain[2][0] in (30,31):
|
|
z=dataremain[2][1]
|
|
dataremain = dataremain[3:]
|
|
else:
|
|
z=0.0
|
|
dataremain = dataremain[2:]
|
|
else:
|
|
y=0.0
|
|
dataremain = dataremain[1:]
|
|
vec = FreeCAD.Vector(x,y,z)
|
|
if groupnumber == 10:
|
|
controlpoints.append(vec)
|
|
elif groupnumber == 11:
|
|
fitpoints.append(vec)
|
|
else:
|
|
dataremain = dataremain[1:]
|
|
#print groupnumber #debug
|
|
|
|
if nbknots != len(knots):
|
|
raise ValueError('Wrong number of knots')
|
|
if nbcontrolp != len(controlpoints):
|
|
raise ValueError('Wrong number of control points')
|
|
if nbfitp != len(fitpoints):
|
|
raise ValueError('Wrong number of fit points')
|
|
if rational == all((w == 1.0 or w is None) for w in weights):
|
|
raise ValueError('inconsistant rational flag')
|
|
if len(weights) == 0:
|
|
weights = None
|
|
elif len(weights) != len(controlpoints):
|
|
raise ValueError('Wrong number of weights')
|
|
|
|
# build knotvector and multvector
|
|
# this means to remove duplicate knots
|
|
multvector=[]
|
|
knotvector=[]
|
|
mult=0
|
|
previousknot=None
|
|
for knotvalue in knots:
|
|
if knotvalue == previousknot:
|
|
mult += 1
|
|
else:
|
|
if mult > 0:
|
|
multvector.append(mult)
|
|
mult = 1
|
|
previousknot = knotvalue
|
|
knotvector.append(knotvalue)
|
|
multvector.append(mult)
|
|
# check if the multiplicities are valid
|
|
innermults = multvector[:] if periodic else multvector[1:-1]
|
|
if any(m>degree for m in innermults): #invalid
|
|
if all(m == degree+1 for m in multvector):
|
|
if not forceShape and weights is None:
|
|
points=controlpoints[:]
|
|
del points[degree+1::degree+1]
|
|
return Draft.makeBezCurve(points,Degree=degree)
|
|
else:
|
|
poles=controlpoints[:]
|
|
edges=[]
|
|
while len(poles) >= degree+1:
|
|
#bezier segments
|
|
bzseg=Part.BezierCurve()
|
|
bzseg.increase(degree)
|
|
bzseg.setPoles(poles[0:degree+1])
|
|
poles=poles[degree+1:]
|
|
if weights is not None:
|
|
bzseg.setWeights(weights[0:degree+1])
|
|
weights=weights[degree+1:]
|
|
edges.append(bzseg.toShape())
|
|
return Part.Wire(edges)
|
|
else:
|
|
warn('polygon fallback on %s' %spline)
|
|
return drawSplineIterpolation(controlpoints,closed=closed,\
|
|
forceShape=forceShape,alwaysDiscretize=True)
|
|
try:
|
|
bspline=Part.BSplineCurve()
|
|
bspline.buildFromPolesMultsKnots(poles=controlpoints,mults=multvector,\
|
|
knots=knotvector,degree=degree,periodic=periodic,\
|
|
weights=weights)
|
|
return bspline.toShape()
|
|
except Part.OCCError:
|
|
warn(spline)
|
|
return None
|
|
|
|
def drawBlock(blockref,num=None,createObject=False):
|
|
"returns a shape from a dxf block reference"
|
|
if not dxfStarBlocks:
|
|
if blockref.name[0] == '*':
|
|
return None
|
|
if len(blockref.entities.data) == 0:
|
|
print("skipping empty block ",blockref.name)
|
|
return None
|
|
#print("creating block ", blockref.name, " containing ", len(blockref.entities.data), " entities")
|
|
shapes = []
|
|
for line in blockref.entities.get_type('line'):
|
|
s = drawLine(line,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for polyline in blockref.entities.get_type('polyline'):
|
|
s = drawPolyline(polyline,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for polyline in blockref.entities.get_type('lwpolyline'):
|
|
s = drawPolyline(polyline,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for arc in blockref.entities.get_type('arc'):
|
|
s = drawArc(arc,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for circle in blockref.entities.get_type('circle'):
|
|
s = drawCircle(circle,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for insert in blockref.entities.get_type('insert'):
|
|
#print("insert ",insert," in block ",insert.block[0])
|
|
if dxfStarBlocks or insert.block[0] != '*':
|
|
s = drawInsert(insert)
|
|
if s: shapes.append(s)
|
|
for solid in blockref.entities.get_type('solid'):
|
|
s = drawSolid(solid)
|
|
if s: shapes.append(s)
|
|
for spline in blockref.entities.get_type('spline'):
|
|
s = drawSpline(spline,forceShape=True)
|
|
if s: shapes.append(s)
|
|
for text in blockref.entities.get_type('text'):
|
|
if dxfImportTexts:
|
|
if dxfImportLayouts or (not rawValue(text,67)):
|
|
addText(text)
|
|
for text in blockref.entities.get_type('mtext'):
|
|
if dxfImportTexts:
|
|
if dxfImportLayouts or (not rawValue(text,67)):
|
|
print("adding block text",text.value, " from ",blockref)
|
|
addText(text)
|
|
try: shape = Part.makeCompound(shapes)
|
|
except Part.OCCError: warn(blockref)
|
|
if shape:
|
|
blockshapes[blockref.name]=shape
|
|
if createObject:
|
|
newob=doc.addObject("Part::Feature",blockref.name)
|
|
newob.Shape = shape
|
|
blockobjects[blockref.name] = newob
|
|
return newob
|
|
return shape
|
|
return None
|
|
|
|
def drawInsert(insert,num=None,clone=False):
|
|
if dxfImportTexts:
|
|
attrs = attribs(insert)
|
|
for a in attrs:
|
|
addText(a,attrib=True)
|
|
if clone:
|
|
if insert.block in blockobjects:
|
|
newob = Draft.clone(blockobjects[insert.block])
|
|
tsf = FreeCAD.Matrix()
|
|
rot = math.radians(insert.rotation)
|
|
pos = vec(insert.loc)
|
|
tsf.move(pos)
|
|
tsf.rotateZ(rot)
|
|
sc = insert.scale
|
|
sc = FreeCAD.Vector(sc[0],sc[1],0)
|
|
newob.Placement = FreeCAD.Placement(tsf)
|
|
newob.Scale = sc
|
|
return newob
|
|
else:
|
|
shape = None
|
|
else:
|
|
if insert in blockshapes:
|
|
shape = blockshapes[insert.block].copy()
|
|
else:
|
|
shape = None
|
|
for b in drawing.blocks.data:
|
|
if b.name == insert.block:
|
|
shape = drawBlock(b,num)
|
|
if shape:
|
|
pos = vec(insert.loc)
|
|
rot = math.radians(insert.rotation)
|
|
scale = insert.scale
|
|
tsf = FreeCAD.Matrix()
|
|
tsf.scale(scale[0],scale[1],0) # for some reason z must be 0 to work
|
|
tsf.rotateZ(rot)
|
|
shape = shape.transformGeometry(tsf)
|
|
shape.translate(pos)
|
|
return shape
|
|
return None
|
|
|
|
def drawLayerBlock(objlist):
|
|
"draws a Draft block with the given shapes or objects"
|
|
obj = None
|
|
if (dxfCreateDraft or dxfCreateSketch):
|
|
try:
|
|
obj = Draft.makeBlock(objlist)
|
|
except Part.OCCError:
|
|
pass
|
|
else:
|
|
try:
|
|
obj = Part.makeCompound(objlist)
|
|
except Part.OCCError:
|
|
pass
|
|
return obj
|
|
|
|
def attribs(insert):
|
|
"checks if an insert has attributes, and returns the values if yes"
|
|
atts = []
|
|
if rawValue(insert,66) != 1: return []
|
|
index = None
|
|
for i in range(len(drawing.entities.data)):
|
|
if drawing.entities.data[i] == insert:
|
|
index = i
|
|
break
|
|
if index == None: return []
|
|
j = index+1
|
|
while True:
|
|
ent = drawing.entities.data[j]
|
|
if str(ent) == 'seqend':
|
|
return atts
|
|
elif str(ent) == 'attrib':
|
|
atts.append(ent)
|
|
j += 1
|
|
|
|
def addObject(shape,name="Shape",layer=None):
|
|
"adds a new object to the document with passed arguments"
|
|
if isinstance(shape,Part.Shape):
|
|
newob=doc.addObject("Part::Feature",name)
|
|
newob.Shape = shape
|
|
else:
|
|
newob = shape
|
|
if layer:
|
|
lay=locateLayer(layer)
|
|
lay.addObject(newob)
|
|
formatObject(newob)
|
|
return newob
|
|
|
|
def addText(text,attrib=False):
|
|
"adds a new text to the document"
|
|
if attrib:
|
|
lay = locateLayer(rawValue(text,8))
|
|
val = rawValue(text,1)
|
|
pos = FreeCAD.Vector(rawValue(text,10),rawValue(text,20),rawValue(text,30))
|
|
hgt = rawValue(text,40)
|
|
else:
|
|
lay = locateLayer(text.layer)
|
|
val = text.value
|
|
pos = FreeCAD.Vector(text.loc[0],text.loc[1],text.loc[2])
|
|
hgt = text.height
|
|
if val:
|
|
if attrib:
|
|
newob = doc.addObject("App::Annotation","Attribute")
|
|
else:
|
|
newob = doc.addObject("App::Annotation","Text")
|
|
lay.addObject(newob)
|
|
val = deformat(val)
|
|
# the following stores text as Latin1 in annotations, which
|
|
# displays ok in coin texts, but causes errors later on.
|
|
# better store as utf8 always.
|
|
#try:
|
|
# val = val.decode("utf8").encode("Latin1")
|
|
#except:
|
|
# try:
|
|
# val = val.encode("latin1")
|
|
# except:
|
|
# pass
|
|
rx = rawValue(text,11)
|
|
ry = rawValue(text,21)
|
|
rz = rawValue(text,31)
|
|
xv = Vector(1,0,0)
|
|
ax = Vector(0,0,1)
|
|
if rx or ry or rz:
|
|
xv = Vector(rx,ry,rz)
|
|
if not DraftVecUtils.isNull(xv):
|
|
ax = (xv.cross(Vector(1,0,0))).negative()
|
|
if DraftVecUtils.isNull(ax):
|
|
ax = Vector(0,0,1)
|
|
ang = -math.degrees(DraftVecUtils.angle(xv,Vector(1,0,0),ax))
|
|
Draft.rotate(newob,ang,axis=ax)
|
|
if ax == Vector(0,0,-1): ax = Vector(0,0,1)
|
|
elif hasattr(text,"rotation"):
|
|
if text.rotation:
|
|
Draft.rotate(newob,text.rotation)
|
|
if attrib:
|
|
attrot = rawValue(text,50)
|
|
if attrot:
|
|
Draft.rotate(newob,attrot)
|
|
newob.LabelText = val.split("\n")
|
|
if gui and draftui and dxfUseStandardSize:
|
|
fsize = draftui.fontsize
|
|
else:
|
|
fsize = float(hgt)*TEXTSCALING
|
|
if hasattr(text,"alignment"):
|
|
yv = ax.cross(xv)
|
|
if text.alignment in [1,2,3]:
|
|
sup = DraftVecUtils.scaleTo(yv,fsize/TEXTSCALING).negative()
|
|
#print(ax,sup)
|
|
pos = pos.add(sup)
|
|
elif text.alignment in [4,5,6]:
|
|
sup = DraftVecUtils.scaleTo(yv,fsize/(2*TEXTSCALING)).negative()
|
|
pos = pos.add(sup)
|
|
newob.Position = pos
|
|
if gui:
|
|
newob.ViewObject.FontSize = fsize
|
|
if hasattr(text,"alignment"):
|
|
if text.alignment in [2,5,8]:
|
|
newob.ViewObject.Justification = "Center"
|
|
elif text.alignment in [3,6,9]:
|
|
newob.ViewObject.Justification = "Right"
|
|
newob.ViewObject.DisplayMode = "World"
|
|
formatObject(newob,text)
|
|
|
|
def addToBlock(obj,layer):
|
|
"adds given shape to the layer dict"
|
|
if layer in layerBlocks:
|
|
layerBlocks[layer].append(obj)
|
|
else:
|
|
layerBlocks[layer] = [obj]
|
|
|
|
def processdxf(document,filename,getShapes=False):
|
|
"this does the translation of the dxf contents into FreeCAD Part objects"
|
|
global drawing # for debugging - so drawing is still accessible to python after the script ran
|
|
FreeCAD.Console.PrintMessage("opening "+filename+"...\n")
|
|
drawing = dxfReader.readDXF(filename)
|
|
global layers
|
|
layers = []
|
|
global doc
|
|
doc = document
|
|
global blockshapes
|
|
blockshapes = {}
|
|
global blockobjects
|
|
blockobjects = {}
|
|
global badobjects
|
|
badobjects = []
|
|
global layerBlocks
|
|
layerBlocks = {}
|
|
sketch = None
|
|
shapes = []
|
|
|
|
# drawing lines
|
|
|
|
lines = drawing.entities.get_type("line")
|
|
if lines: FreeCAD.Console.PrintMessage("drawing "+str(len(lines))+" lines...\n")
|
|
for line in lines:
|
|
if dxfImportLayouts or (not rawValue(line,67)):
|
|
shape = drawLine(line)
|
|
if shape:
|
|
if dxfCreateSketch:
|
|
if dxfMakeBlocks or dxfJoin:
|
|
if sketch:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
sketch = shape
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
elif dxfJoin or getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
elif dxfMakeBlocks:
|
|
addToBlock(shape,line.layer)
|
|
else:
|
|
newob = addObject(shape,"Line",line.layer)
|
|
if gui: formatObject(newob,line)
|
|
|
|
# drawing polylines
|
|
|
|
pls = drawing.entities.get_type("lwpolyline")
|
|
pls.extend(drawing.entities.get_type("polyline"))
|
|
polylines = []
|
|
meshes = []
|
|
for p in pls:
|
|
if hasattr(p,"flags"):
|
|
if p.flags in [16,64]:
|
|
meshes.append(p)
|
|
else:
|
|
polylines.append(p)
|
|
else:
|
|
polylines.append(p)
|
|
if polylines:
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(polylines))+" polylines...\n")
|
|
num = 0
|
|
for polyline in polylines:
|
|
if dxfImportLayouts or (not rawValue(polyline,67)):
|
|
shape = drawPolyline(polyline,num)
|
|
if shape:
|
|
if dxfCreateSketch:
|
|
if isinstance(shape,Part.Shape):
|
|
t = FreeCAD.ActiveDocument.addObject("Part::Feature","Shape")
|
|
t.Shape = shape
|
|
shape = t
|
|
if dxfMakeBlocks or dxfJoin:
|
|
if sketch:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
sketch = shape
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
elif dxfJoin or getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
elif dxfMakeBlocks:
|
|
addToBlock(shape,polyline.layer)
|
|
else:
|
|
newob = addObject(shape,"Polyline",polyline.layer)
|
|
if gui: formatObject(newob,polyline)
|
|
num += 1
|
|
|
|
# drawing arcs
|
|
|
|
arcs = drawing.entities.get_type("arc")
|
|
if arcs: FreeCAD.Console.PrintMessage("drawing "+str(len(arcs))+" arcs...\n")
|
|
for arc in arcs:
|
|
if dxfImportLayouts or (not rawValue(arc,67)):
|
|
shape = drawArc(arc)
|
|
if shape:
|
|
if dxfCreateSketch:
|
|
if dxfMakeBlocks or dxfJoin:
|
|
if sketch:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
sketch = shape
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
elif dxfJoin or getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
elif dxfMakeBlocks:
|
|
addToBlock(shape,arc.layer)
|
|
else:
|
|
newob = addObject(shape,"Arc",arc.layer)
|
|
if gui: formatObject(newob,arc)
|
|
|
|
# joining lines, polylines and arcs if needed
|
|
|
|
if dxfJoin and shapes:
|
|
FreeCAD.Console.PrintMessage("Joining geometry...\n")
|
|
edges = []
|
|
for s in shapes:
|
|
edges.extend(s.Edges)
|
|
if len(edges) > (100):
|
|
FreeCAD.Console.PrintMessage(str(len(edges))+" edges to join\n")
|
|
from PySide import QtGui
|
|
d = QtGui.QMessageBox()
|
|
d.setText("Warning: High number of entities to join (>100)")
|
|
d.setInformativeText("This might take a long time or even freeze your computer. Are you sure? You can also disable the \"join geometry\" setting in DXF import preferences")
|
|
d.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
|
|
d.setDefaultButton(QtGui.QMessageBox.Cancel)
|
|
res = d.exec_()
|
|
if res == QtGui.QMessageBox.Cancel:
|
|
FreeCAD.Console.PrintMessage("Aborted\n")
|
|
return
|
|
shapes = DraftGeomUtils.findWires(edges)
|
|
for s in shapes:
|
|
newob = addObject(s)
|
|
|
|
# drawing circles
|
|
|
|
circles = drawing.entities.get_type("circle")
|
|
if circles: FreeCAD.Console.PrintMessage("drawing "+str(len(circles))+" circles...\n")
|
|
for circle in circles:
|
|
if dxfImportLayouts or (not rawValue(circle,67)):
|
|
shape = drawCircle(circle)
|
|
if shape:
|
|
if dxfCreateSketch:
|
|
if dxfMakeBlocks or dxfJoin:
|
|
if sketch:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
sketch = shape
|
|
else:
|
|
shape = Draft.makeSketch(shape,autoconstraints=True)
|
|
elif dxfMakeBlocks:
|
|
addToBlock(shape,circle.layer)
|
|
elif getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
else:
|
|
newob = addObject(shape,"Circle",circle.layer)
|
|
if gui: formatObject(newob,circle)
|
|
|
|
# drawing solids
|
|
|
|
solids = drawing.entities.get_type("solid")
|
|
if solids: FreeCAD.Console.PrintMessage("drawing "+str(len(circles))+" solids...\n")
|
|
for solid in solids:
|
|
lay = rawValue(solid,8)
|
|
if dxfImportLayouts or (not rawValue(solid,67)):
|
|
shape = drawSolid(solid)
|
|
if shape:
|
|
if dxfMakeBlocks:
|
|
addToBlock(shape,lay)
|
|
elif getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
else:
|
|
newob = addObject(shape,"Solid",lay)
|
|
if gui: formatObject(newob,solid)
|
|
|
|
# drawing splines
|
|
|
|
splines = drawing.entities.get_type("spline")
|
|
if splines: FreeCAD.Console.PrintMessage("drawing "+str(len(splines))+" splines...\n")
|
|
for spline in splines:
|
|
lay = rawValue(spline,8)
|
|
if dxfImportLayouts or (not rawValue(spline,67)):
|
|
shape = drawSpline(spline)
|
|
if shape:
|
|
if dxfMakeBlocks:
|
|
addToBlock(shape,lay)
|
|
elif getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
else:
|
|
newob = addObject(shape,"Spline",lay)
|
|
if gui: formatObject(newob,spline)
|
|
|
|
# drawing ellipses
|
|
|
|
ellipses = drawing.entities.get_type("ellipse")
|
|
if ellipses: FreeCAD.Console.PrintMessage("drawing "+str(len(ellipses))+" ellipses...\n")
|
|
for ellipse in ellipses:
|
|
lay = rawValue(ellipse,8)
|
|
if dxfImportLayouts or (not rawValue(ellipse,67)):
|
|
shape = drawEllipse(ellipse)
|
|
if shape:
|
|
if dxfMakeBlocks:
|
|
addToBlock(shape,lay)
|
|
elif getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
else:
|
|
newob = addObject(shape,"Ellipse",lay)
|
|
if gui: formatObject(newob,ellipse)
|
|
|
|
# drawing texts
|
|
|
|
if dxfImportTexts:
|
|
texts = drawing.entities.get_type("mtext")
|
|
texts.extend(drawing.entities.get_type("text"))
|
|
if texts:
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(texts))+" texts...\n")
|
|
for text in texts:
|
|
if dxfImportLayouts or (not rawValue(text,67)):
|
|
addText(text)
|
|
|
|
else: FreeCAD.Console.PrintMessage("skipping texts...\n")
|
|
|
|
# drawing 3D objects
|
|
|
|
faces3d = drawing.entities.get_type("3dface")
|
|
if faces3d: FreeCAD.Console.PrintMessage("drawing "+str(len(faces3d))+" 3dfaces...\n")
|
|
for face3d in faces3d:
|
|
shape = drawFace(face3d)
|
|
if shape:
|
|
if getShapes:
|
|
if isinstance(shape,Part.Shape):
|
|
shapes.append(shape)
|
|
else:
|
|
shapes.append(shape.Shape)
|
|
else:
|
|
newob = addObject(shape,"Face",face3d.layer)
|
|
if gui: formatObject(newob,face3d)
|
|
if meshes: FreeCAD.Console.PrintMessage("drawing "+str(len(meshes))+" 3dmeshes...\n")
|
|
for mesh in meshes:
|
|
me = drawMesh(mesh)
|
|
if me:
|
|
newob = doc.addObject("Mesh::Feature","Mesh")
|
|
lay = locateLayer(rawValue(mesh,8))
|
|
lay.addObject(newob)
|
|
newob.Mesh = me
|
|
if gui: formatObject(newob,mesh)
|
|
|
|
# end of shape-based objects, return if we are just getting shapes
|
|
|
|
if getShapes and shapes:
|
|
return(shapes)
|
|
|
|
# drawing dims
|
|
|
|
if dxfImportTexts:
|
|
dims = drawing.entities.get_type("dimension")
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(dims))+" dimensions...\n")
|
|
for dim in dims:
|
|
if dxfImportLayouts or (not rawValue(dim,67)):
|
|
try:
|
|
layer = rawValue(dim,8)
|
|
x1 = float(rawValue(dim,10))
|
|
y1 = float(rawValue(dim,20))
|
|
z1 = float(rawValue(dim,30))
|
|
x2 = float(rawValue(dim,13))
|
|
y2 = float(rawValue(dim,23))
|
|
z2 = float(rawValue(dim,33))
|
|
x3 = float(rawValue(dim,14))
|
|
y3 = float(rawValue(dim,24))
|
|
z3 = float(rawValue(dim,34))
|
|
d = rawValue(dim,70)
|
|
if d: align = int(d)
|
|
else: align = 0
|
|
d = rawValue(dim,50)
|
|
if d: angle = float(d)
|
|
else: angle = 0
|
|
except ValueError:
|
|
warn(dim)
|
|
else:
|
|
lay=locateLayer(layer)
|
|
pt = FreeCAD.Vector(x1,y1,z1)
|
|
p1 = FreeCAD.Vector(x2,y2,z2)
|
|
p2 = FreeCAD.Vector(x3,y3,z3)
|
|
if align >= 128:
|
|
align -= 128
|
|
elif align >= 64:
|
|
align -= 64
|
|
elif align >= 32:
|
|
align -= 32
|
|
if align == 0:
|
|
if angle in [0,180]:
|
|
p2 = FreeCAD.Vector(x3,y2,z2)
|
|
elif angle in [90,270]:
|
|
p2 = FreeCAD.Vector(x2,y3,z2)
|
|
newob = doc.addObject("App::FeaturePython","Dimension")
|
|
lay.addObject(newob)
|
|
_Dimension(newob)
|
|
_ViewProviderDimension(newob.ViewObject)
|
|
newob.Start = p1
|
|
newob.End = p2
|
|
newob.Dimline = pt
|
|
if gui:
|
|
dim.layer = layer
|
|
dim.color_index = 256
|
|
formatObject (newob,dim)
|
|
if dxfUseStandardSize and draftui:
|
|
newob.ViewObject.FontSize = draftui.fontsize
|
|
else:
|
|
st = rawValue(dim,3)
|
|
size = getdimheight(st) or 1
|
|
newob.ViewObject.FontSize = float(size)*TEXTSCALING
|
|
else:
|
|
FreeCAD.Console.PrintMessage("skipping dimensions...\n")
|
|
|
|
# drawing points
|
|
|
|
if dxfImportPoints:
|
|
points = drawing.entities.get_type("point")
|
|
if points: FreeCAD.Console.PrintMessage("drawing "+str(len(points))+" points...\n")
|
|
for point in points:
|
|
x = rawValue(point,10)
|
|
y = rawValue(point,20)
|
|
z = rawValue(point,30)
|
|
lay = rawValue(point,8)
|
|
if dxfImportLayouts or (not rawValue(point,67)):
|
|
if dxfMakeBlocks:
|
|
shape = Part.Vertex(x,y,z)
|
|
addToBlock(shape,lay)
|
|
else:
|
|
newob = Draft.makePoint(x,y,z)
|
|
lay = locateLayer(lay)
|
|
lay.addObject(newob)
|
|
if gui:
|
|
formatObject(newob,point)
|
|
else:
|
|
FreeCAD.Console.PrintMessage("skipping points...\n")
|
|
|
|
# drawing leaders
|
|
|
|
if dxfImportTexts:
|
|
leaders = drawing.entities.get_type("leader")
|
|
if leaders:
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(leaders))+" leaders...\n")
|
|
for leader in leaders:
|
|
if dxfImportLayouts or (not rawValue(leader,67)):
|
|
points = getMultiplePoints(leader)
|
|
newob = Draft.makeWire(points)
|
|
lay = locateLayer(rawValue(leader,8))
|
|
lay.addObject(newob)
|
|
if gui:
|
|
newob.ViewObject.EndArrow = True
|
|
formatObject(newob,leader)
|
|
else:
|
|
FreeCAD.Console.PrintMessage("skipping leaders...\n")
|
|
|
|
# drawing hatches
|
|
|
|
if dxfImportHatches:
|
|
hatches = drawing.entities.get_type("hatch")
|
|
if hatches:
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(hatches))+" hatches...\n")
|
|
for hatch in hatches:
|
|
if dxfImportLayouts or (not rawValue(hatch,67)):
|
|
points = getMultiplePoints(hatch)
|
|
if len(points) > 1:
|
|
lay = rawValue(hatch,8)
|
|
points = points[:-1]
|
|
newob = None
|
|
if dxfCreatePart or dxfMakeBlocks:
|
|
points.append(points[0])
|
|
s = Part.makePolygon(points)
|
|
if dxfMakeBlocks:
|
|
addToBlock(s,lay)
|
|
else:
|
|
newob = addObject(s,"Hatch",lay)
|
|
if gui:
|
|
formatObject(newob,hatch)
|
|
else:
|
|
newob = Draft.makeWire(points)
|
|
locateLayer(lay).addObject(newob)
|
|
if gui:
|
|
formatObject(newob,hatch)
|
|
else:
|
|
FreeCAD.Console.PrintMessage("skipping hatches...\n")
|
|
|
|
# drawing blocks
|
|
|
|
inserts = drawing.entities.get_type("insert")
|
|
if not dxfStarBlocks:
|
|
FreeCAD.Console.PrintMessage("skipping *blocks...\n")
|
|
newinserts = []
|
|
for i in inserts:
|
|
if dxfImportLayouts or (not rawValue(i,67)):
|
|
if i.block[0] != '*':
|
|
newinserts.append(i)
|
|
inserts = newinserts
|
|
if inserts:
|
|
FreeCAD.Console.PrintMessage("drawing "+str(len(inserts))+" blocks...\n")
|
|
blockrefs = drawing.blocks.data
|
|
for ref in blockrefs:
|
|
if dxfCreateDraft or dxfCreateSketch:
|
|
drawBlock(ref,createObject=True)
|
|
else:
|
|
drawBlock(ref,createObject=False)
|
|
num = 0
|
|
for insert in inserts:
|
|
if (dxfCreateDraft or dxfCreateSketch) and not(dxfMakeBlocks):
|
|
shape = drawInsert(insert,num,clone=True)
|
|
else:
|
|
shape = drawInsert(insert,num)
|
|
if shape:
|
|
if dxfMakeBlocks:
|
|
addToBlock(shape,insert.layer)
|
|
else:
|
|
newob = addObject(shape,"Block."+insert.block,insert.layer)
|
|
if gui: formatObject(newob,insert)
|
|
num += 1
|
|
|
|
# make blocks, if any
|
|
|
|
if dxfMakeBlocks:
|
|
print("creating layerblocks...")
|
|
for k,l in layerBlocks.items():
|
|
shape = drawLayerBlock(l)
|
|
if shape:
|
|
newob = addObject(shape,k)
|
|
del layerBlocks
|
|
|
|
# hide block objects, if any
|
|
|
|
for k,o in blockobjects.items():
|
|
if o.ViewObject:
|
|
o.ViewObject.hide()
|
|
del blockobjects
|
|
|
|
# finishing
|
|
|
|
print("done processing")
|
|
|
|
doc.recompute()
|
|
FreeCAD.Console.PrintMessage("successfully imported "+filename+"\n")
|
|
if badobjects:
|
|
print("dxf: ",len(badobjects)," objects were not imported")
|
|
del doc
|
|
del blockshapes
|
|
|
|
def warn(dxfobject,num=None):
|
|
"outputs a warning if a dxf object couldn't be imported"
|
|
print("dxf: couldn't import ", dxfobject, " (",num,")")
|
|
badobjects.append(dxfobject)
|
|
|
|
def open(filename):
|
|
"called when freecad opens a file."
|
|
readPreferences()
|
|
if dxfReader:
|
|
docname = os.path.splitext(os.path.basename(filename))[0]
|
|
if isinstance(docname,unicode):
|
|
import sys #workaround since newDocument currently can't handle unicode filenames
|
|
docname = docname.encode(sys.getfilesystemencoding())
|
|
doc = FreeCAD.newDocument(docname)
|
|
doc.Label = decodeName(docname)
|
|
processdxf(doc,filename)
|
|
return doc
|
|
else:
|
|
errorDXFLib(gui)
|
|
|
|
def insert(filename,docname):
|
|
"called when freecad imports a file"
|
|
readPreferences()
|
|
if dxfReader:
|
|
groupname = os.path.splitext(os.path.basename(filename))[0]
|
|
try:
|
|
doc=FreeCAD.getDocument(docname)
|
|
except NameError:
|
|
doc=FreeCAD.newDocument(docname)
|
|
FreeCAD.setActiveDocument(docname)
|
|
importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
|
|
importgroup.Label = decodeName(groupname)
|
|
processdxf(doc,filename)
|
|
for l in layers:
|
|
importgroup.addObject(l)
|
|
else:
|
|
errorDXFLib(gui)
|
|
|
|
def getShapes(filename):
|
|
"reads a dxf file and returns a list of shapes from its contents"
|
|
if dxfReader:
|
|
return processdxf(None,filename,getShapes=True)
|
|
|
|
|
|
# EXPORT ########################################################################
|
|
|
|
def projectShape(shape,direction):
|
|
import Drawing
|
|
edges = []
|
|
try:
|
|
groups = Drawing.projectEx(shape,direction)
|
|
except Part.OCCError:
|
|
print("unable to project shape on direction ",direction)
|
|
return shape
|
|
else:
|
|
for g in groups[0:5]:
|
|
if g:
|
|
edges.append(g)
|
|
return DraftGeomUtils.cleanProjection(Part.makeCompound(edges))
|
|
|
|
def getArcData(edge):
|
|
"returns center, radius, start and end angles of a circle-based edge"
|
|
ce = edge.Curve.Center
|
|
radius = edge.Curve.Radius
|
|
if len(edge.Vertexes) == 1:
|
|
# closed circle
|
|
return DraftVecUtils.tup(ce), radius, 0, 0
|
|
else:
|
|
# new method: recalculate ourselves - cannot trust edge.Curve.Axis or XAxis
|
|
p1 = edge.Vertexes[0].Point
|
|
p2 = edge.Vertexes[-1].Point
|
|
v1 = p1.sub(ce)
|
|
v2 = p2.sub(ce)
|
|
#print v1.cross(v2)
|
|
#print edge.Curve.Axis
|
|
#print p1
|
|
#print p2
|
|
# we can use Z check since arcs getting here will ALWAYS be in XY plane
|
|
# Z can be 0 if the arc is 180 deg
|
|
if (v1.cross(v2).z >= 0) or (edge.Curve.Axis.z > 0):
|
|
#clockwise
|
|
ang1 = -DraftVecUtils.angle(v1)
|
|
ang2 = -DraftVecUtils.angle(v2)
|
|
else:
|
|
#counterclockwise
|
|
ang2 = -DraftVecUtils.angle(v1)
|
|
ang1 = -DraftVecUtils.angle(v2)
|
|
|
|
# obsolete method - fails a lot
|
|
#if round(edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1))) == 1:
|
|
# ang1,ang2=edge.ParameterRange
|
|
#else:
|
|
# ang2,ang1=edge.ParameterRange
|
|
#if edge.Curve.XAxis != FreeCAD.Vector(1,0,0):
|
|
# ang1 -= DraftVecUtils.angle(edge.Curve.XAxis)
|
|
# ang2 -= DraftVecUtils.angle(edge.Curve.XAxis)
|
|
|
|
return DraftVecUtils.tup(ce), radius, math.degrees(ang1),\
|
|
math.degrees(ang2)
|
|
|
|
def getSplineSegs(edge):
|
|
"returns an array of vectors from a Spline or Bezier edge"
|
|
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
|
|
seglength = params.GetFloat("maxsegmentlength",5.0)
|
|
points = []
|
|
if seglength == 0:
|
|
points.append(edge.Vertexes[0].Point)
|
|
points.append(edge.Vertexes[-1].Point)
|
|
else:
|
|
points.append(edge.valueAt(edge.FirstParameter))
|
|
if (edge.Length > seglength):
|
|
nbsegs = int(math.ceil(edge.Length/seglength))
|
|
step = (edge.LastParameter-edge.FirstParameter)/nbsegs
|
|
for nv in range(1,nbsegs):
|
|
#print("value at",nv*step,"=",edge.valueAt(nv*step))
|
|
v = edge.valueAt(edge.FirstParameter+(nv*step))
|
|
points.append(v)
|
|
points.append(edge.valueAt(edge.LastParameter))
|
|
return points
|
|
|
|
def getWire(wire,nospline=False,lw=True):
|
|
"returns an array of dxf-ready points and bulges from a wire"
|
|
def fmt(v,b=0.0):
|
|
if lw:
|
|
# LWpolyline format
|
|
return (v.x,v.y,v.z,None,None,b)
|
|
else:
|
|
# Polyline format
|
|
return ((v.x,v.y,v.z),None,[None,None],b)
|
|
edges = DraftGeomUtils.sortEdges(wire.Edges)
|
|
points = []
|
|
# print("processing wire ",wire.Edges)
|
|
for edge in edges:
|
|
v1 = edge.Vertexes[0].Point
|
|
if DraftGeomUtils.geomType(edge) == "Circle":
|
|
# polyline bulge -> negative makes the arc go clockwise
|
|
angle = edge.LastParameter-edge.FirstParameter
|
|
bul = math.tan(angle/4)
|
|
#if cross1[2] < 0:
|
|
# polyline bulge -> negative makes the arc go clockwise
|
|
#bul = -bul
|
|
if edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1)) < 0:
|
|
bul = -bul
|
|
points.append(fmt(v1,bul))
|
|
elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve","Ellipse"]) and (not nospline):
|
|
spline = getSplineSegs(edge)
|
|
spline.pop()
|
|
for p in spline:
|
|
points.append(fmt(p))
|
|
else:
|
|
points.append(fmt(v1))
|
|
if not DraftGeomUtils.isReallyClosed(wire):
|
|
v = edges[-1].Vertexes[-1].Point
|
|
points.append(fmt(v))
|
|
# print("wire verts: ",points)
|
|
return points
|
|
|
|
def getBlock(sh,obj,lwPoly=False):
|
|
"returns a dxf block with the contents of the object"
|
|
block = dxfLibrary.Block(name=obj.Name,layer=getGroup(obj))
|
|
writeShape(sh,obj,block,lwPoly)
|
|
return block
|
|
|
|
def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False):
|
|
"writes the object's shape contents in the given dxf object"
|
|
processededges = []
|
|
for wire in sh.Wires: # polylines
|
|
for e in wire.Edges:
|
|
processededges.append(e.hashCode())
|
|
if (len(wire.Edges) == 1) and (DraftGeomUtils.geomType(wire.Edges[0]) == "Circle"):
|
|
center, radius, ang1, ang2 = getArcData(wire.Edges[0])
|
|
if center != None:
|
|
if len(wire.Edges[0].Vertexes) == 1: # circle
|
|
dxfobject.append(dxfLibrary.Circle(center, radius,
|
|
color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else: # arc
|
|
dxfobject.append(dxfLibrary.Arc(center, radius,
|
|
ang1, ang2, color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else:
|
|
if (lwPoly):
|
|
if hasattr(dxfLibrary,"LwPolyLine"):
|
|
dxfobject.append(dxfLibrary.LwPolyLine(getWire(wire,nospline), [0.0,0.0],
|
|
int(DraftGeomUtils.isReallyClosed(wire)), color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else:
|
|
FreeCAD.Console.PrintWarning("LwPolyLine support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n")
|
|
else :
|
|
dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline,lw=False), [0.0,0.0,0.0],
|
|
int(DraftGeomUtils.isReallyClosed(wire)), color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
if len(processededges) < len(sh.Edges): # lone edges
|
|
loneedges = []
|
|
for e in sh.Edges:
|
|
if not(e.hashCode() in processededges): loneedges.append(e)
|
|
# print("lone edges ",loneedges)
|
|
for edge in loneedges:
|
|
if (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve"]): # splines
|
|
if (len(edge.Vertexes) == 1) and (edge.Curve.isClosed()) and (edge.Area > 0):
|
|
# special case: 1-vert closed spline, approximate as a circle
|
|
c = DraftGeomUtils.getCircleFromSpline(edge)
|
|
if c:
|
|
dxfobject.append(dxfLibrary.Circle(DraftVecUtils.tup(c.Curve.Center), c.Curve.Radius,
|
|
color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else:
|
|
points = []
|
|
spline = getSplineSegs(edge)
|
|
for p in spline:
|
|
points.append(((p.x,p.y,p.z),None,[None,None],0.0))
|
|
dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0],
|
|
0, color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
elif DraftGeomUtils.geomType(edge) == "Circle": # curves
|
|
center, radius, ang1, ang2 = getArcData(edge)
|
|
if center != None:
|
|
if not isinstance(center,tuple):
|
|
center = DraftVecUtils.tup(center)
|
|
if len(edge.Vertexes) == 1: # circles
|
|
dxfobject.append(dxfLibrary.Circle(center, radius,
|
|
color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else : # arcs
|
|
dxfobject.append(dxfLibrary.Arc(center, radius,
|
|
ang1, ang2, color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
elif DraftGeomUtils.geomType(edge) == "Ellipse": # ellipses:
|
|
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("DiscretizeEllipses",True):
|
|
points = []
|
|
spline = getSplineSegs(edge)
|
|
for p in spline:
|
|
points.append(((p.x,p.y,p.z),None,[None,None],0.0))
|
|
dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0],
|
|
0, color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else:
|
|
if hasattr(dxfLibrary,"Ellipse"):
|
|
center = DraftVecUtils.tup(edge.Curve.Center)
|
|
norm = DraftVecUtils.tup(edge.Curve.Axis)
|
|
start = edge.FirstParameter
|
|
end = edge.LastParameter
|
|
ax = edge.Curve.Focus1.sub(edge.Curve.Center)
|
|
major = DraftVecUtils.tup(DraftVecUtils.scaleTo(ax,edge.Curve.MajorRadius))
|
|
minor = edge.Curve.MinorRadius/edge.Curve.MajorRadius
|
|
# print("exporting ellipse: ",center,norm,start,end,major,minor)
|
|
dxfobject.append(dxfLibrary.Ellipse(center=center,majorAxis=major,normalAxis=norm,
|
|
minorAxisRatio=minor,startParameter=start,
|
|
endParameter=end,
|
|
color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
else:
|
|
FreeCAD.Console.PrintWarning("Ellipses support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n")
|
|
else: # anything else is treated as lines
|
|
if len(edge.Vertexes) > 1:
|
|
ve1=edge.Vertexes[0].Point
|
|
ve2=edge.Vertexes[1].Point
|
|
dxfobject.append(dxfLibrary.Line([DraftVecUtils.tup(ve1), DraftVecUtils.tup(ve2)],
|
|
color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
|
|
def writeMesh(ob,dxfobject):
|
|
"export a shape as a polyface mesh"
|
|
meshdata = ob.Shape.tessellate(0.5)
|
|
# print(meshdata)
|
|
points = []
|
|
faces = []
|
|
for p in meshdata[0]:
|
|
points.append([p.x,p.y,p.z])
|
|
for f in meshdata[1]:
|
|
faces.append([f[0]+1,f[1]+1,f[2]+1])
|
|
# print(len(points),len(faces))
|
|
dxfobject.append(dxfLibrary.PolyLine([points,faces], [0.0,0.0,0.0],
|
|
64, color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
|
|
def export(objectslist,filename,nospline=False,lwPoly=False):
|
|
"called when freecad exports a file. If nospline=True, bsplines are exported as straight segs lwPoly=True for OpenSCAD DXF"
|
|
readPreferences()
|
|
if dxfLibrary:
|
|
global exportList
|
|
exportList = objectslist
|
|
exportList = Draft.getGroupContents(exportList)
|
|
|
|
if (len(exportList) == 1) and (Draft.getType(exportList[0]) == "ArchSectionView"):
|
|
# arch view: export it "as is"
|
|
dxf = exportList[0].Proxy.getDXF()
|
|
if dxf:
|
|
f = open(filename,"w")
|
|
f.write(dxf)
|
|
f.close()
|
|
|
|
elif (len(exportList) == 1) and (exportList[0].isDerivedFrom("Drawing::FeaturePage")):
|
|
# page: special hack-export! (see below)
|
|
exportPage(exportList[0],filename)
|
|
|
|
else:
|
|
# other cases, treat edges
|
|
dxf = dxfLibrary.Drawing()
|
|
for ob in exportList:
|
|
print("processing "+str(ob.Name))
|
|
if ob.isDerivedFrom("Part::Feature"):
|
|
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfmesh"):
|
|
sh = None
|
|
if not ob.Shape.isNull():
|
|
writeMesh(ob,dxf)
|
|
elif gui and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfproject"):
|
|
direction = FreeCADGui.ActiveDocument.ActiveView.\
|
|
getViewDirection().multiply(-1)
|
|
sh = projectShape(ob.Shape,direction)
|
|
else:
|
|
if ob.Shape.Volume > 0:
|
|
sh = projectShape(ob.Shape,Vector(0,0,1))
|
|
else:
|
|
sh = ob.Shape
|
|
if sh:
|
|
if not sh.isNull():
|
|
if sh.ShapeType == 'Compound':
|
|
if (len(sh.Wires) == 1):
|
|
# only one wire in this compound, no lone edge -> polyline
|
|
if (len(sh.Wires[0].Edges) == len(sh.Edges)):
|
|
writeShape(sh,ob,dxf,nospline,lwPoly)
|
|
else:
|
|
# 1 wire + lone edges -> block
|
|
block = getBlock(sh,ob,lwPoly)
|
|
dxf.blocks.append(block)
|
|
dxf.append(dxfLibrary.Insert(name=ob.Name.upper()))
|
|
else:
|
|
# all other cases: block
|
|
block = getBlock(sh,ob,lwPoly)
|
|
dxf.blocks.append(block)
|
|
dxf.append(dxfLibrary.Insert(name=ob.Name.upper()))
|
|
else:
|
|
writeShape(sh,ob,dxf,nospline,lwPoly)
|
|
|
|
elif Draft.getType(ob) == "Annotation":
|
|
# texts
|
|
|
|
# temporary - as dxfLibrary doesn't support mtexts well, we use several single-line texts
|
|
# well, anyway, at the moment, Draft only writes single-line texts, so...
|
|
for text in ob.LabelText:
|
|
point = DraftVecUtils.tup(FreeCAD.Vector(ob.Position.x,
|
|
ob.Position.y-ob.LabelText.index(text),
|
|
ob.Position.z))
|
|
if gui: height = float(ob.ViewObject.FontSize)
|
|
else: height = 1
|
|
dxf.append(dxfLibrary.Text(text,point,height=height,
|
|
color=getACI(ob,text=True),
|
|
style='STANDARD',
|
|
layer=getGroup(ob)))
|
|
|
|
elif Draft.getType(ob) == "Dimension":
|
|
p1 = DraftVecUtils.tup(ob.Start)
|
|
p2 = DraftVecUtils.tup(ob.End)
|
|
base = Part.Line(ob.Start,ob.End).toShape()
|
|
proj = DraftGeomUtils.findDistance(ob.Dimline,base)
|
|
if not proj:
|
|
pbase = DraftVecUtils.tup(ob.End)
|
|
else:
|
|
pbase = DraftVecUtils.tup(ob.End.add(proj.negative()))
|
|
dxf.append(dxfLibrary.Dimension(pbase,p1,p2,color=getACI(ob),
|
|
layer=getGroup(ob)))
|
|
|
|
dxf.saveas(filename)
|
|
FreeCAD.Console.PrintMessage("successfully exported "+filename+"\r\n")
|
|
else:
|
|
errorDXFLib(gui)
|
|
|
|
def exportPage(page,filename):
|
|
"special export for pages"
|
|
template = os.path.splitext(page.Template)[0]+".dxf"
|
|
global dxfhandle
|
|
dxfhandle = 1
|
|
if os.path.exists(template):
|
|
f = pythonopen(template,"U")
|
|
template = f.read()
|
|
f.close()
|
|
# find & replace editable texts
|
|
import re
|
|
f = pythonopen(page.Template,"rb")
|
|
svgtemplate = f.read()
|
|
f.close()
|
|
editables = re.findall("freecad:editable=\"(.*?)\"",svgtemplate)
|
|
values = page.EditableTexts
|
|
for i in range(len(editables)):
|
|
if len(values) > i:
|
|
template = template.replace(editables[i],values[i])
|
|
else:
|
|
# dummy default template
|
|
print("DXF version of the template not found. Creating a default empty template.")
|
|
template = "999\nFreeCAD DXF exporter v"+FreeCAD.Version()[0]+"."+FreeCAD.Version()[1]+"-"+FreeCAD.Version()[2]+"\n"
|
|
template += "0\nSECTION\n2\nHEADER\n9\n$ACADVER\n1\nAC1009\n0\nENDSEC\n"
|
|
template += "0\nSECTION\n2\nBLOCKS\n$blocks\n0\nENDSEC\n"
|
|
template += "0\nSECTION\n2\nENTITIES\n$entities\n0\nENDSEC\n"
|
|
template += "0\nEOF"
|
|
blocks = ""
|
|
entities = ""
|
|
for view in page.Group:
|
|
b,e = getViewDXF(view)
|
|
blocks += b
|
|
entities += e
|
|
result = template.replace("999\n$blocks",blocks[:-1])
|
|
result = result.replace("999\n$entities",entities[:-1])
|
|
f = pythonopen(filename,"wb")
|
|
f.write(result)
|
|
f.close()
|
|
|
|
|
|
def getViewDXF(view):
|
|
"returns a DXF fragment from a Drawing View"
|
|
global dxfhandle
|
|
block = ""
|
|
insert = ""
|
|
|
|
if view.isDerivedFrom("App::DocumentObjectGroup"):
|
|
for child in view.Group:
|
|
b,e = getViewDXF(child)
|
|
block += b
|
|
insert += e
|
|
|
|
elif view.isDerivedFrom("Drawing::FeatureViewPython"):
|
|
if hasattr(view.Proxy,"getDXF"):
|
|
r = view.Rotation
|
|
if r != 0: r = -r # fix rotation direction
|
|
count = 0
|
|
block = ""
|
|
insert = ""
|
|
geom = view.Proxy.getDXF(view)
|
|
if not isinstance(geom,list): geom = [geom]
|
|
for g in geom: # getDXF returns a list of entities
|
|
g = g.replace("sheet_layer\n","0\n6\nBYBLOCK\n62\n0\n") # change layer and set color and ltype to BYBLOCK (0)
|
|
block += "0\nBLOCK\n8\n0\n2\n"+view.Name+str(count)+"\n70\n0\n10\n0\n20\n0\n3\n"+view.Name+str(count)+"\n1\n\n"
|
|
block += g
|
|
block += "0\nENDBLK\n8\n0\n"
|
|
insert += "0\nINSERT\n5\naaaa"+hex(dxfhandle)[2:]+"\n8\n0\n6\nBYLAYER\n62\n256\n2\n"+view.Name+str(count)
|
|
insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
|
|
insert += "\n30\n0\n41\n"+str(view.Scale)+"\n42\n"+str(view.Scale)+"\n43\n"+str(view.Scale)
|
|
insert += "\n50\n"+str(r)+"\n"
|
|
dxfhandle += 1
|
|
count += 1
|
|
|
|
elif view.isDerivedFrom("Drawing::FeatureViewPart"):
|
|
r = view.Rotation
|
|
if r != 0: r = -r # fix rotation direction
|
|
import Drawing
|
|
proj = Drawing.projectToDXF(view.Source.Shape,view.Direction)
|
|
proj = proj.replace("sheet_layer\n","0\n6\nBYBLOCK\n62\n0\n") # change layer and set color and ltype to BYBLOCK (0)
|
|
block = "0\nBLOCK\n8\n0\n2\n"+view.Name+"\n70\n0\n10\n0\n20\n0\n3\n"+view.Name+"\n1\n\n"
|
|
block += proj
|
|
block += "0\nENDBLK\n8\n0\n"
|
|
insert = "0\nINSERT\n5\naaaa"+hex(dxfhandle)[2:]+"\n8\n0\n6\nBYLAYER\n62\n256\n2\n"+view.Name
|
|
insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
|
|
insert += "\n30\n0\n41\n"+str(view.Scale)+"\n42\n"+str(view.Scale)+"\n43\n"+str(view.Scale)
|
|
insert += "\n50\n"+str(r)+"\n"
|
|
dxfhandle += 1
|
|
|
|
elif view.isDerivedFrom("Drawing::FeatureViewAnnotation"):
|
|
r = view.Rotation
|
|
if r != 0: r = -r # fix rotation direction
|
|
insert ="0\nTEXT\n5\n"+hex(dxfhandle)[2:]+"\n8\n0"
|
|
insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
|
|
insert += "\n30\n0\n40\n"+str(view.Scale/2)
|
|
insert += "\n50\n"+str(r)
|
|
insert += "\n1\n"+view.Text[0]+"\n"
|
|
dxfhandle += 1
|
|
|
|
else:
|
|
print("Unable to get DXF representation from view: ",view.Label)
|
|
return block,insert
|
|
|
|
|
|
def exportPageLegacy(page,filename):
|
|
"exports the given page the old way, by converting its SVG code to DXF with the Draft module"
|
|
import importSVG
|
|
tempdoc = importSVG.open(page.PageResult)
|
|
tempobj = tempdoc.Objects
|
|
export(tempobj,filename,nospline=True,lwPoly=False)
|
|
FreeCAD.closeDocument(tempdoc.Name)
|
|
|
|
def readPreferences():
|
|
# reading parameters
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
|
|
global dxfCreatePart, dxfCreateDraft, dxfCreateSketch, dxfDiscretizeCurves, dxfStarBlocks
|
|
global dxfMakeBlocks, dxfJoin, dxfRenderPolylineWidth, dxfImportTexts, dxfImportLayouts
|
|
global dxfImportPoints, dxfImportHatches, dxfUseStandardSize, dxfGetColors, dxfUseDraftVisGroups
|
|
global dxfFillMode, dxfBrightBackground, dxfDefaultColor
|
|
dxfCreatePart = p.GetBool("dxfCreatePart",True)
|
|
dxfCreateDraft = p.GetBool("dxfCreateDraft",False)
|
|
dxfCreateSketch = p.GetBool("dxfCreateSketch",False)
|
|
dxfDiscretizeCurves = p.GetBool("DiscretizeEllipses",True)
|
|
dxfStarBlocks = p.GetBool("dxfstarblocks",False)
|
|
dxfMakeBlocks = p.GetBool("groupLayers",False)
|
|
dxfJoin = p.GetBool("joingeometry",False)
|
|
dxfRenderPolylineWidth = p.GetBool("renderPolylineWidth",False)
|
|
dxfImportTexts = p.GetBool("dxftext",False)
|
|
dxfImportLayouts = p.GetBool("dxflayouts",False)
|
|
dxfImportPoints = p.GetBool("dxfImportPoints",False)
|
|
dxfImportHatches = p.GetBool("importDxfHatches",False)
|
|
dxfUseStandardSize = p.GetBool("dxfStdSize",False)
|
|
dxfGetColors = p.GetBool("dxfGetOriginalColors",False)
|
|
dxfUseDraftVisGroups = p.GetBool("dxfUseDraftVisGroups",False)
|
|
dxfFillMode = p.GetBool("fillmode",True)
|
|
dxfBrightBackground = isBrightBackground()
|
|
dxfDefaultColor = getColor()
|