Hull and Minkowski operations using OpenSCAD

This commit is contained in:
Keith Sloan
2013-10-30 10:04:34 +01:00
committed by Sebastian Hoogen
parent 7336a31e44
commit bcb7d02c05
12 changed files with 11930 additions and 9374 deletions

View File

@@ -69,7 +69,7 @@ def workaroundforissue128needed():
#return fdate < 2012.4759
def getopenscadversion(osfilename=None):
import os,subprocess,tempfile,time
import os,subprocess,time
if not osfilename:
import FreeCAD
osfilename = FreeCAD.ParamGet(\
@@ -81,6 +81,16 @@ def getopenscadversion(osfilename=None):
p.wait()
return p.stdout.read().strip()
def newtempfilename():
import os,time
formatstr='fc-%05d-%06d-%06d'
count = 0
while True:
count+=1
yield formatstr % (os.getpid(),int(time.time()*100) % 1000000,count)
tempfilenamegen=newtempfilename()
def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=False):
'''call the open scad binary
returns the filename of the result (or None),
@@ -107,8 +117,8 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals
outputfilename=os.path.join(dir1,'%s.%s' % (os.path.split(\
inputfilename)[1].rsplit('.',1)[0],outputext))
else:
outputfilename=os.path.join(dir1,'output-%d.%s' % \
(int(time.time()*100) % 1000000,outputext))
outputfilename=os.path.join(dir1,'%s.%s' % \
(tempfilenamegen.next(),outputext))
check_output2([osfilename,'-o',outputfilename, inputfilename],\
stderr=subprocess.STDOUT)
return outputfilename
@@ -119,8 +129,7 @@ def callopenscadstring(scadstr,outputext='csg'):
please delete the file afterwards'''
import os,tempfile,time
dir1=tempfile.gettempdir()
inputfilename=os.path.join(dir1,'input-%d.scad' % \
(int(time.time()*10) % 1000000))
inputfilename=os.path.join(dir1,'%s.scad' % tempfilenamegen.next())
inputfile = open(inputfilename,'w')
inputfile.write(scadstr)
inputfile.close()
@@ -183,3 +192,160 @@ def isspecialorthogonalpython(submat,precision=4):
def isspecialorthogonal(mat,precision=4):
return abs(mat.submatrix(3).isOrthogonal(10**(-precision))-1.0) < 10**(-precision) and \
abs(mat.submatrix(3).determinant()-1.0) < 10**(-precision)
def callopenscadmeshstring(scadstr):
"""Call OpenSCAD and return the result as a Mesh"""
import Mesh,os
tmpfilename=callopenscadstring(scadstr,'stl')
newmesh=Mesh.Mesh()
newmesh.read(tmpfilename)
try:
os.unlink(tmpfilename)
except OSError:
pass
return newmesh
def meshopinline(opname,iterable1):
"""uses OpenSCAD to combine meshes
takes the name of the CGAL operation and an iterable (tuple,list) of
FreeCAD Mesh objects
includes all the mesh data in the SCAD file
"""
from exportCSG import mesh2polyhedron
return callopenscadmeshstring('%s(){%s}' % (opname,' '.join(\
(mesh2polyhedron(meshobj) for meshobj in iterable1))))
def meshoptempfile(opname,iterable1):
"""uses OpenSCAD to combine meshes
takes the name of the CGAL operation and an iterable (tuple,list) of
FreeCAD Mesh objects
uses stl files to supply the mesh data
"""
import os,tempfile
dir1=tempfile.gettempdir()
filenames = []
for mesh in iterable1:
outputfilename=os.path.join(dir1,'%s.stl' % tempfilenamegen.next())
mesh.write(outputfilename)
filenames.append(outputfilename)
#absolute path causes error. We rely that the scad file will be in the dame tmpdir
meshimports = ' '.join("import(file = \"%s\");" % \
#filename \
os.path.split(filename)[1] for filename in filenames)
result = callopenscadmeshstring('%s(){%s}' % (opname,meshimports))
for filename in filenames:
try:
os.unlink(filename)
except OSError:
pass
return result
def meshoponobjs(opname,inobjs):
"""
takes a string (operation name) and a list of Feature Objects
returns a mesh and a list of objects that were used
Part Objects will be meshed
"""
objs=[]
meshes=[]
for obj in inobjs:
if obj.isDerivedFrom('Mesh::Feature'):
objs.append(obj)
meshes.append(obj.Mesh)
elif obj.isDerivedFrom('Part::Feature'):
#mesh the shape
import FreeCAD
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
objs.append(obj)
if False: # disabled due to issue 1292
import MeshPart
meshes.append(MeshPart.meshFromShape(obj.Shape,params.GetFloat(\
'meshmaxlength',1.0), params.GetFloat('meshmaxarea',0.0),\
params.GetFloat('meshlocallen',0.0),\
params.GetFloat('meshdeflection',0.0)))
else:
import Mesh
meshes.append(Mesh.Mesh(obj.Shape.tessellate(params.GetFloat(\
'meshmaxlength',1.0))))
else:
pass #neither a mesh nor a part
if len(objs) > 0:
return (meshoptempfile(opname,meshes),objs)
else:
return (None,[])
def process2D_ObjectsViaOpenSCAD(ObjList,Operation,doc=None):
import FreeCAD,importDXF
import os,tempfile
#print "process2D"
doc = doc or FreeCAD.activeDocument()
dir1=tempfile.gettempdir()
filenames = []
#print "Export DXF"
for item in ObjList :
outputfilename=os.path.join(dir1,'%s.dxf' % tempfilenamegen.next())
#print "Call Export : "+outputfilename
importDXF.export([item],outputfilename,True,True)
#print "File Exported"
filenames.append(outputfilename)
dxfimports = ' '.join("import(file = \"%s\");" % \
#filename \
os.path.split(filename)[1] for filename in filenames)
#print "Call OpenSCAD : "+dxfimports
tmpfilename = callopenscadstring('%s(){%s}' % (Operation,dxfimports),'dxf')
#from importCSG import processDXF #import the result
#obj = processDXF(tmpfilename,None)
from OpenSCAD2Dgeom import importDXFface
#print "Import DXF"
face = importDXFface(tmpfilename,None,None)
#print "Add Hull"
obj=doc.addObject('Part::Feature',Operation)
obj.Shape=face
# Hide Children
if FreeCAD.GuiUp:
for index in ObjList :
index.ViewObject.hide()
#clean up
filenames.append(tmpfilename) #delete the ouptut file as well
try:
os.unlink(tmpfilename)
except OSError:
pass
return(obj)
def process3D_ObjectsViaOpenSCAD(doc,ObjList,Operation):
import FreeCAD,Mesh,Part
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
if False: # disabled due to issue 1292
import MeshPart
meshes = [MeshPart.meshFromShape(obj.Shape,params.GetFloat(\
'meshmaxlength',1.0), params.GetFloat('meshmaxarea',0.0),\
params.GetFloat('meshlocallen',0.0),\
params.GetFloat('meshdeflection',0.0)) for obj in ObjList]
else:
meshes = [Mesh.Mesh(obj.Shape.tessellate(params.GetFloat(\
'meshmaxlength',1.0))) for obj in ObjList]
if max(mesh.CountPoints for mesh in meshes) < \
params.GetInt('tempmeshmaxpoints',5000):
stlmesh = meshoptempfile(Operation,meshes)
sh=Part.Shape()
sh.makeShapeFromMesh(stlmesh.Topology,0.1)
solid = Part.Solid(sh)
obj=doc.addObject('Part::Feature',Operation) #non parametric objec
solid=solid.removeSplitter()
if solid.Volume < 0:
solid.complement()
obj.Shape=solid#.removeSplitter()
if FreeCAD.GuiUp:
for index in ObjList :
index.ViewObject.hide()
return(obj)
def process_ObjectsViaOpenSCAD(doc,children,name):
if all(obj.Shape.Volume == 0 for obj in children):
return process2D_ObjectsViaOpenSCAD(children,name)
elif all(obj.Shape.Volume > 0 for obj in children):
return process3D_ObjectsViaOpenSCAD(doc,children,name)
else:
FreeCAD.Console.PrintError( unicode(translate('OpenSCAD',\
"Error Both shapes must be either 2D or both must be 3D"))+u'\n')