OpenSCAD: pep8-ified + removed superfluous whitespace + uniform headers

This commit is contained in:
luz paz
2022-01-13 19:25:24 -05:00
committed by Chris Hennes
parent c14e6b318e
commit 78d0fc786a
16 changed files with 602 additions and 506 deletions

View File

@@ -2,7 +2,7 @@
# (c) 2001 Juergen Riegel
#***************************************************************************
#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 *
#* Copyright (c) 2002 Juergen Riegel <juergen.riegel@web.de> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
@@ -22,18 +22,21 @@
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Juergen Riegel 2002 *
#***************************************************************************/
import FreeCAD,os
FreeCAD.addImportType("OpenSCAD CSG Format (*.csg)","importCSG")
import os
import FreeCAD
FreeCAD.addImportType("OpenSCAD CSG Format (*.csg)", "importCSG")
param = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD")
"User parameter:BaseApp/Preferences/Mod/OpenSCAD")
openscadfilename = param.GetString('openscadexecutable')
openscadbin = openscadfilename and os.path.isfile(openscadfilename)
if openscadbin:
FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG")
FreeCAD.addImportType("OpenSCAD Format (*.scad)", "importCSG")
FreeCAD.__unit_test__ += ["TestOpenSCADApp"]
FreeCAD.addExportType("OpenSCAD CSG Format (*.csg)","exportCSG")
FreeCAD.addExportType("OpenSCAD Format (*.scad)","exportCSG")
FreeCAD.addExportType("OpenSCAD CSG Format (*.csg)", "exportCSG")
FreeCAD.addExportType("OpenSCAD Format (*.scad)", "exportCSG")

View File

