FreeCAD/src/Mod/Draft/importDXF.py
2011-11-05 14:28:29 +00:00

1124 lines
42 KiB
Python

# -*- coding: utf8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2009 Yorik van Havre <yorik@gmx.fr> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* 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@gmx.fr>"
__url__ = ["http://yorik.orgfree.com","http://free-cad.sourceforge.net"]
'''
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)
'''
import FreeCAD, os, Part, math, re, string, Mesh, Draft
from draftlibs import fcvec, dxfColorMap, dxfLibrary, fcgeo
from draftlibs.dxfReader import readDXF
from Draft import Dimension, ViewProviderDimension
from FreeCAD import Vector
try: import FreeCADGui
except: gui = False
else: gui = True
try: draftui = FreeCADGui.draftToolBar
except: draftui = None
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")
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 locateLayer(wantedLayer):
"returns layer group and creates it if needed"
wantedLayerName = decodeName(wantedLayer)
for l in layers:
if wantedLayerName==l.Label:
return l
newLayer = doc.addObject("App::DocumentObjectGroup",wantedLayer)
newLayer.Label = wantedLayerName
layers.append(newLayer)
return newLayer
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
startpoint = v1.add(fcvec.scale(chord,0.5))
perp = chord.cross(Vector(0,0,1))
if not fcvec.isNull(perp): perp.normalize()
endpoint = fcvec.scale(perp,sagitta)
return startpoint.add(endpoint)
def getGroup(ob,exportList):
"checks if the object is part of a group"
for i in exportList:
if (i.Type == "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
class fcformat:
"this contains everything related to color/lineweight formatting"
def __init__(self,drawing):
self.dxf = drawing
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
self.paramtext = params.GetBool("dxftext")
self.paramstarblocks = params.GetBool("dxfstarblocks")
self.dxflayout = params.GetBool("dxflayouts")
self.paramstyle = params.GetInt("dxfstyle")
self.join = params.GetBool("joingeometry")
self.makeBlocks = params.GetBool("groupLayers")
bparams = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
if self.paramstyle > 1:
# checking if FreeCAD background is dark or bright
if bparams.GetBool("Gradient"):
c1 = bparams.GetUnsigned("BackgroundColor2")
c2 = bparams.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 = fcvec.scale(v,0.5)
cv = v1.add(v)
else:
c1 = bparams.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: self.brightbg = False
else:
self.brightbg = True
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)
self.lw = float(draftui.linewidth)
else:
self.lw = float(params.GetInt("linewidth"))
c = params.GetUnsigned("color")
r = float(((c>>24)&0xFF)/255)
g = float(((c>>16)&0xFF)/255)
b = float(((c>>8)&0xFF)/255)
self.col = (r,g,b,0.0)
if self.paramstyle == 3:
parammappingfile = params.GetString("dxfmappingfile")
self.table = self.buildTable(parammappingfile)
def buildTable(self,tablefile):
"builds a table for converting colors into linewidths"
try: f = pythonopen(tablefile)
except ValueError:
print "error: ",tablefile, " not found"
return None
table = {}
header = len(f.readline().split("\t"))
if header == 15:
for l in f:
s = l.split("\t")
if "Color_" in s[0]:
index = int(s[0].split("_")[1])
if s[1] == "(Object)": color = "object"
else:
c = s[2].split(",")
color = [float(c[0])/255,float(c[1])/255,float(c[2])/255]
if (color == [0.0,0.0,0.0]) and (not self.brightbg):
color = [1.0,1.0,1.0]
if s[2] == "(Object)": width = "object"
else: width = float(s[10])*10
table[index]=[color,width]
elif header == 3:
for l in f:
s = l.split("\t")
index = int(s[0])
c = string.replace(s[1],'"','')
c = c.split(",")
color = [float(c[0])/255,float(c[1])/255,float(c[2])/255]
width = float(s[2])
table[index]=[color,width]
for i in range(256):
if not i in table.keys():
table[i]=["object","object"]
else:
print "error building mapping table: file format not recognized"
table = None
print table
return table
def formatObject(self,obj,dxfobj,textmode=False):
"applies color and linetype to objects"
if self.paramstyle == 1:
if textmode:
obj.ViewObject.TextColor = (0.0,0.0,0.0)
else:
obj.ViewObject.LineColor = self.col
obj.ViewObject.LineWidth = self.lw
elif self.paramstyle == 2:
if textmode:
if dxfobj.color_index == 256: cm = self.getGroupColor(dxfobj)[:3]
else: cm = dxfColorMap.color_map[dxfobj.color_index]
obj.ViewObject.TextColor = (cm[0],cm[1],cm[2])
else:
if dxfobj.color_index == 256: cm = self.getGroupColor(dxfobj)
elif (dxfobj.color_index == 7) and self.brightbg: 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)
obj.ViewObject.LineWidth = self.lw
elif self.paramstyle == 3:
if textmode:
cm = table[dxfobj.color_index][0]
wm = table[dxfobj.color_index][1]
obj.ViewObject.TextColor = (cm[0],cm[1],cm[2])
else:
if dxfobj.color_index == 256:
cm = self.table[self.getGroupColor(dxfobj,index=True)][0]
wm = self.table[self.getGroupColor(dxfobj,index=True)][1]
else:
cm = self.table[dxfobj.color_index][0]
wm = self.table[dxfobj.color_index][1]
if cm == "object": cm = self.getGroupColor(dxfobj)
else: obj.ViewObject.LineColor = (cm[0],cm[1],cm[2],0.0)
if wm == "object": wm = self.lw
else: obj.ViewObject.LineWidth = wm
def getGroupColor(self,dxfobj,index=False):
"get color of bylayer stuff"
name = dxfobj.layer
for table in self.dxf.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 self.brightbg: return [0.0,0.0,0.0]
else: return dxfColorMap.color_map[l.color]
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,shapemode=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 fcvec.equals(v1,v2):
try:
if (fmt.paramstyle == 4) and (not fmt.makeBlocks) and (not shapemode):
return Draft.makeWire([v1,v2])
else:
return Part.Line(v1,v2).toShape()
except:
warn(line)
return None
def drawPolyline(polyline,shapemode=False):
"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 fcvec.equals(v1,v2):
if polyline.points[p].bulge:
curves = True
cv = calcBulge(v1,polyline.points[p].bulge,v2)
if fcvec.isColinear([v1,cv,v2]):
try: edges.append(Part.Line(v1,v2).toShape())
except: warn(polyline)
else:
try: edges.append(Part.Arc(v1,cv,v2).toShape())
except: warn(polyline)
else:
try: edges.append(Part.Line(v1,v2).toShape())
except: warn(polyline)
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 fcvec.equals(v1,v2):
if fcvec.isColinear([v1,cv,v2]):
try: edges.append(Part.Line(v1,v2).toShape())
except: warn(polyline)
else:
try: edges.append(Part.Arc(v1,cv,v2).toShape())
except: warn(polyline)
if edges:
try:
if (fmt.paramstyle == 4) and (not curves) and (not fmt.makeBlocks) and (not shapemode):
ob = Draft.makeWire(verts)
ob.Closed = polyline.closed
return ob
else:
if polyline.closed:
w = Part.Wire(edges)
return(Part.Face(w))
else:
return Part.Wire(edges)
except:
warn(polyline)
return None
def drawArc(arc,shapemode=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 (fmt.paramstyle == 4) and (not fmt.makeBlocks) and (not shapemode):
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:
warn(arc)
return None
def drawCircle(circle,shapemode=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 (fmt.paramstyle == 4) and (not fmt.makeBlocks) and (not shapemode):
pl = FreeCAD.Placement()
pl.move(v)
return Draft.makeCircle(circle.radius,pl)
else:
return curve.toShape()
except:
warn(circle)
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:
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:
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,p3,p4,p1]))
except:
warn(solid)
else:
try:
return Part.Face(Part.makePolygon([p1,p2,p3,p1]))
except:
warn(solid)
return None
def drawBlock(blockref):
"returns a shape from a dxf block reference"
shapes = []
for line in blockref.entities.get_type('line'):
s = drawLine(line,shapemode=True)
if s: shapes.append(s)
for polyline in blockref.entities.get_type('polyline'):
s = drawPolyline(polyline,shapemode=True)
if s: shapes.append(s)
for polyline in blockref.entities.get_type('lwpolyline'):
s = drawPolyline(polyline,shapemode=True)
if s: shapes.append(s)
for arc in blockref.entities.get_type('arc'):
s = drawArc(arc,shapemode=True)
if s: shapes.append(s)
for circle in blockref.entities.get_type('circle'):
s = drawCircle(circle,shapemode=True)
if s: shapes.append(s)
for insert in blockref.entities.get_type('insert'):
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 text in blockref.entities.get_type('text'):
if fmt.dxflayout or (not rawValue(text,67)):
addText(text)
for text in blockref.entities.get_type('mtext'):
if fmt.dxflayout or (not rawValue(text,67)):
addText(text)
try: shape = Part.makeCompound(shapes)
except: warn(blockref)
if shape:
blockshapes[blockref.name]=shape
return shape
return None
def drawInsert(insert):
if blockshapes.has_key(insert):
shape = blockshapes[insert.block]
else:
shape = None
for b in drawing.blocks.data:
if b.name == insert.block:
shape = drawBlock(b)
if fmt.paramtext:
attrs = attribs(insert)
for a in attrs:
addText(a,attrib=True)
if shape:
pos = vec(insert.loc)
rot = math.radians(insert.rotation)
scale = insert.scale
tsf = FreeCAD.Matrix()
tsf.scale(vec(scale))
tsf.rotateZ(rot)
shape = shape.transformGeometry(tsf)
shape.translate(pos)
return shape
return None
def drawLayerBlock(shapeslist):
"draws a compound with the given shapes"
shape = None
try:
shape = Part.makeCompound(shapeslist)
except:
pass
return shape
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]
print str(ent)
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)
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:
newob=doc.addObject("App::Annotation","Text")
lay.addObject(newob)
val = re.sub('{([^!}]([^}]|\n)*)}', '', val)
val = re.sub('%%d','°',val)
val = re.sub('%%c','Ø',val)
val = val.decode("Latin1").encode("Latin1")
newob.LabelText = val
newob.Position = pos
if gui:
newob.ViewObject.FontSize=float(hgt)
newob.ViewObject.DisplayMode = "World"
fmt.formatObject(newob,text,textmode=True)
def addToBlock(shape,layer):
"adds given shape to the layer dict"
if layer in layerBlocks:
layerBlocks[layer].append(shape)
else:
layerBlocks[layer] = [shape]
def processdxf(document,filename):
"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
FreeCAD.Console.PrintMessage("opening "+filename+"...\n")
drawing = readDXF(filename)
global layers
layers = []
global doc
doc = document
global blockshapes
blockshapes = {}
global badobjects
badobjects = []
global layerBlocks
layerBlocks = {}
# getting config parameters
global fmt
fmt = fcformat(drawing)
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 fmt.dxflayout or (not rawValue(line,67)):
shape = drawLine(line)
if shape:
if fmt.join:
if isinstance(shape,Part.Shape):
shapes.append(shape)
else:
shapes.append(shape.Shape)
elif fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,line.layer)
else:
addToBlock(shape.Shape,line.layer)
else:
newob = addObject(shape,"Line",line.layer)
if gui: fmt.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")
for polyline in polylines:
if fmt.dxflayout or (not rawValue(polyline,67)):
shape = drawPolyline(polyline)
if shape:
if fmt.join:
if isinstance(shape,Part.Shape):
shapes.append(shape)
else:
shapes.append(shape.Shape)
elif fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,polyline.layer)
else:
addToBlock(shape.Shape,polyline.layer)
else:
newob = addObject(shape,"Polyline",polyline.layer)
if gui: fmt.formatObject(newob,polyline)
# drawing arcs
arcs = drawing.entities.get_type("arc")
if arcs: FreeCAD.Console.PrintMessage("drawing "+str(len(arcs))+" arcs...\n")
for arc in arcs:
if fmt.dxflayout or (not rawValue(arc,67)):
shape = drawArc(arc)
if shape:
if fmt.join:
if isinstance(shape,Part.Shape):
shapes.append(shape)
else:
shapes.append(shape.Shape)
elif fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,arc.layer)
else:
addToBlock(shape.Shape,arc.layer)
else:
newob = addObject(shape,"Arc",arc.layer)
if gui: fmt.formatObject(newob,arc)
# joining lines, polylines and arcs if needed
if fmt.join and shapes:
FreeCAD.Console.PrintMessage("Joining geometry...\n")
edges = []
for s in shapes:
edges.extend(s.Edges)
shapes = fcgeo.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 fmt.dxflayout or (not rawValue(circle,67)):
shape = drawCircle(circle)
if shape:
if fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,circle.layer)
else:
addToBlock(shape.Shape,circle.layer)
else:
newob = addObject(shape,"Circle",circle.layer)
if gui: fmt.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 fmt.dxflayout or (not rawValue(solid,67)):
shape = drawSolid(solid)
if shape:
if fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,lay)
else:
addToBlock(shape.Shape,lay)
else:
newob = addObject(shape,"Solid",lay)
if gui: fmt.formatObject(newob,solid)
# drawing texts
if fmt.paramtext:
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 fmt.dxflayout 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:
newob = addObject(shape,"Face",face3d.layer)
if gui: fmt.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: fmt.formatObject(newob,mesh)
# drawing dims
if fmt.paramtext:
dims = drawing.entities.get_type("dimension")
FreeCAD.Console.PrintMessage("drawing "+str(len(dims))+" dimensions...\n")
for dim in dims:
if fmt.dxflayout 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:
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 == 0:
if angle == 0:
p2 = FreeCAD.Vector(x3,y2,z2)
else:
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
fmt.formatObject (newob,dim)
else: FreeCAD.Console.PrintMessage("skipping dimensions...\n")
# drawing blocks
inserts = drawing.entities.get_type("insert")
if not fmt.paramstarblocks:
FreeCAD.Console.PrintMessage("skipping *blocks...\n")
newinserts = []
for i in inserts:
if fmt.dxflayout 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:
drawBlock(ref)
for insert in inserts:
shape = drawInsert(insert)
if shape:
if fmt.makeBlocks:
if isinstance(shape,Part.Shape):
addToBlock(shape,block.layer)
else:
addToBlock(shape.Shape,block.layer)
else:
newob = addObject(shape,"Block."+insert.block,insert.layer)
if gui: fmt.formatObject(newob,insert)
# make blocks, if any
if fmt.makeBlocks:
for k,l in layerBlocks.iteritems():
shape = drawLayerBlock(l)
if shape:
newob = addObject(shape,k)
del layerBlocks
# finishing
doc.recompute()
FreeCAD.Console.PrintMessage("successfully imported "+filename+"\n")
if badobjects: print "dxf: ",len(badobjects)," objects were not imported"
del fmt
del doc
del blockshapes
def warn(dxfobject):
"outputs a warning if a dxf object couldn't be imported"
print "dxf: couldn't import", dxfobject
badobjects.append(dxfobject)
def open(filename):
"called when freecad opens a file."
docname = os.path.splitext(os.path.basename(filename))[0]
doc = FreeCAD.newDocument(docname)
doc.Label = decodeName(docname)
processdxf(doc,filename)
return doc
def insert(filename,docname):
"called when freecad imports a file"
groupname = os.path.splitext(os.path.basename(filename))[0]
try:
doc=FreeCAD.getDocument(docname)
except:
doc=FreeCAD.newDocument(docname)
importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
importgroup.Label = decodeName(groupname)
processdxf(doc,filename)
for l in layers:
importgroup.addObject(l)
# EXPORT ########################################################################
def getSplineSegs(edge):
"returns an array of vectors from a bSpline edge"
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
seglength = params.GetInt("maxsplinesegment")
points = []
if seglength == 0:
points.append(edge.Vertexes[0].Point)
points.append(edge.Vertexes[-1].Point)
else:
l = edge.Length
points.append(edge.valueAt(0))
if l > seglength:
nbsegs = int(math.ceil(l/seglength))
step = l/nbsegs
for nv in range(1,nbsegs):
v = edge.valueAt(nv*step)
points.append(v)
points.append(edge.valueAt(edge.Length))
return points
class savePoint:
def __init__(self,end_pt):
self.old_pt = end_pt
def old(self):
return self.old_pt
def getWire(wire,nospline=False):
"returns an array of dxf-ready points and bulges from a wire"
edges = fcgeo.sortEdges(wire.Edges)
points = []
for edge in edges:
v1 = edge.Vertexes[0].Point
end_pt = savePoint(edge.Vertexes[-1].Point)
if (isinstance(edge.Curve,Part.Circle)):
mp = fcgeo.findMidpoint(edge)
v2 = edge.Vertexes[-1].Point
c = edge.Curve.Center
angle = abs(fcvec.angle(v1.sub(c),v2.sub(c)))
# if (fcvec.angle(v2.sub(c)) < fcvec.angle(v1.sub(c))):
# angle = -angle
# polyline bulge -> negative makes the arc go clockwise
bul = math.tan(angle/4)
# the next bit of code is for finding the direction of the arc
# a negative cross product means the arc is clockwise
tang1 = edge.Curve.tangent(edge.ParameterRange[0])
tang2 = edge.Curve.tangent(edge.ParameterRange[1])
cross1 = Vector.cross(Vector(tang1[0][0],tang1[0][1],tang1[0][2]),Vector(tang2[0][0],tang2[0][1],tang2[0][2]))
if cross1[2] < 0:
# polyline bulge -> negative makes the arc go clockwise
bul = -bul
points.append((v1.x,v1.y,v1.z,None,None,bul))
elif (isinstance(edge.Curve,Part.BSplineCurve)) and (not nospline):
bul = 0.0
spline = getSplineSegs(edge)
spline.pop()
for p in spline:
points.append((p.x,p.y,p.z,None,None,bul))
else:
bul = 0.0
points.append((v1.x,v1.y,v1.z,None,None,bul))
if not fcgeo.isReallyClosed(wire):
v = edges[-1].Vertexes[-1].Point
points.append(fcvec.tup(v))
# print "wire verts: ",points
return points
def getBlock(obj):
"returns a dxf block with the contents of the object"
block = dxfLibrary.Block(name=obj.Name,layer=getGroup(obj,exportList))
writeShape(obj,block)
return block
def writeShape(ob,dxfobject,nospline=False):
"writes the object's shape contents in the given dxf object"
processededges = []
for wire in ob.Shape.Wires: # polylines
for e in wire.Edges: processededges.append(e.hashCode())
if (len(wire.Edges) == 1) and isinstance(wire.Edges[0].Curve,Part.Circle):
center = fcvec.tup(wire.Edges[0].Curve.Center)
radius = wire.Edges[0].Curve.Radius
dxfobject.append(dxfLibrary.Circle(center, radius,
color=getACI(ob),
layer=getGroup(ob,exportList)))
else:
dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline), [0.0,0.0,0.0],
int(fcgeo.isReallyClosed(wire)), color=getACI(ob),
layer=getGroup(ob,exportList)))
if len(processededges) < len(ob.Shape.Edges): # lone edges
loneedges = []
for e in ob.Shape.Edges:
if not(e.hashCode() in processededges): loneedges.append(e)
# print "lone edges ",loneedges
for edge in loneedges:
if (isinstance(edge.Curve,Part.BSplineCurve)) and ((not nospline) or (len(edge.Vertexes) == 1)): # splines
points = []
spline = getSplineSegs(edge)
for p in spline:
points.append((p.x,p.y,p.z,None,None,0.0))
dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0],
0, color=getACI(ob),
layer=getGroup(ob,exportList)))
elif (len(edge.Vertexes) == 1) and (isinstance(edge.Curve,Part.Circle)): # circles
center = fcvec.tup(edge.Curve.Center)
radius = ob.Shape.Edges[0].Curve.Radius
dxfobject.append(dxfLibrary.Circle(center, radius,
color=getACI(ob),
layer=getGroup(ob,exportList)))
elif (len(edge.Vertexes) == 3) or (isinstance (edge.Curve,Part.Circle)): # arcs
ce = ob.Shape.Edges[0].Curve.Center
radius = ob.Shape.Edges[0].Curve.Radius
ve1 = edge.Vertexes[0].Point
ve2 = edge.Vertexes[-1].Point
ang1=-math.degrees(fcvec.angle(ve1.sub(ce)))
ang2=-math.degrees(fcvec.angle(ve2.sub(ce)))
dxfobject.append(dxfLibrary.Arc(fcvec.tup(ce), radius,
ang1, ang2, color=getACI(ob),
layer=getGroup(ob,exportList)))
else: # anything else is treated as lines
# if (len(edge.Vertexes) == 2) and (isinstance (edge.Curve,Part.Line)): # lines
ve1=edge.Vertexes[0].Point
ve2=edge.Vertexes[1].Point
dxfobject.append(dxfLibrary.Line([fcvec.tup(ve1), fcvec.tup(ve2)],
color=getACI(ob),
layer=getGroup(ob,exportList)))
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,exportList)))
def export(objectslist,filename,nospline=False):
"called when freecad exports a file. If nospline=True, bsplines are exported as straight segs"
global exportList
exportList = objectslist
dxf = dxfLibrary.Drawing()
if (len(exportList) == 1) and (exportList[0].isDerivedFrom("Drawing::FeaturePage")):
exportPage(exportList[0],filename)
return
for ob in exportList:
print "processing ",ob.Name
if ob.isDerivedFrom("Part::Feature"):
if not ob.Shape.isNull():
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfmesh"):
writeMesh(ob,dxf)
else:
if ob.Shape.ShapeType == 'Compound':
if (len(ob.Shape.Wires) == 1):
# only one wire in this compound, no lone edge -> polyline
if (len(ob.Shape.Wires[0].Edges) == len(ob.Shape.Edges)):
writeShape(ob,dxf,nospline)
else:
# 1 wire + lone edges -> block
block = getBlock(ob)
dxf.blocks.append(block)
dxf.append(dxfLibrary.Insert(name=ob.Name.upper()))
else:
# all other cases: block
block = getBlock(ob)
dxf.blocks.append(block)
dxf.append(dxfLibrary.Insert(name=ob.Name.upper()))
else:
writeShape(ob,dxf,nospline)
elif (ob.Type == "App::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 = fcvec.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,exportList)))
elif 'Dimline' in ob.PropertiesList:
p1 = fcvec.tup(ob.Start)
p2 = fcvec.tup(ob.End)
base = Part.Line(ob.Start,ob.End).toShape()
proj = fcgeo.findDistance(ob.Dimline,base)
if not proj:
pbase = fcvec.tup(ob.End)
else:
pbase = fcvec.tup(ob.End.add(fcvec.neg(proj)))
dxf.append(dxfLibrary.Dimension(pbase,p1,p2,color=getACI(ob),
layer=getGroup(ob,exportList)))
dxf.saveas(filename)
FreeCAD.Console.PrintMessage("successfully exported "+filename+"\r\n")
def exportPage(page,filename):
"special export for pages"
import importSVG
tempdoc = importSVG.open(page.PageResult)
tempobj = tempdoc.Objects
export(tempobj,filename,nospline=True)
FreeCAD.closeDocument(tempdoc.Name)