OpenSCAD: pep8-ified + removed superfluous whitespace + uniform headers
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user