@@ -5,7 +5,7 @@
# runs when the gui is up
#***************************************************************************
#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 *
#* Copyright (c) 2002 Juergen Riegel <juergen.riegel@web.de> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
@@ -25,7 +25,6 @@
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Juergen Riegel 2002 *
#***************************************************************************/
import FreeCAD
@@ -63,7 +62,7 @@ class OpenSCADWorkbench ( Workbench ):
import OpenSCADUtils
openscadfilename = OpenSCADUtils.searchforopenscadexe()
if openscadfilename: #automatic search was succsessful
FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG")
FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG")
param.SetString('openscadexecutable',openscadfilename) #save the result
if openscadfilename:
commands.extend(['OpenSCAD_AddOpenSCADElement', 'OpenSCAD_MeshBoolean',
@@ -82,6 +81,7 @@ class OpenSCADWorkbench ( Workbench ):
FreeCADGui.addIconPath(":/icons")
FreeCADGui.addLanguagePath(":/translations")
FreeCADGui.addPreferencePage(":/ui/openscadprefs-base.ui","OpenSCAD")
def GetClassName(self):
return "Gui::PythonWorkbench"

View File

@@ -19,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - 2D helper functions"
__title__ = "FreeCAD OpenSCAD Workbench - 2D helper functions"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - GUI Commands"
__title__ = "FreeCAD OpenSCAD Workbench - GUI Commands"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -106,7 +105,7 @@ class ExplodeGroup:
explode(obj)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_Explode_Group',
return {'Pixmap' : 'OpenSCAD_Explode_Group',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Explode Group'),
'ToolTip': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Remove fusion, apply placement to children, and color randomly')}
@@ -122,8 +121,8 @@ class ColorCodeShape:
objs=FreeCAD.ActiveDocument.Objects
colorcodeshapes.colorcodeshapes(objs)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_ColorCodeShape',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'),
return {'Pixmap' : 'OpenSCAD_ColorCodeShape',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'),
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes by validity and type')}
class Edgestofaces:
@@ -411,7 +410,7 @@ class AddSCADTask:
except OpenSCADUtils.OpenSCADError as e:
self.form.textMsg.setPlainText(e.value)
FreeCAD.Console.PrintError(e.value)
def refreshelement(self):
self.form.textMsg.setPlainText('')
doc=FreeCAD.activeDocument()
@@ -428,7 +427,7 @@ class AddSCADTask:
with open(filename,'r') as fp :
data = fp.read()
self.form.textEdit.setText(data)
def saveelement(self) :
filename, filter = QtGui.QFileDialog.getSaveFileName(parent=self.form, caption='Open file', dir='.', filter='OpenSCAD Files (*.scad)',selectedFilter='',option=0)
@@ -436,7 +435,7 @@ class AddSCADTask:
Text = self.form.textEdit.toPlainText()
with open(filename,'w') as fp :
fp.write(Text)
class OpenSCADMeshBooleanWidget(QtGui.QWidget):
def __init__(self,*args):
QtGui.QWidget.__init__(self,*args)
@@ -513,8 +512,8 @@ class AddOpenSCADElement:
FreeCADGui.Control.showDialog(panel)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'),
return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'),
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement',
'Add an OpenSCAD element by entering OpenSCAD code and executing the OpenSCAD binary')}
@@ -528,8 +527,8 @@ class OpenSCADMeshBoolean:
FreeCADGui.Control.showDialog(panel)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_MeshBooleans',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'),
return {'Pixmap' : 'OpenSCAD_MeshBooleans',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'),
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean',
'Export objects as meshes and use OpenSCAD to perform a boolean operation')}
@@ -567,8 +566,8 @@ class Minkowski:
importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"minkowski")
FreeCAD.ActiveDocument.recompute()
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_Minkowski',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski'),
return {'Pixmap' : 'OpenSCAD_Minkowski',
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski'),
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Perform Minkowski')}
FreeCADGui.addCommand('OpenSCAD_ColorCodeShape',ColorCodeShape())

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - Parametric Features"
__title__ = "FreeCAD OpenSCAD Workbench - Parametric Features"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -32,13 +31,15 @@ except NameError:
'''
This Script includes python Features to represent OpenSCAD Operations
'''
class ViewProviderTree:
"A generic View Provider for Elements with Children"
def __init__(self, obj):
obj.Proxy = self
self.Object = obj.Object
def attach(self, obj):
self.Object = obj.Object
return
@@ -80,7 +81,7 @@ class ViewProviderTree:
objs.extend(self.Object.Children)
return objs
def getIcon(self):
import OpenSCAD_rc
if isinstance(self.Object.Proxy,RefineShape):
@@ -165,6 +166,7 @@ static char * openscadlogo_xpm[] = {
"4444444444444444"};
"""
class OpenSCADPlaceholder:
def __init__(self,obj,children=None,arguments=None):
obj.addProperty("App::PropertyLinkList",'Children','OpenSCAD',"Base Objects")
@@ -174,13 +176,14 @@ class OpenSCADPlaceholder:
obj.Children = children
if arguments:
obj.Arguments = arguments
def execute(self,fp):
import Part
fp.Shape = Part.Compound([]) #empty Shape
class Resize :
def __init__(self,obj,target,vector) :
class Resize:
def __init__(self,obj,target,vector):
import FreeCAD
#self.Obj = obj
self.Target = target
@@ -193,24 +196,24 @@ class Resize :
obj.Proxy = self
def onChanged(self, fp, prop):
if prop in ['Object','Vector'] :
if prop in ['Object','Vector']:
self.createGeometry(fp)
def execute(self, fp):
self.createGeometry(fp)
def createGeometry(self, fp) :
def createGeometry(self, fp):
import FreeCAD
mat = FreeCAD.Matrix()
mat.A11 = self.Vector[0]
mat.A22 = self.Vector[1]
mat.A33 = self.Vector[2]
fp.Shape = self.Target.Shape.transformGeometry(mat)
fp.Shape = self.Target.Shape.transformGeometry(mat)
def __getstate__(self):
return None
def __setstate__(self,state):
def __setstate__(self,state):
return None
@@ -234,15 +237,16 @@ class MatrixTransform:
def execute(self, fp):
if fp.Matrix and fp.Base:
sh=fp.Base.Shape#.copy()
m=sh.Placement.toMatrix().multiply(fp.Matrix)
sh = fp.Base.Shape#.copy()
m = sh.Placement.toMatrix().multiply(fp.Matrix)
fp.Shape = sh.transformGeometry(m)
#else:
#FreeCAD.Console.PrintMessage('base %s\nmat %s/n' % (fp.Base,fp.Matrix))
class ImportObject:
def __init__(self, obj,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
obj.addProperty("App::PropertyLink", "Base", "Base",
"The base object that must be tranfsformed")
obj.Proxy = self
obj.Base = child
@@ -256,10 +260,11 @@ class ImportObject:
# if fp.Base:
# fp.Shape = fp.Base.Shape.copy()
class RefineShape:
'''return a refined shape'''
def __init__(self, obj,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
def __init__(self, obj, child=None):
obj.addProperty("App::PropertyLink", "Base", "Base",
"The base object that must be refined")
obj.Proxy = self
obj.Base = child
@@ -271,14 +276,14 @@ class RefineShape:
def execute(self, fp):
if fp.Base and fp.Base.Shape.isValid():
import OpenSCADUtils
sh=fp.Base.Shape.removeSplitter()
fp.Shape=OpenSCADUtils.applyPlacement(sh)
sh = fp.Base.Shape.removeSplitter()
fp.Shape = OpenSCADUtils.applyPlacement(sh)
class IncreaseTolerance:
'''increase the tolerance of every vertex
in the current implementation its' placement is linked'''
def __init__(self,obj,child,tolerance=0):
obj.addProperty("App::PropertyLink","Base","Base",
obj.addProperty("App::PropertyLink", "Base", "Base",
"The base object that wire must be extracted")
obj.addProperty("App::PropertyDistance","Vertex","Tolerance","Vertexes tolerance (0 default)")
obj.addProperty("App::PropertyDistance","Edge","Tolerance","Edges tolerance (0 default)")
@@ -307,11 +312,11 @@ class IncreaseTolerance:
# New properties
else:
for vertex in sh.Vertexes:
vertex.Tolerance = max(vertex.Tolerance,fp.Vertex.Value)
vertex.Tolerance = max(vertex.Tolerance, fp.Vertex.Value)
for edge in sh.Edges:
edge.Tolerance = max(edge.Tolerance,fp.Edge.Value)
edge.Tolerance = max(edge.Tolerance, fp.Edge.Value)
for face in sh.Faces:
face.Tolerance = max(face.Tolerance,fp.Face.Value)
face.Tolerance = max(face.Tolerance, fp.Face.Value)
fp.Shape = sh
fp.Placement = sh.Placement
@@ -319,7 +324,7 @@ class IncreaseTolerance:
class GetWire:
'''return the first wire from a given shape'''
def __init__(self, obj,child=None):
def __init__(self, obj, child=None):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that wire must be extracted")
obj.Proxy = self
@@ -362,8 +367,8 @@ class Frustum:
import FreeCAD,Part
#from draftlibs import fcgeo
plm = fp.Placement
wires=[]
faces=[]
wires = []
faces = []
for ir,r in enumerate((fp.Radius1,fp.Radius2)):
angle = (math.pi*2)/fp.FacesNumber
pts = [FreeCAD.Vector(r.Value,0,ir*fp.Height.Value)]
@@ -374,13 +379,13 @@ class Frustum:
pts.append(pts[0])
shape = Part.makePolygon(pts)
face = Part.Face(shape)
if ir==0: #top face
if ir == 0: #top face
face.reverse()
wires.append(shape)
faces.append(face)
#shellperi=Part.makeRuledSurface(*wires)
shellperi=Part.makeLoft(wires)
shell=Part.Shell(shellperi.Faces+faces)
#shellperi = Part.makeRuledSurface(*wires)
shellperi = Part.makeLoft(wires)
shell = Part.Shell(shellperi.Faces+faces)
fp.Shape = Part.Solid(shell)
fp.Placement = plm
@@ -445,7 +450,7 @@ class Twist:
pipe_shell.build()
faces.extend(pipe_shell.shape().Faces)
try:
fullshell=Part.Shell(faces)
fullshell = Part.Shell(faces)
solid=Part.Solid(fullshell)
if solid.Volume < 0:
solid.reverse()
@@ -486,9 +491,9 @@ class PrismaticToroid:
sweep_angle_per_segment = fp.Angle / num_segments # Always >= min_sweep_angle_per_segment
# From the OpenSCAD documentation:
# The 2D shape must lie completely on either the right (recommended) or the left side of the Y-axis.
# More precisely speaking, every vertex of the shape must have either x >= 0 or x <= 0. If the shape
# spans the X axis a warning appears in the console windows and the rotate_extrude() is ignored. If
# The 2D shape must lie completely on either the right (recommended) or the left side of the Y-axis.
# More precisely speaking, every vertex of the shape must have either x >= 0 or x <= 0. If the shape
# spans the X axis a warning appears in the console windows and the rotate_extrude() is ignored. If
# the 2D shape touches the Y axis, i.e. at x=0, it must be a line that touches, not a point.
for start_face in fp.Base.Shape.Faces:
@@ -509,7 +514,7 @@ class PrismaticToroid:
edges.append(edge)
ribs.append(Part.Wire(edges))
faces = []
shell = Part.makeShellFromWires (ribs)
for face in shell.Faces:
@@ -520,7 +525,7 @@ class PrismaticToroid:
faces.append(start_face.reversed()) # Reversed so the normal faces out of the shell
faces.append(end_face)
else:
faces.append(start_face)
faces.append(start_face)
faces.append(end_face.reversed()) # Reversed so the normal faces out of the shell
try:

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
#* *
#* This program is free software; you can redistribute it and/or modify *

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -26,6 +25,7 @@ import unittest
import FreeCAD
import OpenSCAD
class TestDummy(unittest.TestCase):
MODULE = 'test_dummy' # file name without extension
@@ -35,4 +35,4 @@ class TestDummy(unittest.TestCase):
pass
def test_tests(self):
self.assertTrue(True)
self.assertTrue(True)

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - Utility Functions"
__title__ = "FreeCAD OpenSCAD Workbench - Utility Functions"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -29,7 +28,8 @@ This Script includes various python helper functions that are shared across
the module
'''
from exportCSG import mesh2polyhedron
import FreeCAD, io
import FreeCAD
import io
if FreeCAD.GuiUp:
try:
@@ -49,16 +49,20 @@ try:
except (ImportError, AttributeError):
BaseError = RuntimeError
class OpenSCADError(BaseError):
def __init__(self,value):
self.value= value
def __init__(self, value):
self.value = value
#def __repr__(self):
# return self.msg
def __str__(self):
return repr(self.value)
def getopenscadexe(osfilename=None):
import os,subprocess,time
import os
import subprocess
import time
if not osfilename:
import FreeCAD
osfilename = FreeCAD.ParamGet(\
@@ -68,8 +72,11 @@ def getopenscadexe(osfilename=None):
return osfilename
return searchforopenscadexe()
def searchforopenscadexe():
import os,sys,subprocess
import os
import sys
import subprocess
if sys.platform == 'win32':
testpaths = [os.path.join(os.environ.get('Programfiles(x86)','C:'),\
'OpenSCAD\\openscad.exe')]
@@ -84,19 +91,19 @@ def searchforopenscadexe():
b'POSIX path of (application file id "org.openscad.OpenSCAD"'
b'as alias)\n'
b'end tell')
p1=subprocess.Popen(['osascript','-'],stdin=subprocess.PIPE,\
p1=subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE,\
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
stdout,stderr = p1.communicate(ascript)
stdout, stderr = p1.communicate(ascript)
if p1.returncode == 0:
opathl = stdout.decode().split('\n') if sys.version_info.major >= 3 else stdout.split('\n')
if len(opathl) >=1:
if len(opathl) >= 1:
return opathl[0]+'Contents/MacOS/OpenSCAD'
#test the default path
testpath="/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
if os.path.isfile(testpath):
return testpath
else: #unix
p1=subprocess.Popen(['which','openscad'],stdout=subprocess.PIPE)
p1 = subprocess.Popen(['which','openscad'], stdout=subprocess.PIPE)
if p1.wait() == 0:
output = p1.stdout.read()
if sys.version_info.major >= 3:
@@ -104,59 +111,71 @@ def searchforopenscadexe():
opath = output.split('\n')[0]
return opath
def workaroundforissue128needed():
'''sets the import path depending on the OpenSCAD Version
for versions <= 2012.06.23 to the current working dir
for versions above to the inputfile dir
see https://github.com/openscad/openscad/issues/128'''
vdate=getopenscadversion().split('-')[0]
vdate=vdate.split(' ')[2].split('.')
vdate = getopenscadversion().split('-')[0]
vdate = vdate.split(' ')[2].split('.')
if len(vdate) == 1: # probably YYYYMMDD format (i.e. git version)
vdate = vdate[0]
year, mon = int("".join(vdate[0:4])), int("".join(vdate[4:6]))
else: # YYYY.MM(.DD?) (latest release)
year,mon=int(vdate[0]),int(vdate[1])
return (year<2012 or (year==2012 and (mon <6 or (mon == 6 and \
(len(vdate)<3 or int(vdate[2]) <=23)))))
year, mon = int(vdate[0]), int(vdate[1])
return (year < 2012 or (year == 2012 and (mon < 6 or (mon == 6 and \
(len(vdate) < 3 or int(vdate[2]) <= 23)))))
#ifdate=int(vdate[0])+(int(vdate[1])-1)/12.0
#if len(vdate)>2:
# fdate+=int((vdate[2])-1)/12.0/31.0
#return fdate < 2012.4759
def getopenscadversion(osfilename=None):
import os,subprocess,time
import os
import subprocess
import time
if not osfilename:
import FreeCAD
osfilename = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetString('openscadexecutable')
if osfilename and os.path.isfile(osfilename):
with subprocess.Popen([osfilename,'-v'],\
stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True) as p:
with subprocess.Popen([osfilename, '-v'],\
stdout = subprocess.PIPE,stderr=subprocess.PIPE, universal_newlines=True) as p:
p.wait()
stdout=p.stdout.read().strip()
stderr=p.stderr.read().strip()
stdout = p.stdout.read().strip()
stderr = p.stderr.read().strip()
return (stdout or stderr)
def newtempfilename():
import os,time
formatstr='fc-%05d-%06d-%06d'
import os
import time
formatstr = 'fc-%05d-%06d-%06d'
count = 0
while True:
count+=1
yield formatstr % (os.getpid(),int(time.time()*100) % 1000000,count)
count += 1
yield formatstr % (os.getpid(), int(time.time()*100) % 1000000, count)
tempfilenamegen=newtempfilename()
tempfilenamegen = newtempfilename()
def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=False):
def callopenscad(inputfilename,outputfilename=None, outputext='csg', keepname=False):
'''call the open scad binary
returns the filename of the result (or None),
please delete the file afterwards'''
import FreeCAD,os,subprocess,tempfile,time
def check_output2(*args,**kwargs):
import FreeCAD
import os
import subprocess
import tempfile
import time
def check_output2(*args, **kwargs):
kwargs.update({'stdout':subprocess.PIPE,'stderr':subprocess.PIPE})
p=subprocess.Popen(*args,**kwargs)
stdoutd,stderrd = p.communicate()
p = subprocess.Popen(*args, **kwargs)
stdoutd, stderrd = p.communicate()
stdoutd = stdoutd.decode("utf8")
stderrd = stderrd.decode("utf8")
if p.returncode != 0:
@@ -172,7 +191,7 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals
osfilename = preferences.GetString('openscadexecutable')
transferMechanism = preferences.GetInt('transfermechanism',0)
if transferMechanism == 0: # Use the Python temp-directory creation function
transferDirectory = tempfile.gettempdir()
transferDirectory = tempfile.gettempdir()
elif transferMechanism == 1: # Use a user-specified directory for the transfer
transferDirectory = preferences.GetString('transferdirectory')
elif transferMechanism == 2: # Use pipes instead of tempfiles
@@ -185,18 +204,19 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals
dir1 = transferDirectory
if keepname:
outputfilename=os.path.join(dir1,'%s.%s' % (os.path.split(\
outputfilename = os.path.join(dir1, '%s.%s' % (os.path.split(\
inputfilename)[1].rsplit('.',1)[0],outputext))
else:
outputfilename=os.path.join(dir1,'%s.%s' % \
outputfilename = os.path.join(dir1,'%s.%s' % \
(next(tempfilenamegen),outputext))
check_output2([osfilename,'-o',outputfilename, inputfilename])
check_output2([osfilename, '-o', outputfilename, inputfilename])
return outputfilename
else:
raise OpenSCADError('OpenSCAD executable unavailable')
def call_openscad_with_pipes(input_filename, output_filename, output_extension, keep_name):
''' Call OpenSCAD by sending input data to stdin, and read the output from stdout.
''' Call OpenSCAD by sending input data to stdin, and read the output from stdout.
Returns the tempfile the output is stored in on success, or None on failure.
NOTE: This feature was added to OpenSCAD in 2021.01'''
@@ -209,13 +229,13 @@ def call_openscad_with_pipes(input_filename, output_filename, output_extension,
# Load the data back in from our tempfile:
with open(input_filename) as datafile:
openscad_data = datafile.read()
# On the command line this looks like:
# On the command line this looks like:
# $ cat myfile.scad | openscad --export-format csg -o - -
preferences = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
openscad_executable = preferences.GetString('openscadexecutable')
p = subprocess.Popen([openscad_executable,"--export-format","csg", "-o", "-", "-"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
p = subprocess.Popen([openscad_executable,"--export-format","csg", "-o", "-", "-"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdoutd,stderrd = p.communicate (input = openscad_data.encode('utf8'), timeout=15)
stdoutd = stdoutd.decode("utf8")
@@ -235,26 +255,28 @@ def call_openscad_with_pipes(input_filename, output_filename, output_extension,
outfile.write(stdoutd);
return output_filename
return None
def callopenscadstring(scadstr,outputext='csg'):
'''create a tempfile and call the open scad binary
returns the filename of the result (or None),
please delete the file afterwards'''
import os,tempfile,time
dir1=tempfile.gettempdir()
inputfilename=os.path.join(dir1,'%s.scad' % next(tempfilenamegen))
dir1 = tempfile.gettempdir()
inputfilename = os.path.join(dir1,'%s.scad' % next(tempfilenamegen))
inputfile = io.open(inputfilename,'w', encoding="utf8")
inputfile.write(scadstr)
inputfile.close()
outputfilename = callopenscad(inputfilename,outputext=outputext,\
outputfilename = callopenscad(inputfilename, outputext=outputext,\
keepname=True)
os.unlink(inputfilename)
return outputfilename
def reverseimporttypes():
'''allows to search for supported filetypes by module'''
def getsetfromdict(dict1,index):
def getsetfromdict(dict1, index):
if index in dict1:
return dict1[index]
else:
@@ -262,7 +284,7 @@ def reverseimporttypes():
dict1[index]=set1
return set1
importtypes={}
importtypes = {}
import FreeCAD
for key,value in FreeCAD.getImportType().items():
if type(value) is str:
@@ -278,10 +300,11 @@ def fcsubmatrix(m):
as a list of row vectors"""
return [[m.A11,m.A12,m.A13],[m.A21,m.A22,m.A23],[m.A31,m.A32,m.A33]]
def multiplymat(l,r):
def multiplymat(l, r):
"""multiply matrices given as lists of row vectors"""
rt=zip(*r) #transpose r
rt=list(rt)
rt = zip(*r) #transpose r
rt = list(rt)
mat=[]
for y in range(len(rt)):
mline=[]
@@ -290,11 +313,13 @@ def multiplymat(l,r):
mat.append(mline)
return mat
def isorthogonal(submatrix,precision=4):
def isorthogonal(submatrix, precision=4):
"""checking if 3x3 Matrix is orthogonal (M*Transp(M)==I)"""
prod=multiplymat(submatrix,list(zip(*submatrix)))
prod = multiplymat(submatrix,list(zip(*submatrix)))
return [[round(f,precision) for f in line] \
for line in prod]==[[1,0,0],[0,1,0],[0,0,1]]
for line in prod] == [[1,0,0],[0,1,0],[0,0,1]]
def detsubmatrix(s):
"""get the determinant of a 3x3 Matrix given as list of row vectors"""
@@ -302,54 +327,58 @@ def detsubmatrix(s):
s[0][2]*s[1][0]*s[2][1]-s[2][0]*s[1][1]*s[0][2]-\
s[2][1]*s[1][2]*s[0][0]-s[2][2]*s[1][0]*s[0][1]
def isspecialorthogonalpython(submat,precision=4):
return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision)==1
def isspecialorthogonalpython(submat, precision=4):
return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision) == 1
def isrotoinversionpython(submat,precision=4):
return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision)==-1
def isrotoinversionpython(submat, precision=4):
return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision) == -1
def isspecialorthogonal(mat,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 decomposerotoinversion(m,precision=4):
def decomposerotoinversion(m, precision=4):
import FreeCAD
rmat = [[round(f,precision) for f in line] for line in fcsubmatrix(m)]
cmat = FreeCAD.Matrix()
if rmat ==[[-1,0,0],[0,1,0],[0,0,1]]:
if rmat == [[-1,0,0],[0,1,0],[0,0,1]]:
cmat.scale(-1,1,1)
return m*cmat,FreeCAD.Vector(1)
elif rmat ==[[1,0,0],[0,-1,0],[0,0,1]]:
elif rmat == [[1,0,0],[0,-1,0],[0,0,1]]:
cmat.scale(1,-1,1)
return m*cmat, FreeCAD.Vector(0,1)
elif rmat ==[[1,0,0],[0,1,0],[0,0,-1]]:
elif rmat == [[1,0,0],[0,1,0],[0,0,-1]]:
cmat.scale(1,1,-1)
return m*cmat, FreeCAD.Vector(0,0,1)
else:
cmat.scale(1,1,-1)
return m*cmat, FreeCAD.Vector(0,0,1)
def mirror2mat(nv,bv):
def mirror2mat(nv, bv):
import FreeCAD
"""calculate the transformation matrix of a mirror feature"""
mbef=FreeCAD.Matrix()
mbef = FreeCAD.Matrix()
mbef.move(bv * -1)
maft=FreeCAD.Matrix()
maft = FreeCAD.Matrix()
maft.move(bv)
return maft*vec2householder(nv)*mbef
def vec2householder(nv):
"""calculated the householder matrix for a given normal vector"""
import FreeCAD
lnv=nv.dot(nv)
l=2/lnv if lnv > 0 else 0
hh=FreeCAD.Matrix(nv.x*nv.x*l,nv.x*nv.y*l,nv.x*nv.z*l,0,\
lnv = nv.dot(nv)
l = 2/lnv if lnv > 0 else 0
hh = FreeCAD.Matrix(nv.x*nv.x*l,nv.x*nv.y*l,nv.x*nv.z*l,0,\
nv.y*nv.x*l,nv.y*nv.y*l,nv.y*nv.z*l,0,\
nv.z*nv.x*l,nv.z*nv.y*l,nv.z*nv.z*l,0,0,0,0,0)
return FreeCAD.Matrix()-hh
def mirrormesh(msh,vec):
def mirrormesh(msh, vec):
"""mirrormesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector"""
poly = mesh2polyhedron(msh)
vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']'
@@ -358,7 +387,8 @@ def mirrormesh(msh,vec):
mi.flipNormals()
return mi
def scalemesh(msh,vec):
def scalemesh(msh, vec):
"""scalemesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector"""
poly = mesh2polyhedron(msh)
vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']'
@@ -367,7 +397,8 @@ def scalemesh(msh,vec):
mi.flipNormals()
return mi
def resizemesh(msh,vec):
def resizemesh(msh, vec):
"""resizemesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector"""
poly = mesh2polyhedron(msh)
vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']'
@@ -376,9 +407,11 @@ def resizemesh(msh,vec):
mi.flipNormals()
return mi
def angneg(d):
return d if (d <= 180.0) else (d-360)
def shorthexfloat(f):
mantisse, exponent = f.hex().split('p',1)
return '%sp%s' % (mantisse.rstrip('0'),exponent)
@@ -388,7 +421,7 @@ def comparerotations(r1,r2):
import FreeCAD
'''compares two rotations
a value of zero means that they are identical'''
r2c=FreeCAD.Rotation(r2)
r2c = FreeCAD.Rotation(r2)
r2c.invert()
return r1.multiply(r2c).Angle
@@ -438,10 +471,11 @@ def findbestmatchingrotation(r1):
-2.0, -1.0)
def tup2nvect(tup):
"""convert a tuple to a normalized vector"""
v=FreeCAD.Vector(*tup)
v = FreeCAD.Vector(*tup)
v.normalize()
return v
def wkaxes():
"""well known axes for rotations"""
vtupl=((1,0,0),(0,1,0),(0,0,1),
@@ -449,34 +483,35 @@ def findbestmatchingrotation(r1):
(1,1,1),(1,1,-1),(1,-1,1),(-1,1,1))
return tuple(tup2nvect(tup) for tup in vtupl)
bestrot=FreeCAD.Rotation()
dangle = comparerotations(r1,bestrot)
bestrot = FreeCAD.Rotation()
dangle = comparerotations(r1, bestrot)
for axis in wkaxes():
for angle in vangl:
for axissign in (1.0,-1.0):
r2=FreeCAD.Rotation(axis*axissign,angle)
r2=FreeCAD.Rotation(axis*axissign, angle)
dangletest = comparerotations(r1,r2)
if dangletest < dangle:
bestrot = r2
dangle = dangletest
return (bestrot,dangle)
def roundrotation(rot,maxangulardistance=1e-5):
def roundrotation(rot, maxangulardistance=1e-5):
'''guess the rotation axis and angle for a rotation
recreated from rounded floating point values
(from a quaterion or transformation matrix)'''
def teststandardrot(r1,maxangulardistance=1e-5):
def teststandardrot(r1, maxangulardistance=1e-5):
'''test a few common rotations beforehand'''
import FreeCAD,itertools
eulers = []
for angle in (90,-90,180,45,-45,135,-135):
for euler in itertools.permutations((0,0,angle)):
eulers.append(euler)
for euler in itertools.product((0,45,90,135,180,-45,-90,-135),repeat=3):
for euler in itertools.product((0,45,90,135,180,-45,-90,-135), repeat=3):
eulers.append(euler)
for euler in eulers:
r2 = FreeCAD.Rotation(*euler)
if comparerotations(r1,r2) < maxangulardistance:
if comparerotations(r1, r2) < maxangulardistance:
return r2
if rot.isNull():
@@ -491,11 +526,12 @@ def roundrotation(rot,maxangulardistance=1e-5):
else: #use original
return rot
def callopenscadmeshstring(scadstr):
"""Call OpenSCAD and return the result as a Mesh"""
import Mesh,os
tmpfilename=callopenscadstring(scadstr,'stl')
newmesh=Mesh.Mesh()
tmpfilename = callopenscadstring(scadstr, 'stl')
newmesh = Mesh.Mesh()
newmesh.read(tmpfilename)
try:
os.unlink(tmpfilename)
@@ -503,7 +539,8 @@ def callopenscadmeshstring(scadstr):
pass
return newmesh
def meshopinline(opname,iterable1):
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
@@ -513,17 +550,19 @@ def meshopinline(opname,iterable1):
return callopenscadmeshstring('%s(){%s}' % (opname,' '.join(\
(mesh2polyhedron(meshobj) for meshobj in iterable1))))
def meshoptempfile(opname,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()
import os
import tempfile
dir1 = tempfile.gettempdir()
filenames = []
for mesh in iterable1:
outputfilename=os.path.join(dir1,'%s.stl' % next(tempfilenamegen))
outputfilename = os.path.join(dir1, '%s.stl' % next(tempfilenamegen))
mesh.write(outputfilename)
filenames.append(outputfilename)
#absolute path causes error. We rely that the scad file will be in the dame tmpdir
@@ -538,14 +577,15 @@ def meshoptempfile(opname,iterable1):
pass
return result
def meshoponobjs(opname,inobjs):
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=[]
"""
objs = []
meshes = []
for obj in inobjs:
if obj.isDerivedFrom('Mesh::Feature'):
objs.append(obj)
@@ -572,19 +612,22 @@ def meshoponobjs(opname,inobjs):
else:
return (None,[])
def process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc):
import FreeCAD,importDXF
import os,tempfile
def process2D_ObjectsViaOpenSCADShape(ObjList, Operation, doc):
import FreeCAD
import importDXF
import os
import tempfile
# https://www.freecadweb.org/tracker/view.php?id=3419
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
fn = params.GetInt('fnForImport',32)
fnStr = ",$fn=" + str(fn)
#
dir1=tempfile.gettempdir()
dir1 = tempfile.gettempdir()
filenames = []
for item in ObjList :
outputfilename=os.path.join(dir1,'%s.dxf' % next(tempfilenamegen))
importDXF.export([item],outputfilename,True,True)
importDXF.export([item],outputfilename, True, True)
filenames.append(outputfilename)
# https://www.freecadweb.org/tracker/view.php?id=3419
dxfimports = ' '.join("import(file = \"%s\" %s);" % \
@@ -603,7 +646,8 @@ def process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc):
pass
return face
def process2D_ObjectsViaOpenSCAD(ObjList,Operation,doc=None):
def process2D_ObjectsViaOpenSCAD(ObjList, Operation, doc=None):
import FreeCAD
doc = doc or FreeCAD.activeDocument()
face=process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc)
@@ -615,8 +659,11 @@ def process2D_ObjectsViaOpenSCAD(ObjList,Operation,doc=None):
index.ViewObject.hide()
return(obj)
def process3D_ObjectsViaOpenSCADShape(ObjList,Operation,maxmeshpoints=None):
import FreeCAD,Mesh,Part
def process3D_ObjectsViaOpenSCADShape(ObjList, Operation, maxmeshpoints=None):
import FreeCAD
import Mesh
import Part
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
if False: # disabled due to issue 1292
import MeshPart
@@ -628,27 +675,29 @@ def process3D_ObjectsViaOpenSCADShape(ObjList,Operation,maxmeshpoints=None):
meshes = [Mesh.Mesh(obj.Shape.tessellate(params.GetFloat(\
'meshmaxlength',1.0))) for obj in ObjList]
if max(mesh.CountPoints for mesh in meshes) < \
(maxmeshpoints or params.GetInt('tempmeshmaxpoints',5000)):
(maxmeshpoints or params.GetInt('tempmeshmaxpoints', 5000)):
stlmesh = meshoptempfile(Operation,meshes)
sh=Part.Shape()
sh.makeShapeFromMesh(stlmesh.Topology,0.1)
sh = Part.Shape()
sh.makeShapeFromMesh(stlmesh.Topology, 0.1)
solid = Part.Solid(sh)
solid=solid.removeSplitter()
solid = solid.removeSplitter()
if solid.Volume < 0:
solid.complement()
return solid
def process3D_ObjectsViaOpenSCAD(doc,ObjList,Operation):
def process3D_ObjectsViaOpenSCAD(doc,ObjList, Operation):
solid = process3D_ObjectsViaOpenSCADShape(ObjList,Operation)
if solid is not None:
obj=doc.addObject('Part::Feature',Operation) #non-parametric object
obj = doc.addObject('Part::Feature',Operation) #non-parametric object
obj.Shape=solid#.removeSplitter()
if FreeCAD.GuiUp:
for index in ObjList :
for index in ObjList:
index.ViewObject.hide()
return(obj)
def process_ObjectsViaOpenSCADShape(doc,children,name,maxmeshpoints=None):
def process_ObjectsViaOpenSCADShape(doc, children, name, maxmeshpoints=None):
if all((not obj.Shape.isNull() and obj.Shape.Volume == 0) \
for obj in children):
return process2D_ObjectsViaOpenSCADShape(children,name,doc)
@@ -672,17 +721,18 @@ def process_ObjectsViaOpenSCAD(doc,children,name):
FreeCAD.Console.PrintError( translate('OpenSCAD',\
"Error all shapes must be either 2D or both must be 3D")+u'\n')
def removesubtree(objs):
def addsubobjs(obj,toremoveset):
def addsubobjs(obj, toremoveset):
toremove.add(obj)
for subobj in obj.OutList:
addsubobjs(subobj,toremoveset)
addsubobjs(subobj, toremoveset)
import FreeCAD
toremove=set()
toremove = set()
for obj in objs:
addsubobjs(obj,toremove)
checkinlistcomplete =False
addsubobjs(obj, toremove)
checkinlistcomplete = False
while not checkinlistcomplete:
for obj in toremove:
if (obj not in objs) and (frozenset(obj.InList) - toremove):
@@ -693,6 +743,7 @@ def removesubtree(objs):
for obj in toremove:
obj.Document.removeObject(obj.Name)
def applyPlacement(shape):
if shape.Placement.isNull():
return shape

View File

@@ -22,7 +22,7 @@
# ***************************************************************************
# Gui Unit tests for the FEM module
from OpenSCADTest.gui.test_dummy import TestDummy as OpenSCADGuiTestDummy
from OpenSCADTest.gui.test_dummy import TestDummy as OpenSCADGuiTestDummy
# dummy usage to get flake8 and lgtm quiet

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - 2D helper functions"
__title__ = "FreeCAD OpenSCAD Workbench - 2D helper functions"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -29,20 +28,23 @@ This Script includes python functions to find out the most basic shape type
in a compound and to change the color of shapes according to their shape type
'''
def shapedict(shapelst):
return dict([(shape.hashCode(),shape) for shape in shapelst])
def shapeset(shapelst):
return set([shape.hashCode() for shape in shapelst])
def mostbasiccompound(comp):
'''searches for the most basic shape in a Compound'''
solids=shapeset(comp.Solids)
shells=shapeset(comp.Shells)
faces=shapeset(comp.Faces)
wires=shapeset(comp.Wires)
edges=shapeset(comp.Edges)
vertexes=shapeset(comp.Vertexes)
solids = shapeset(comp.Solids)
shells = shapeset(comp.Shells)
faces = shapeset(comp.Faces)
wires = shapeset(comp.Wires)
edges = shapeset(comp.Edges)
vertexes = shapeset(comp.Vertexes)
#FreeCAD.Console.PrintMessage('%s\n' % (str((len(solids),len(shells),len(faces),len(wires),len(edges),len(vertexes)))))
for shape in comp.Solids:
shells -= shapeset(shape.Shells)
@@ -80,7 +82,7 @@ def mostbasiccompound(comp):
return "Solid"
def colorcodeshapes(objs):
shapecolors={
shapecolors = {
"Compound":(0.3,0.3,0.4),
"CompSolid":(0.1,0.5,0.0),
"Solid":(0.0,0.8,0.0),
@@ -93,17 +95,17 @@ def colorcodeshapes(objs):
None:(0.0,0.0,0.0)}
for obj in objs:
if hasattr(obj,'Shape'):
if hasattr(obj, 'Shape'):
try:
if obj.Shape.isNull():
continue
if not obj.Shape.isValid():
color=(1.0,0.4,0.4)
color = (1.0,0.4,0.4)
else:
st=obj.Shape.ShapeType
if st in ["Compound","CompSolid"]:
if st in ["Compound", "CompSolid"]:
st = mostbasiccompound(obj.Shape)
color=shapecolors[st]
color = shapecolors[st]
obj.ViewObject.ShapeColor = color
except Exception:
raise

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - expand placements and matrices functions"
__title__ = "FreeCAD OpenSCAD Workbench - expand placements and matrices functions"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -34,6 +33,7 @@ from OpenSCADFeatures import *
from OpenSCADUtils import isspecialorthogonal
import replaceobj
def likeprimitive(obj,extrusion=False):
'''we can't push the matrix transformation further down'''
return not obj.OutList or obj.isDerivedFrom('Part::Extrusion')\
@@ -41,33 +41,34 @@ def likeprimitive(obj,extrusion=False):
or obj.isDerivedFrom('Part::FeaturePython')) or \
not obj.isDerivedFrom('Part::Feature')
def expandplacementsmatrix(obj,matrix):
'''expand afine transformation down the feature tree'''
ownmatrix=matrix.multiply(obj.Placement.toMatrix())
ownmatrix = matrix.multiply(obj.Placement.toMatrix())
if obj.isDerivedFrom('Part::Feature') and \
isinstance(obj.Proxy,MatrixTransform):
innermatrix=ownmatrix.multiply(obj.Matrix)
isinstance(obj.Proxy, MatrixTransform):
innermatrix = ownmatrix.multiply(obj.Matrix)
if likeprimitive(obj.Base,True): #this matrix is needed
obj.Placement=FreeCAD.Placement()
obj.Placement = FreeCAD.Placement()
obj.Matrix = innermatrix
else: #the inner object is not a primitive
expandplacementsmatrix(obj.Base,innermatrix)
else: #the inner object is not a primitive
expandplacementsmatrix(obj.Base, innermatrix)
#remove the matrix object
for parent in obj.Base.InList:
replaceobj.replaceobj(parent,obj,obj.Base)
replaceobj.replaceobj(parent, obj, obj.Base)
out.Document.removeObject(obj.Name)
elif likeprimitive(obj,True):
elif likeprimitive(obj, True):
#if isspecialorthogonalpython(fcsubmatrix(ownmatrix)):
if isspecialorthogonal(ownmatrix):
obj.Placement=FreeCAD.Placement()
obj.Placement = FreeCAD.Placement()
#this should never happen unless matrices cancel out
obj.Placement=FreeCAD.Placement(ownmatrix)
obj.Placement = FreeCAD.Placement(ownmatrix)
else:
newobj=doc.addObject("Part::FeaturePython",'exp_trans')
newobj = doc.addObject("Part::FeaturePython", 'exp_trans')
MatrixTransform(newobj,ownmatrix,obj) #This object is not mutable GUI
ViewProviderTree(newobj.ViewObject)
for parent in obj.InList:
replaceobj.replaceobj(parent,obj,newobj) # register the new object in the feature tree
replaceobj.replaceobj(parent, obj, newobj) # register the new object in the feature tree
obj.Placement=FreeCAD.Placement()
else: #not a primitive
for outobj in obj.OutList:
@@ -79,38 +80,38 @@ def expandplacementsmatrix(obj,matrix):
outobj.Matrix = newmatrix
outobj.Base.Placement=FreeCAD.Placement()
else: #remove the MatrixTransformation
plainobj=outobj.Base
plainobj = outobj.Base
for parent in outobj.InList:
replaceobj.replaceobj(parent,outobj,plainobj)
replaceobj.replaceobj(parent, outobj, plainobj)
outobj.Document.removeObject(outobj.Name)
expandplacementsmatrix(outobj,newmatrix)
else:
expandplacementsmatrix(outobj,ownmatrix)
obj.Placement=FreeCAD.Placement()
expandplacementsmatrix(outobj, ownmatrix)
obj.Placement = FreeCAD.Placement()
def expandplacements(obj,placement):
ownplacement=placement.multiply(obj.Placement)
if obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,MatrixTransform):
ownplacement = placement.multiply(obj.Placement)
if obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy, MatrixTransform):
#expandplacementsmatrix(obj,ownplacement.toMatrix())
expandplacementsmatrix(obj,placement.toMatrix())
elif likeprimitive(obj,False):
obj.Placement=ownplacement
elif likeprimitive(obj, False):
obj.Placement = ownplacement
elif obj.isDerivedFrom('Part::Mirroring'):
import OpenSCADUtils
mm = OpenSCADUtils.mirror2mat(obj.Normal,obj.Base)
#TODO: set the base to 0,0,0
innerp=FreeCAD.Placement(mm * ownplacement.toMatrix() *mm)
expandplacements(obj.Source,innerp)
obj.Placement=FreeCAD.Placement()
innerp = FreeCAD.Placement(mm * ownplacement.toMatrix() *mm)
expandplacements(obj.Source, innerp)
obj.Placement = FreeCAD.Placement()
else:
for outobj in obj.OutList:
if obj.isDerivedFrom('Part::Extrusion'):
obj.Dir=ownplacement.Rotation.multVec(obj.Dir)
obj.Dir = ownplacement.Rotation.multVec(obj.Dir)
elif obj.isDerivedFrom('Part::Revolution'):
obj.Axis=ownplacement.Rotation.multVec(obj.Axis)
obj.Axis = ownplacement.Rotation.multVec(obj.Axis)
#obj.Base=ownplacement.Rotation.multVec(obj.Base)
expandplacements(outobj,ownplacement)
obj.Placement=FreeCAD.Placement()
expandplacements(outobj, ownplacement)
obj.Placement = FreeCAD.Placement()
#expandplacements(rootobj,FreeCAD.Placement())

View File

@@ -1,6 +1,5 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -18,14 +17,14 @@
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* *
#* Acknowledgements : *
#* *
#* Thanks to shoogen on the FreeCAD forum for programming advice *
#* and some code. *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG exporter Version"
__title__ = "FreeCAD OpenSCAD Workbench - CSG exporter Version"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/Export/Export.html"]
@@ -41,10 +40,10 @@ else:
#fafs = '$fa = 12, $fs = 2'
#convexity = 'convexity = 10'
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
fa = params.GetFloat('exportFa',12.0)
fs = params.GetFloat('exportFs',2.0)
conv = params.GetInt('exportConvexity',10)
fafs = '$fa = %f, $fs = %f' % (fa,fs)
fa = params.GetFloat('exportFa', 12.0)
fs = params.GetFloat('exportFs', 2.0)
conv = params.GetInt('exportConvexity', 10)
fafs = '$fa = %f, $fs = %f' % (fa, fs)
convexity = 'convexity = %d' % conv
#***************************************************************************
# Radius values not fixed for value apart from cylinder & Cone
@@ -59,7 +58,8 @@ def center(b):
else:
return 'false'
def check_multmatrix(csg,ob,x,y,z):
def check_multmatrix(csg, ob, x, y, z):
b = FreeCAD.Vector(x,y,z)
if ob.Placement.isNull():
return 0 # center = false no mm
@@ -76,68 +76,72 @@ def check_multmatrix(csg,ob,x,y,z):
0, 0, 0, 1]]){\n")
return 1 # center = false and mm
def mesh2polyhedron(mesh):
pointstr=','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]])
trianglestr=','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]])
pointstr = ','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]])
trianglestr = ','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]])
#avoid deprecation warning by changing triangles to faces
#return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr,trianglestr)
return 'polyhedron ( points = [%s], faces = [%s]);' % (pointstr,trianglestr)
#return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr, trianglestr)
return 'polyhedron ( points = [%s], faces = [%s]);' % (pointstr, trianglestr)
def vector2d(v):
return [v[0],v[1]]
def vertexs2polygon(vertex):
pointstr=','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex])
pointstr = ','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex])
return 'polygon ( points = [%s], paths = undef, convexity = 1);}' % pointstr
def shape2polyhedron(shape):
import MeshPart
return mesh2polyhedron(MeshPart.meshFromShape(Shape=shape,\
Deflection= params.GetFloat('meshdeflection',0.0)))
Deflection = params.GetFloat('meshdeflection', 0.0)))
def process_object(csg,ob):
print("Placement")
print("Pos : "+str(ob.Placement.Base))
print("axis : "+str(ob.Placement.Rotation.Axis))
print("angle : "+str(ob.Placement.Rotation.Angle))
if ob.TypeId == "Part::Sphere" :
print("Sphere Radius : "+str(ob.Radius))
check_multmatrix(csg,ob,0,0,0)
csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n")
elif ob.TypeId == "Part::Box" :
print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")")
mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2)
csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.TypeId == "Part::Cylinder" :
print("cylinder : Height "+str(ob.Height)+ " Radius "+str(ob.Radius))
mm = check_multmatrix(csg,ob,0,0,-ob.Height/2)
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius.Value)+\
if ob.TypeId == "Part::Sphere":
print("Sphere Radius : "+str(ob.Radius))
check_multmatrix(csg, ob, 0, 0, 0)
csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n")
elif ob.TypeId == "Part::Box":
print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")")
mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2)
csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.TypeId == "Part::Cylinder":
print("cylinder : Height "+str(ob.Height) + " Radius "+str(ob.Radius))
mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2)
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value) + ", r1 = "+str(ob.Radius.Value)+\
", r2 = " + str(ob.Radius.Value) + ", center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.TypeId == "Part::Cone" :
print("cone : Height "+str(ob.Height)+ " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2))
mm = check_multmatrix(csg,ob,0,0,-ob.Height/2)
if mm == 1 : csg.write("}\n")
elif ob.TypeId == "Part::Cone":
print("cone : Height "+str(ob.Height) + " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2))
mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2)
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius1.Value)+\
", r2 = "+str(ob.Radius2.Value)+", center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.TypeId == "Part::Torus" :
elif ob.TypeId == "Part::Torus":
print("Torus")
print(ob.Radius1)
print(ob.Radius2)
if ob.Angle3 == 360.00 :
mm = check_multmatrix(csg,ob,0,0,0)
if ob.Angle3 == 360.00:
mm = check_multmatrix(csg, ob, 0, 0, 0)
csg.write("rotate_extrude("+convexity+", $fn = 0, "+fafs+")\n")
csg.write("multmatrix([[1, 0, 0, "+str(ob.Radius1)+"], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])\n")
csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n")
csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n")
if mm == 1 : csg.write("}\n")
else : # Cannot convert to rotate extrude so best effort is polyhedron
else: # Cannot convert to rotate extrude so best effort is polyhedron
csg.write('%s\n' % shape2polyhedron(ob.Shape))
elif ob.TypeId == "Part::Prism":
@@ -146,24 +150,24 @@ def process_object(csg,ob):
# r = str(ob.Length/2.0/math.sin(math.pi/ob.Polygon))
r = str(ob.Circumradius) # length seems to be the outer radius
h = str(ob.Height.Value)
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2)
csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\
", r2 = "+r+", center = "+center(mm)+");\n")
if mm == 1: csg.write("}\n")
elif ob.TypeId == "Part::RegularPolygon":
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2)
csg.write("circle($fn = "+str(ob.NumberOfSides)+", "+fafs+", r = "+str(ob.Radius)+");\n")
if mm == 1: csg.write("}\n")
elif ob.TypeId == "Part::Extrusion" :
elif ob.TypeId == "Part::Extrusion":
print("Extrusion")
print(ob.Base)
print(ob.Base.Name)
if ob.Base.isDerivedFrom('Part::Part2DObjectPython') and \
hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy,'TypeId'):
ptype=ob.Base.Proxy.TypeId
if ptype == "Polygon" :
hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy, 'TypeId'):
ptype = ob.Base.Proxy.TypeId
if ptype == "Polygon":
f = str(ob.Base.FacesNumber)
r = str(ob.Base.Radius)
h = str(ob.Dir[2])
@@ -175,7 +179,7 @@ def process_object(csg,ob):
", r2 = "+r+", center = "+center(mm)+");\n")
if mm == 1: csg.write("}\n")
elif ptype == "Circle" :
elif ptype == "Circle":
r = str(ob.Base.Radius)
h = str(ob.Dir[2])
print("Radius : " + r)
@@ -185,10 +189,10 @@ def process_object(csg,ob):
", r2 = "+r+", center = "+center(mm)+");\n")
if mm == 1: csg.write("}\n")
elif ptype == "Wire" :
elif ptype == "Wire":
print("Wire extrusion")
print(ob.Base)
mm = check_multmatrix(csg,ob,0,0,0)
mm = check_multmatrix(csg, ob, 0, 0, 0)
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = "+center(mm)+", "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
csg.write(vertexs2polygon(ob.Base.Shape.Vertexes))
if mm == 1: csg.write("}\n")
@@ -197,76 +201,76 @@ def process_object(csg,ob):
mm = check_multmatrix(csg,ob,0,0,0)
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = true, "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
csg.write("square (size = ["+str(ob.Base.Length.Value)+", "+str(ob.Base.Width.Value)+"], center = "+center(mm)+");\n}\n")
if mm == 1: csg.write("}\n")
if mm == 1: csg.write("}\n")
elif ob.Base.Name.startswith('this_is_a_bad_idea'):
pass
else:
pass # There should be a fallback solution
elif ob.TypeId == "Part::Cut" :
elif ob.TypeId == "Part::Cut":
print("Cut")
csg.write("difference() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.TypeId == "Part::Fuse" :
elif ob.TypeId == "Part::Fuse":
print("union")
csg.write("union() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.TypeId == "Part::Common" :
elif ob.TypeId == "Part::Common":
print("intersection")
csg.write("intersection() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.TypeId == "Part::MultiFuse" :
elif ob.TypeId == "Part::MultiFuse":
print("Multi Fuse / union")
csg.write("union() {\n")
for subobj in ob.Shapes:
process_object(csg,subobj)
csg.write("}\n")
elif ob.TypeId == "Part::MultiCommon" :
elif ob.TypeId == "Part::MultiCommon":
print("Multi Common / intersection")
csg.write("intersection() {\n")
for subobj in ob.Shapes:
process_object(csg,subobj)
csg.write("}\n")
elif ob.isDerivedFrom('Part::Feature') :
elif ob.isDerivedFrom('Part::Feature'):
print("Part::Feature")
mm = check_multmatrix(csg,ob,0,0,0)
csg.write('%s\n' % shape2polyhedron(ob.Shape))
if mm == 1 : csg.write("}\n")
def export(exportList,filename):
def export(exportList, filename):
"called when FreeCAD exports a file"
# process Objects
print("\nStart Export 0.1d\n")
print("Open Output File")
csg = pythonopen(filename,'w')
print("Write Initial Output")
# Not sure if comments as per scad are allowed in csg file
# Not sure if comments as per scad are allowed in csg file
csg.write("// CSG file generated from FreeCAD %s\n" % \
'.'.join(FreeCAD.Version()[0:3]))
#write initial group statements - not sure if required
#write initial group statements - not sure if required
csg.write("group() {\n group(){\n")
for ob in exportList:
print(ob)
print("Name : "+ob.Name)
print("Type : "+ob.TypeId)
print("Name : " + ob.Name)
print("Type : " + ob.TypeId)
print("Shape : ")
print(ob.Shape)
process_object(csg,ob)
process_object(csg, ob)
# write closing group braces
csg.write("}\n}\n")
# close file
# close file
csg.close()
FreeCAD.Console.PrintMessage("successfully exported" + " " + filename)

View File

@@ -18,21 +18,23 @@
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Acknowledgements : *
#* *
#* Acknowledgements: *
#* *
#* Thanks to shoogen on the FreeCAD forum and Peter Li *
#* for programming advice and some code. *
#* *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG importer"
__title__ = "FreeCAD OpenSCAD Workbench - CSG importer"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/ImportCSG"]
printverbose = False
import FreeCAD, io, os
import FreeCAD
import io
import os
import ply.lex as lex
import ply.yacc as yacc
@@ -42,7 +44,7 @@ from OpenSCADFeatures import *
from OpenSCADUtils import *
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
printverbose = params.GetBool('printVerbose',False)
printverbose = params.GetBool('printVerbose', False)
if FreeCAD.GuiUp:
gui = True
@@ -50,14 +52,15 @@ else:
if printverbose: print("FreeCAD Gui not present.")
gui = False
hassetcolor=[]
alreadyhidden=[]
hassetcolor = []
alreadyhidden = []
original_root_objects = []
# Get the token map from the lexer. This is required.
# Get the token map from the lexer. This is required.
import tokrules
from tokrules import tokens
def shallHide(subject):
for obj in subject.OutListRecursive:
if "Matrix_Union" in str(obj.FullName):
@@ -66,6 +69,7 @@ def shallHide(subject):
return True
return False
def setColorRecursively(obj, color, transp):
'''
For some reason a part made by cutting or fusing other parts do not have a color
@@ -83,16 +87,18 @@ def setColorRecursively(obj, color, transp):
if currentObject not in hassetcolor:
setColorRecursively(currentObject, color, transp)
def fixVisibility():
# After an import, only the remaining root objects that we created should be visible, not any
# After an import, only the remaining root objects that we created should be visible, not any
# of their individual component objects. But make sure to only handle the ones we just imported,
# not anything that already existed. And objects that exist at the toplevel without any
# children are ignored.
for root_object in FreeCAD.ActiveDocument.RootObjects:
if root_object not in original_root_objects:
root_object.ViewObject.Visibility=True
root_object.ViewObject.Visibility = True
for obj in root_object.OutListRecursive:
obj.ViewObject.Visibility=False
obj.ViewObject.Visibility = False
if gui:
try:
@@ -107,6 +113,7 @@ if gui:
from PySide import QtGui
return QtGui.QApplication.translate(context, text, None)
def open(filename):
"called when freecad opens a file."
global doc
@@ -114,10 +121,10 @@ def open(filename):
docname = os.path.splitext(os.path.basename(filename))[0]
doc = FreeCAD.newDocument(docname)
if filename.lower().endswith('.scad'):
tmpfile=callopenscad(filename)
tmpfile = callopenscad(filename)
if workaroundforissue128needed():
pathName = '' #https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128
pathName = '' # https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() # https://github.com/openscad/openscad/issues/128
else:
pathName = os.path.dirname(os.path.normpath(filename))
processcsg(tmpfile)
@@ -130,23 +137,24 @@ def open(filename):
processcsg(filename)
return doc
def insert(filename,docname):
def insert(filename, docname):
"called when freecad imports a file"
global doc
global pathName
groupname_unused = os.path.splitext(os.path.basename(filename))[0]
try:
doc=FreeCAD.getDocument(docname)
doc = FreeCAD.getDocument(docname)
for obj in doc.RootObjects:
original_root_objects.append(obj)
except NameError:
doc=FreeCAD.newDocument(docname)
doc = FreeCAD.newDocument(docname)
#importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
if filename.lower().endswith('.scad'):
tmpfile=callopenscad(filename)
tmpfile = callopenscad(filename)
if workaroundforissue128needed():
pathName = '' #https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128
pathName = '' # https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() # https://github.com/openscad/openscad/issues/128
else:
pathName = os.path.dirname(os.path.normpath(filename))
processcsg(tmpfile)
@@ -161,13 +169,13 @@ def insert(filename,docname):
def processcsg(filename):
global doc
if printverbose: print ('ImportCSG Version 0.6a')
if printverbose: print('ImportCSG Version 0.6a')
# Build the lexer
if printverbose: print('Start Lex')
lex.lex(module=tokrules)
if printverbose: print('End Lex')
# Build the parser
# Build the parser
if printverbose: print('Load Parser')
# No debug out otherwise Linux has protection exception
parser = yacc.yacc(debug=False)
@@ -191,6 +199,7 @@ def processcsg(filename):
FreeCAD.Console.PrintMessage('End processing CSG file\n')
doc.recompute()
def p_block_list_(p):
'''
block_list : statement
@@ -200,34 +209,37 @@ def p_block_list_(p):
'''
#if printverbose: print("Block List")
#if printverbose: print(p[1])
if(len(p) > 2) :
if(len(p) > 2):
if printverbose: print(p[2])
p[0] = p[1] + p[2]
else :
else:
p[0] = p[1]
#if printverbose: print("End Block List")
def p_render_action(p):
'render_action : render LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
if printverbose: print("Render (ignored)")
p[0] = p[6]
def p_group_action1(p):
'group_action1 : group LPAREN RPAREN OBRACE block_list EBRACE'
if printverbose: print("Group")
# Test if need for implicit fuse
if (len(p[5]) > 1) :
p[0] = [fuse(p[5],"Group")]
else :
if (len(p[5]) > 1):
p[0] = [fuse(p[5], "Group")]
else:
p[0] = p[5]
def p_group_action2(p) :
def p_group_action2(p):
'group_action2 : group LPAREN RPAREN SEMICOL'
if printverbose: print("Group2")
p[0] = []
def p_boolean(p) :
def p_boolean(p):
'''
boolean : true
| false
@@ -238,10 +250,12 @@ def p_boolean(p) :
# 'string : QUOTE ID QUOTE'
# p[0] = p[2]
def p_stripped_string(p):
'stripped_string : STRING'
p[0] = p[1].strip('"')
def p_statement(p):
'''statement : part
| operation
@@ -254,26 +268,29 @@ def p_statement(p):
'''
p[0] = p[1]
def p_anymodifier(p):
'''anymodifier : MODIFIERBACK
| MODIFIERDEBUG
| MODIFIERROOT
| MODIFIERDISABLE
'''
#just return the plain modifier for now
#has to be changed when the modifiers are implemented
#please note that disabled objects usually are stripped of the CSG output during compilation
# just return the plain modifier for now
# has to be changed when the modifiers are implemented
# please note that disabled objects usually are stripped of the CSG output during compilation
p[0] = p[1]
def p_statementwithmod(p):
'''statementwithmod : anymodifier statement'''
#ignore the modifiers but add them to the label
# ignore the modifiers but add them to the label
modifier = p[1]
obj = p[2]
if hasattr(obj,'Label'):
if hasattr(obj, 'Label'):
obj.Label = modifier + obj.Label
p[0] = obj
def p_part(p):
'''
part : sphere_action
@@ -288,11 +305,13 @@ def p_part(p):
'''
p[0] = p[1]
def p_2d_point(p):
'2d_point : OSQUARE NUMBER COMMA NUMBER ESQUARE'
global points_list
if printverbose: print("2d Point")
p[0] = [float(p[2]),float(p[4])]
p[0] = [float(p[2]), float(p[4])]
def p_points_list_2d(p):
'''
@@ -300,12 +319,12 @@ def p_points_list_2d(p):
| points_list_2d 2d_point COMMA
| points_list_2d 2d_point
'''
if p[2] == ',' :
if p[2] == ',':
#if printverbose:
# print("Start List")
# print(p[1])
p[0] = [p[1]]
else :
else:
if printverbose:
print(p[1])
print(p[2])
@@ -313,23 +332,25 @@ def p_points_list_2d(p):
p[0] = p[1]
#if printverbose: print(p[0])
def p_3d_point(p):
'3d_point : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
global points_list
if printverbose: print("3d point")
p[0] = [p[2],p[4],p[6]]
p[0] = [p[2], p[4], p[6]]
def p_points_list_3d(p):
'''
points_list_3d : 3d_point COMMA
| points_list_3d 3d_point COMMA
| points_list_3d 3d_point
'''
if p[2] == ',' :
if p[2] == ',':
if printverbose: print("Start List")
if printverbose: print(p[1])
p[0] = [p[1]]
else :
else:
if printverbose: print(p[1])
if printverbose: print(p[2])
p[1].append(p[2])
@@ -343,11 +364,11 @@ def p_path_points(p):
| path_points NUMBER
'''
#if printverbose: print("Path point")
if p[2] == ',' :
if p[2] == ',':
#if printverbose: print('Start list')
#if printverbose: print(p[1])
p[0] = [int(p[1])]
else :
else:
#if printverbose: print(p[1])
#if printverbose: print(len(p[1]))
#if printverbose: print(p[2])
@@ -362,16 +383,17 @@ def p_path_list(p):
#if printverbose: print(p[2])
p[0] = p[2]
def p_path_set(p) :
def p_path_set(p):
'''
path_set : path_list
| path_set COMMA path_list
'''
#if printverbose: print('Path Set')
#if printverbose: print(len(p))
if len(p) == 2 :
if len(p) == 2:
p[0] = [p[1]]
else :
else:
p[1].append(p[3])
p[0] = p[1]
#if printverbose: print(p[0])
@@ -394,10 +416,10 @@ def p_operation(p):
'''
p[0] = p[1]
def placeholder(name,children,arguments):
def placeholder(name, children, arguments):
from OpenSCADFeatures import OpenSCADPlaceholder
newobj=doc.addObject("Part::FeaturePython",name)
OpenSCADPlaceholder(newobj,children,str(arguments))
OpenSCADPlaceholder(newobj, children, str(arguments))
if gui:
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
@@ -408,9 +430,9 @@ def placeholder(name,children,arguments):
#don't hide the children
return newobj
def CGALFeatureObj(name,children,arguments=[]):
myobj=doc.addObject("Part::FeaturePython",name)
CGALFeature(myobj,name,children,str(arguments))
def CGALFeatureObj(name, children,arguments=[]):
myobj=doc.addObject("Part::FeaturePython", name)
CGALFeature(myobj, name, children, str(arguments))
if gui:
for subobj in children:
subobj.ViewObject.hide()
@@ -431,19 +453,19 @@ def p_offset_action(p):
subobj = p[6]
else:
subobj = fuse(p[6],"Offset Union")
if 'r' in p[3] :
if 'r' in p[3]:
offset = float(p[3]['r'])
if 'delta' in p[3] :
if 'delta' in p[3]:
offset = float(p[3]['delta'])
if subobj[0].Shape.Volume == 0 :
newobj=doc.addObject("Part::Offset2D",'Offset2D')
newobj.Source = subobj[0]
newobj.Source = subobj[0]
newobj.Value = offset
if 'r' in p[3] :
newobj.Join = 0
else :
newobj.Join = 2
else :
if 'r' in p[3]:
newobj.Join = 0
else:
newobj.Join = 2
else:
newobj=doc.addObject("Part::Offset",'offset')
newobj.Shape = subobj[0].Shape.makeOffset(offset)
newobj.Document.recompute()
@@ -470,24 +492,24 @@ def p_resize_action(p):
'''
resize_action : resize LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE '''
new_size = p[3]['newsize']
auto = p[3]['auto']
auto = p[3]['auto']
p[6][0].recompute()
if p[6][0].Shape.isNull():
doc.recompute()
p[6][0].Shape.tessellate(0.05)
old_bbox = p[6][0].Shape.BoundBox
old_size = [old_bbox.XLength, old_bbox.YLength, old_bbox.ZLength]
for r in range(0,3) :
if auto[r] == '1' :
for r in range(0,3):
if auto[r] == '1':
new_size[r] = new_size[0]
if new_size[r] == '0' :
if new_size[r] == '0':
new_size[r] = str(old_size[r])
# Calculate a transform matrix from the current bounding box to the new one:
transform_matrix = FreeCAD.Matrix()
scale = FreeCAD.Vector(float(new_size[0])/old_size[0],
float(new_size[1])/old_size[1],
scale = FreeCAD.Vector(float(new_size[0])/old_size[0],
float(new_size[1])/old_size[1],
float(new_size[2])/old_size[2])
transform_matrix.scale(scale)
@@ -503,7 +525,7 @@ def p_resize_action(p):
new_part.ViewObject.Proxy = 0
p[6][0].ViewObject.hide()
p[0] = [new_part]
def p_not_supported(p):
'''
@@ -599,8 +621,8 @@ def p_union_action(p):
if printverbose: print("Push Union Result")
p[0] = [newpart]
if printverbose: print("End Union")
def p_difference_action(p):
def p_difference_action(p):
'difference_action : difference LPAREN RPAREN OBRACE block_list EBRACE'
if printverbose: print("difference")
@@ -611,7 +633,7 @@ def p_difference_action(p):
elif (len(p[5]) == 1 ): #single object
p[0] = p[5]
else:
# Cut using Fuse
# Cut using Fuse
mycut = doc.addObject('Part::Cut',p[1])
mycut.Base = p[5][0]
# Can only Cut two objects do we need to fuse extras
@@ -689,9 +711,9 @@ def process_rotate_extrude_prism(obj, angle, n):
obj.ViewObject.hide()
return(newobj)
def p_rotate_extrude_action(p):
def p_rotate_extrude_action(p):
'rotate_extrude_action : rotate_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
if printverbose: print("Rotate Extrude")
if printverbose: print("Rotate Extrude")
angle = 360.0
if 'angle' in p[3]:
angle = float(p[3]['angle'])
@@ -752,7 +774,7 @@ def process_linear_extrude(obj,h) :
newobj.ViewObject.hide()
return(mylinear)
def process_linear_extrude_with_transform(base,height,twist,scale) :
def process_linear_extrude_with_transform(base,height,twist,scale) :
newobj=doc.addObject("Part::FeaturePython",'transform_extrude')
Twist(newobj,base,height,-twist,scale) #base is an FreeCAD Object, height and twist are floats, scale is a two-component vector of floats
if gui:
@@ -861,7 +883,7 @@ def process_mesh_file(fname,ext):
def processTextCmd(t):
from OpenSCADUtils import callopenscadstring
tmpfilename = callopenscadstring(t,'dxf')
from OpenSCAD2Dgeom import importDXFface
from OpenSCAD2Dgeom import importDXFface
face = importDXFface(tmpfilename,None,None)
obj=doc.addObject('Part::Feature','text')
obj.Shape=face
@@ -972,16 +994,16 @@ def p_multmatrix_action(p):
if part.Shape.isNull():
doc.recompute()
new_part = doc.addObject("Part::Feature","Matrix Deformation")
new_part.Shape = part.Shape.transformGeometry(transform_matrix)
new_part.Shape = part.Shape.transformGeometry(transform_matrix)
if gui:
part.ViewObject.hide()
if False :
# Does not fix problemfile or beltTighener although later is closer
if False :
# Does not fix problemfile or beltTighener although later is closer
newobj=doc.addObject("Part::FeaturePython",'RefineMultMatrix')
RefineShape(newobj,new_part)
if gui:
newobj.ViewObject.Proxy = 0
new_part.ViewObject.hide()
new_part.ViewObject.hide()
p[0] = [newobj]
else :
p[0] = [new_part]
@@ -989,7 +1011,7 @@ def p_multmatrix_action(p):
new_part.ViewObject.ShapeColor=parentcolor
new_part.ViewObject.Transparency = parenttransparency
if printverbose: print("Multmatrix applied")
def p_matrix(p):
'matrix : OSQUARE vector COMMA vector COMMA vector COMMA vector ESQUARE'
if printverbose: print("Matrix")
@@ -1004,7 +1026,7 @@ def center(obj,x,y,z):
obj.Placement = FreeCAD.Placement(\
FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
FreeCAD.Rotation(0,0,0,1))
def p_sphere_action(p):
'sphere_action : sphere LPAREN keywordargument_list RPAREN SEMICOL'
if printverbose: print("Sphere : ",p[3])
@@ -1106,8 +1128,8 @@ def p_cylinder_action(p):
if printverbose: print("Center = ",tocenter)
if tocenter=='true' :
center(mycyl,0,0,h)
if False :
# Does not fix problemfile or beltTighener although later is closer
if False :
# Does not fix problemfile or beltTighener although later is closer
newobj=doc.addObject("Part::FeaturePython",'RefineCylinder')
RefineShape(newobj,mycyl)
if gui:
@@ -1285,7 +1307,7 @@ def p_polyhedron_action(p) :
print(v)
print ("Polyhedron "+p[9])
print (p[12])
faces_list = []
faces_list = []
mypolyhed = doc.addObject('Part::Feature',p[1])
for i in p[12] :
if printverbose: print(i)

View File

@@ -1,12 +1,14 @@
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# 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.
# the License, or (at your option) any later version.
import FreeCAD
import re,math
import math
import re
from OpenSCADFeatures import *
from OpenSCAD2Dgeom import *
from OpenSCADUtils import *
@@ -14,50 +16,53 @@ from OpenSCADUtils import *
if open.__module__ in ['__builtin__','io']:
pythonopen = open # to distinguish python built-in open function from the one declared here
def openscadmesh(doc,scadstr,objname):
def openscadmesh(doc, scadstr, objname):
import Part,Mesh,os,OpenSCADUtils
tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,'stl')
if tmpfilename:
#mesh1 = doc.getObject(objname) #reuse imported object
Mesh.insert(tmpfilename)
os.unlink(tmpfilename)
mesh1=doc.getObject(objname) #blog
mesh1 = doc.getObject(objname) #blog
mesh1.ViewObject.hide()
sh=Part.Shape()
sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
sh.makeShapeFromMesh(mesh1.Mesh.Topology, 0.1)
solid = Part.Solid(sh)
obj=doc.addObject("Part::FeaturePython",objname)
ImportObject(obj,mesh1) #This object is not mutable from the GUI
obj = doc.addObject("Part::FeaturePython", objname)
ImportObject(obj, mesh1) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
solid=solid.removeSplitter()
solid = solid.removeSplitter()
if solid.Volume < 0:
solid.complement()
obj.Shape=solid#.removeSplitter()
obj.Shape = solid#.removeSplitter()
return obj
else:
print(scadstr)
class Node:
#fnmin=12 # maximal fn for implicit polygon rendering
fnmin= FreeCAD.ParamGet(\
#fnmin = 12 # maximal fn for implicit polygon rendering
fnmin = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").GetInt('useMaxFN')
planedim=1e10 #size of the square used as x-y-plane
def __init__(self,name,arguments=None,children=None,):
planedim = 1e10 #size of the square used as x-y-plane
def __init__(self, name, arguments=None, children=None,):
pass
self.name=name
self.arguments=arguments or {}
self.children=children or []
self.name = name
self.arguments = arguments or {}
self.children = children or []
def __repr__(self):
str1 ='Node(name=%s' % self.name
str1 = 'Node(name=%s' % self.name
if self.arguments:
str1 += ',arguments=%s' % self.arguments
if self.children:
str1 += ',children=%s' % self.children
return str1+')'
def __nonzero__(self):
'''A Node is not obsolete if doesn't have children.
'''A Node is not obsolete if doesn't have children.
Only if as neither name children or arguments'''
return bool(self.name or self.arguments or self.children)
@@ -65,50 +70,50 @@ class Node:
'''return the number of children'''
return len(self.children)
def __getitem__(self,key):
def __getitem__(self, key):
'''direct access to the children'''
return self.children.__getitem__(key)
def rlen(self,checkmultmarix=False):
def rlen(self, checkmultmarix=False):
'''Total number of nodes'''
if self.children:
return 1+sum([ch.rlen() for ch in self.children])
else:
return 1
def addtofreecad(self,doc=None,fcpar=None):
def center(obj,x,y,z):
obj.Placement = FreeCAD.Placement(\
FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
FreeCAD.Rotation(0,0,0,1))
import FreeCAD,Part
import FreeCAD
import Part
if not doc:
doc=FreeCAD.newDocument()
obj=None
namel=self.name.lower()
doc = FreeCAD.newDocument()
obj = None
namel = self.name.lower()
multifeature={'union':"Part::MultiFuse",'imp_union':"Part::MultiFuse",
'intersection':"Part::MultiCommon"}
if namel in multifeature:
if len(self.children)>1:
obj=doc.addObject(multifeature[namel],namel)
obj = doc.addObject(multifeature[namel],namel)
subobjs = [child.addtofreecad(doc,obj) for child in self.children]
obj.Shapes = subobjs
for subobj in subobjs:
subobj.ViewObject.hide()
elif len(self.children)==1:
elif len(self.children) == 1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj = fcpar
elif namel == 'difference':
if len(self.children)==1:
if len(self.children) == 1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj=doc.addObject("Part::Cut",namel)
else:
obj = doc.addObject("Part::Cut",namel)
base = self.children[0].addtofreecad(doc,obj)
if len(self.children)==2:
if len(self.children) == 2:
tool = self.children[1].addtofreecad(doc,obj)
else:
tool = Node(name='imp_union',\
@@ -118,57 +123,57 @@ class Node:
base.ViewObject.hide()
tool.ViewObject.hide()
elif namel == 'cube':
obj=doc.addObject('Part::Box',namel)
x,y,z=self.arguments['size']
obj.Length=x
obj.Width=y
obj.Height=z
obj = doc.addObject('Part::Box', namel)
x,y,z = self.arguments['size']
obj.Length = x
obj.Width = y
obj.Height = z
if self.arguments['center']:
center(obj,x,y,z)
elif namel == 'sphere':
obj=doc.addObject("Part::Sphere",namel)
obj = doc.addObject("Part::Sphere", namel)
obj.Radius = self.arguments['r']
elif namel == 'cylinder':
h = self.arguments['h']
r1 ,r2 = self.arguments['r1'], self.arguments['r2']
r1, r2 = self.arguments['r1'], self.arguments['r2']
if '$fn' in self.arguments and self.arguments['$fn'] > 2 \
and self.arguments['$fn']<=Node.fnmin: # polygonal
if r1 == r2: # prismatic
obj = doc.addObject("Part::Prism","prism")
obj.Polygon = int(self.arguments['$fn'])
obj.Circumradius = r1
obj.Height = h
obj.Circumradius = r1
obj.Height = h
if self.arguments['center']:
center(obj,0,0,h)
#base.ViewObject.hide()
elif False: #use Frustum Feature with makeRuledSurface
obj=doc.addObject("Part::FeaturePython",'frustum')
Frustum(obj,r1,r2,int(self.arguments['$fn']),h)
obj = doc.addObject("Part::FeaturePython",'frustum')
Frustum(obj,r1,r2,int(self.arguments['$fn']), h)
ViewProviderTree(obj.ViewObject)
if self.arguments['center']:
center(obj,0,0,h)
else: #Use Part::Loft and GetWire Feature
obj=doc.addObject('Part::Loft','frustum')
obj = doc.addObject('Part::Loft', 'frustum')
import Draft
p1 = Draft.makePolygon(int(self.arguments['$fn']),r1)
p2 = Draft.makePolygon(int(self.arguments['$fn']),r2)
p1 = Draft.makePolygon(int(self.arguments['$fn']), r1)
p2 = Draft.makePolygon(int(self.arguments['$fn']), r2)
if self.arguments['center']:
p1.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,-h/2.0),FreeCAD.Rotation())
p2.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,h/2.0),FreeCAD.Rotation())
FreeCAD.Vector(0.0,0.0,h/2.0), F reeCAD.Rotation())
else:
p2.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,h),FreeCAD.Rotation())
w1=doc.addObject("Part::FeaturePython",'polygonwire1')
w2=doc.addObject("Part::FeaturePython",'polygonwire2')
w1 = doc.addObject("Part::FeaturePython",'polygonwire1')
w2 = doc.addObject("Part::FeaturePython",'polygonwire2')
GetWire(w1,p1)
GetWire(w2,p2)
ViewProviderTree(w1.ViewObject)
ViewProviderTree(w2.ViewObject)
obj.Sections=[w1,w2]
obj.Solid=True
obj.Ruled=True
obj.Sections = [w1,w2]
obj.Solid = True
obj.Ruled = True
p1.ViewObject.hide()
p2.ViewObject.hide()
w1.ViewObject.hide()
@@ -199,7 +204,7 @@ class Node:
obj.Shape=solid#.removeSplitter()
elif namel == 'polygon':
obj = doc.addObject("Part::Feature",namel)
obj = doc.addObject("Part::Feature", namel)
points=self.arguments['points']
paths = self.arguments.get('paths')
if not paths:
@@ -242,7 +247,7 @@ class Node:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj = Node(name='imp_union',\
children=self.children).addtofreecad(doc,fcpar or True)
children = self.children).addtofreecad(doc,fcpar or True)
#FreeCAD.Console.PrintMessage('obj %s\nmat %s/n' % (obj.Placement,m1))
obj.Placement=FreeCAD.Placement(m1).multiply(obj.Placement)
else: #we need to apply the matrix transformation to the Shape using a custom PythonFeature
@@ -349,49 +354,49 @@ class Node:
Mesh.insert(filename)
mesh1=doc.getObject(objname)
mesh1.ViewObject.hide()
sh=Part.Shape()
sh = Part.Shape()
sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
solid = Part.Solid(sh)
obj=doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname))
obj = doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname))
#obj=doc.addObject('Part::Feature',)
ImportObject(obj,mesh1) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
solid=solid.removeSplitter()
solid = solid.removeSplitter()
if solid.Volume < 0:
#sh.reverse()
#sh = sh.copy()
solid.complement()
obj.Shape=solid#.removeSplitter()
obj.Shape = solid#.removeSplitter()
elif extension in ['dxf']:
layera = self.arguments.get('layer')
featname='import_dxf_%s_%s'%(objname,layera)
featname ='import_dxf_%s_%s'%(objname,layera)
# reusing an already imported object does not work if the
# shape in not yet calculated
import importDXF
global dxfcache
layers=dxfcache.get(id(doc),[])
layers = dxfcache.get(id(doc),[])
if layers:
groupobj=[go for go in layers if (not layera) or go.Label == layera]
groupobj = [go for go in layers if (not layera) or go.Label == layera]
else:
groupobj= None
groupobj = None
if not groupobj:
groupname=objname
groupname = objname
layers = importDXF.processdxf(doc,filename) or importDXF.layers
dxfcache[id(doc)] = layers[:]
for l in layers:
for o in l.Group:
o.ViewObject.hide()
l.ViewObject.hide()
groupobj=[go for go in layers if (not layera) or go.Label == layera]
edges=[]
groupobj = [go for go in layers if (not layera) or go.Label == layera]
edges = []
for shapeobj in groupobj[0].Group:
edges.extend(shapeobj.Shape.Edges)
try:
f=edgestofaces(edges)
f = edgestofaces(edges)
except Part.OCCError:
FreeCAD.Console.PrintError('processing of dxf import failed\nPlease rework \'%s\' manually\n' % layera)
f=Part.Shape() #empty Shape
obj=doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera))
f = Part.Shape() #empty Shape
obj = doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera))
#obj=doc.addObject('Part::Feature',)
ImportObject(obj,groupobj[0]) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
@@ -405,22 +410,22 @@ class Node:
if origin is not None and any([c != 0 for c in origin]):
raise(NotImplementedError)# order of transformations unknown
child = obj
m1=FreeCAD.Matrix()
m1 = FreeCAD.Matrix()
m1.scale(scale,scale,scale)
obj=doc.addObject("Part::FeaturePython",'scale_import')
obj = doc.addObject("Part::FeaturePython",'scale_import')
MatrixTransform(obj,m1,child) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
elif origin is not None and any([c != 0 for c in origin]):
placement=FreeCAD.Placement(FreeCAD.Vector(*[-c for c in origin]),FreeCAD.Rotation())
obj.Placement=placement.multiply(obj.Placement)
placement = FreeCAD.Placement(FreeCAD.Vector(*[-c for c in origin]),FreeCAD.Rotation())
obj.Placement = placement.multiply(obj.Placement)
else:
FreeCAD.Console.ErrorMessage('Import of %s failed\n' % (filename))
elif namel == 'minkowski':
childrennames=[child.name.lower() for child in self.children]
childrennames = [child.name.lower() for child in self.children]
if len(self.children) == 2 and \
childrennames.count('cube')==1 and \
childrennames.count('cube') == 1 and \
(childrennames.count('sphere') + \
childrennames.count('cylinder')) == 1:
if self.children[0].name.lower() == 'cube':
@@ -429,15 +434,15 @@ class Node:
elif self.children[1].name.lower() == 'cube':
cube = self.children[1]
roundobj = self.children[0]
roundobjname=roundobj.name.lower()
roundobjname = roundobj.name.lower()
issphere = roundobjname == 'sphere'
cubeobj=doc.addObject('Part::Box','roundedcube')
x,y,z=cube.arguments['size']
r=roundobj.arguments.get('r') or \
cubeobj = doc.addObject('Part::Box','roundedcube')
x,y,z = cube.arguments['size']
r = roundobj.arguments.get('r') or \
roundobj.arguments.get('r1')
cubeobj.Length=x+2*r
cubeobj.Width=y+2*r
cubeobj.Height=z+2*r*issphere
cubeobj.Length = x+2*r
cubeobj.Width = y+2*r
cubeobj.Height = z+2*r*issphere
obj=doc.addObject("Part::Fillet","%s_%s"%(namel,roundobjname))
obj.Base = cubeobj
cubeobj.ViewObject.hide()
@@ -450,13 +455,13 @@ class Node:
else: #htandle a rotated cylinder
#OffsetShape
raise(NotImplementedError)
elif childrennames.count('sphere')==1:
elif childrennames.count('sphere') == 1:
sphereindex=childrennames.index('sphere')
sphere=self.children[sphereindex]
offset=sphere.arguments['r']
nonsphere=self.children[0:sphereindex]+\
sphere = self.children[sphereindex]
offset = sphere.arguments['r']
nonsphere = self.children[0:sphereindex]+\
self.sphere[sphereindex+1:]
obj=doc.addObject("Part::FeaturePython",'Offset')
obj = doc.addObject("Part::FeaturePython",'Offset')
if len(nonsphere) == 1:
child = nonsphere[0].addtofreecad(doc,obj)
else:
@@ -466,7 +471,7 @@ class Node:
ViewProviderTree(obj.ViewObject)
elif False:
raise(NotImplementedError)
pass # handle rotated cylinders and select edges that
pass # handle rotated cylinders and select edges that
#radius = radius0 * m1.multiply(FreeCAD.Vector(0,0,1)).dot(edge.Curve.tangent(0)[0])
else:
raise(NotImplementedError)
@@ -486,7 +491,7 @@ class Node:
elif namel in ['glide','hull']:
raise(NotImplementedError)
elif namel in ['render','subdiv'] or True:
lenchld=len(self.children)
lenchld = len(self.children)
if lenchld == 1:
FreeCAD.Console.PrintMessage('Not recognized %s\n' % (self))
obj = self.children[0].addtofreecad(doc,fcpar)
@@ -556,13 +561,13 @@ class Node:
for i,child in enumerate(self.children):
child.pprint2('%s[%d]'%(path,i),pathjust)
def parseexpression(e):
e=e.strip()
e = e.strip()
el = e.lower()
if len(el)==0: return None
if len(el) == 0: return None
if el == 'true': return True
elif el == 'false': return False
elif el == 'undef': return None
@@ -589,59 +594,59 @@ def parseexpression(e):
def parseargs(argstring):
if '=' in argstring:
level=0
tok=[]
a=[]
level = 0
tok = []
a = []
for i,char in enumerate(argstring):
if char=='[': level+=1
elif char ==']': level -=1
if level==0 and (char=='=' or char==','):
if char == '[': level += 1
elif char ==']': level -= 1
if level == 0 and (char == '=' or char == ','):
tok.append(''.join(a).strip())
a=[]
a= []
else:
a.append(char)
tok.append(''.join(a).strip())
#print(tok)
argdict=dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]]))
# argdict={}
argdict = dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]]))
# argdict = {}
# for key, value in re.findall(r"(\$?\w+)\s*=\s*(\[?\w+]?),?\s*",argstring):
# argdict[key] = parseexpression(value)
# argdict[key] = parseexpression(value)
return argdict
else:
return parseexpression(argstring)
def parsenode(str1):
name,str2=str1.strip().split('(',1)
name, str2 = str1.strip().split('(',1)
assert('}' not in name)
name=name.strip('#!%* ')#remove/ignore modifiers
args,str3=str2.split(')',1)
str4=str3.lstrip()
name = name.strip('#!%* ')#remove/ignore modifiers
args, str3 = str2.split(')',1)
str4 = str3.lstrip()
if str4.startswith(';'):
#has no children
nextelement=str4[1:].lstrip()
nextelement = str4[1:].lstrip()
return Node(name,parseargs(args)),nextelement
elif str4.startswith('{'):
#has children
level=0
level = 0
for index,char in enumerate(str4):
if char == '{': level += 1
elif char == '}': level -= 1
if level == 0:
break
#end of children
childstr= str4[1:index].strip()
childstr = str4[1:index].strip()
nextelement = str4[index+1:].lstrip()
bopen,bclose=childstr.count('{'),childstr.count('}')
bopen,bclose = childstr.count('{'),childstr.count('}')
assert(bopen == bclose)
children=[]
children= []
while childstr:
try:
childnode,childstr=parsenode(childstr)
childnode,childstr = parsenode(childstr)
children.append(childnode)
except ValueError:
raise
if args:
args=parseargs(args)
args = parseargs(args)
return Node(name,args,children),nextelement
def readfile(filename):
@@ -656,7 +661,7 @@ def readfile(filename):
f = pythonopen(tmpfile)
else:
f = pythonopen(filename)
rootnode=parsenode(f.read())[0]
rootnode = p arsenode(f.read())[0]
f.close()
if isopenscad and tmpfile:
try:
@@ -667,8 +672,8 @@ def readfile(filename):
def open(filename):
import os
docname=os.path.split(filename)[1]
doc=FreeCAD.newDocument(docname)
docname = os.path.split(filename)[1]
doc = FreeCAD.newDocument(docname)
doc.Label = (docname.split('.',1)[0])
readfile(filename).addtofreecad(doc)
#doc.recompute()
@@ -676,9 +681,9 @@ def open(filename):
def insert(filename,docname):
try:
doc=FreeCAD.getDocument(docname)
doc = FreeCAD.getDocument(docname)
except NameError:
doc=FreeCAD.newDocument(docname)
doc = FreeCAD.newDocument(docname)
readfile(filename).addtofreecad(doc)
#doc.recompute()

View File

@@ -1,5 +1,4 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -20,7 +19,7 @@
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - replace object function"
__title__ = "FreeCAD OpenSCAD Workbench - replace object function"
__author__ = "Sebastian Hoogen"
__url__ = ["https://www.freecadweb.org"]
@@ -28,9 +27,10 @@ __url__ = ["https://www.freecadweb.org"]
This functions allows to replace an object in the feature hierarchy
'''
def replaceobj(parent,oldchild,newchild):
def replaceobj(parent, oldchild, newchild):
for propname in parent.PropertiesList:
propvalue=parent.getPropertyByName(propname)
propvalue = parent.getPropertyByName(propname)
if type(propvalue) == list:
bModified = False
for dontcare in range(propvalue.count(oldchild)):
@@ -51,8 +51,8 @@ def replaceobj(parent,oldchild,newchild):
def replaceobjfromselection(objs):
# The Parent can be omitted as long as one object is orphaned
if len(objs)==2:
InListLength= tuple((len(obj.InList)) for obj in objs)
if len(objs) == 2:
InListLength = tuple((len(obj.InList)) for obj in objs)
if InListLength == (0,1):
newchild,oldchild = objs
parent = oldchild.InList[0]
@@ -62,7 +62,7 @@ def replaceobjfromselection(objs):
else:
raise ValueError("Selection ambiguous. Please select oldchild,\
newchild and parent")
elif len(objs)==3:
elif len(objs) == 3:
if objs[2] in objs[0].InList: oldchild, newchild, parent = objs
elif objs[0] in objs[1].InList: parent, oldchild, newchild = objs
elif objs[0] in objs[2].InList: parent, newchild, oldchild = objs
@@ -76,9 +76,9 @@ def replaceobjfromselection(objs):
replaceobj(parent,oldchild,newchild)
parent.Document.recompute()
if __name__ == '__main__':
import FreeCAD,FreeCADGui
objs=[selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()]
import FreeCAD
import FreeCADGui
objs = [selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()]
replaceobjfromselection(objs)

View File

@@ -1,7 +1,6 @@
# -*- coding: utf8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
#* *
#* This program is free software; you can redistribute it and/or modify *
@@ -21,7 +20,7 @@
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG importer Version 0.5c"
__title__ = "FreeCAD OpenSCAD Workbench - CSG importer Version 0.5c"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/ImportCSG"]
@@ -108,27 +107,33 @@ reserved_map = { }
for r in reserved:
reserved_map[r.lower()] = r
# Deal with Comments
def t_comment1(t) :
def t_comment1(t):
r'//[^\r\n]*((\r\n)|<<EOF>>)'
pass
def t_comment2(t) :
def t_comment2(t):
r'//[^\n]*((\n)|<<EOF>>)'
pass
def t_ID(t):
r'[$]?[a-zA-Z_]+[0-9]*'
t.type = reserved_map.get(t.value, "ID")
return t
# Define a rule so we can track line numbers
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
# A string containing ignored characters (spaces and tabs)
t_ignore = " \t\r"
t_ignore = " \t\r"
# Error handling rule
def t_error(t):