adjustments in path2openscad

This commit is contained in:
leyghisbb 2021-05-14 00:08:41 +02:00
parent 1ab46d13b7
commit e988ae202f
2 changed files with 143 additions and 202 deletions

View File

@ -4,27 +4,16 @@
<id>fablabchemnitz.de.paths2openscad</id> <id>fablabchemnitz.de.paths2openscad</id>
<param name="tab" type="notebook"> <param name="tab" type="notebook">
<page name="splash" gui-text="Paths to OpenSCAD"> <page name="splash" gui-text="Paths to OpenSCAD">
<param type="path" name="fname" gui-text="Ausgabedatei" mode="file_new" filetypes="scad">{NAME}.scad</param> <param type="path" name="fname" gui-text="Output file" mode="file_new" filetypes="scad" gui-description="Use '{NAME}.scad' here to use the name from svg.">{NAME}.scad</param>
<label>Use "{NAME}.scad" here to use the name from svg.</label> <param name="zsize" type="float" min="0" max="1000" precision="2" gui-text="Depth (Z) [mm]" gui-description="Default 3D extrude depth (when no zsize is parsed from an svg object).">5.0</param>
<param name="zsize" type="float" min="0" max="1000" precision="2" gui-text="Depth (Z) [mm]">5.0</param> <param name="parsedesc" type="bool" gui-text="Description parsing" gui-description="Read the extruded zsize, zoffset, scale per svg object from its description or ID. See the Extrusion Syntax tab for details.">true</param>
<label>Default 3D extrude depth (when no zsize is parsed from an svg object).</label> <param name="scadview" type="bool" gui-text="View in OpenSCAD" gui-description="Start OpenSCAD to view the 3D-model.">false</param>
<param name="parsedesc" type="bool" gui-text="Description parsing">true</param> <param name="scad2stl" type="bool" gui-text="Convert to STL" gui-description="Also save an .stl file next to the specified output file.">false</param>
<label>Read the extruded zsize, zoffset, scale per svg object from its description or ID. See the Extrusion Syntax tab for details.</label> <param name="stlpost" type="bool" gui-text="STL post processing" gui-description="Start e.g. a slicer after converting to STL. See the Commands tab for details.">false</param>
<spacer/>
<param name="scadview" type="bool" gui-text="View in OpenSCAD">false</param>
<label>Start OpenSCAD to view the 3D-model.</label>
<spacer/>
<param name="scad2stl" type="bool" gui-text="Convert to STL">false</param>
<label>Also save an .stl file next to the specified output file.</label>
<spacer/>
<param name="stlpost" type="bool" gui-text="STL postprocessing">false</param>
<label>Start e.g. a slicer after converting to STL. See the Commands tab for details.</label>
<param name="stlmodule" type="bool" gui-text="Only create a module">false</param> <param name="stlmodule" type="bool" gui-text="Only create a module">false</param>
<spacer/>
</page> </page>
<page name="tuning" gui-text="Tuning"> <page name="tuning" gui-text="Tuning">
<param name="smoothness" type="float" min="0.0001" max="5" precision="4" gui-text="Smoothing">0.2</param> <param name="smoothness" type="float" min="0.0001" max="5" precision="4" gui-text="Smoothing" gui-description="Used when rendering curves. Smaller values are smoother. Range: 0.0001 to 5">0.2</param>
<label>Used when rendering curves. Smaller values are smoother. Range: 0.0001 to 5</label>
<param type="float" name="chamfer" min="0.0" gui-text="Add chamfer radius [mm]">0</param> <param type="float" name="chamfer" min="0.0" gui-text="Add chamfer radius [mm]">0</param>
<param name="chamfer_fn" type="optiongroup" appearance="combo" gui-text="Chamfer precision ($fn)"> <param name="chamfer_fn" type="optiongroup" appearance="combo" gui-text="Chamfer precision ($fn)">
<option value="4">4: Rough (fast)</option> <option value="4">4: Rough (fast)</option>
@ -33,9 +22,7 @@
<option value="32">32: Perfekt (slowest)</option> <option value="32">32: Perfekt (slowest)</option>
</param> </param>
<label appearance="header">Outline Mode</label> <label appearance="header">Outline Mode</label>
<label xml:space="preserve">Objects are extruded into 3D either in normal mode, or in outline mode. Normally filled areas are extruded, ignoring the line width. Objects with no fill are rendered in outline mode. <label>Objects are extruded into 3D either in normal mode, or in outline mode. Normally filled areas are extruded, ignoring the line width. Objects with no fill are rendered in outline mode. Note: the 'Scale: XX %' instruction has no effect in outline mode.</label>
Note: the 'Scale: XX %' instruction has no effect in outline mode.</label>
<param name="min_line_width" type="float" min="0.1" max="10" gui-text="Minimum line width [mm]">1.0</param> <param name="min_line_width" type="float" min="0.1" max="10" gui-text="Minimum line width [mm]">1.0</param>
<param type="float" name="line_width_scale_perc" min="1" max="10000" gui-text="Scale line width [%]">100.0</param> <param type="float" name="line_width_scale_perc" min="1" max="10000" gui-text="Scale line width [%]">100.0</param>
<param name="line_fn" type="optiongroup" appearance="combo" gui-text="Line width precision ($fn)"> <param name="line_fn" type="optiongroup" appearance="combo" gui-text="Line width precision ($fn)">
@ -49,22 +36,9 @@ Note: the 'Scale: XX %' instruction has no effect in outline mode.</label>
</page> </page>
<page name="commands" gui-text="Commands"> <page name="commands" gui-text="Commands">
<label>Placeholders: You can use "{NAME}.scad" for the OpenSCAD file to read as specified as 'Output file' in the main tab. You can use "{NAME}.stl" for an STL file to write.</label> <label>Placeholders: You can use "{NAME}.scad" for the OpenSCAD file to read as specified as 'Output file' in the main tab. You can use "{NAME}.stl" for an STL file to write.</label>
<label>Beware, Windows and Mac users: The shipped defaults are for Linux.</label> <param name="scadviewcmd" type="string" gui-text="View in OpenSCAD:" gui-description="Is only started, if no command of the same name is running. Always started non-blocking. A running OpenSCAD automatically picks up changed file content">openscad "{NAME}.scad"</param>
<label>Mac: /Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD</label> <param name="scad2stlcmd" type="string" gui-text="Convert to STL:" gui-description="Automatically run, when STL postprocessing is requested.">openscad "{NAME}.scad" -o "{NAME}.stl"</param>
<spacer/> <param name="stlpostcmd" type="string" gui-text="STL postprocessing:" gui-description="Append an '&amp;' for non-blocking.">cura "{NAME}.stl" &amp;</param>
<!-- CAUTION: keep this command in sync with the default INX_SCADVIEW in paths2openscad.py -->
<param name="scadviewcmd" type="string" gui-text="View in OpenSCAD: ">openscad "{NAME}.scad"</param>
<label>Is only started, if no command of the same name is running. Always started non-blocking. A running OpenSCAD automatically picks up changed file contents. Defaults:</label>
<label>Linux: openscad "{NAME}.scad"</label>
<spacer/>
<!-- CAUTION: keep this command in sync with the default INX_SCAD2STL in paths2openscad.py -->
<param name="scad2stlcmd" type="string" gui-text="Convert to STL: ">openscad "{NAME}.scad" -o "{NAME}.stl"</param>
<label>Automatically run, when STL postprocessing is requested.</label>
<spacer/>
<!-- CAUTION: keep this command in sync with the default INX_STL_POSTPROCESSING in paths2openscad.py -->
<param name="stlpostcmd" type="string" gui-text="STL postprocessing: ">cura "{NAME}.stl" &amp;</param>
<label>Append an "&amp;" for non-blocking.</label>
<spacer/>
</page> </page>
<page name="extrusion" gui-text="Extrusion Syntax"> <page name="extrusion" gui-text="Extrusion Syntax">
<label xml:space="preserve">The depth (Z-Axis) and other parameters can be defined through an svg object's description. The menu 'Object-&gt;Object Properties ...' (Shift-Ctrl-O) shows details of one selected object. <label xml:space="preserve">The depth (Z-Axis) and other parameters can be defined through an svg object's description. The menu 'Object-&gt;Object Properties ...' (Shift-Ctrl-O) shows details of one selected object.
@ -95,26 +69,16 @@ Parameters in the objects take precedence over groups.</label>
</page> </page>
<page name="info" gui-text="About ..."> <page name="info" gui-text="About ...">
<label xml:space="preserve"> <label xml:space="preserve">
This extension converts Inkscape paths to This extension converts Inkscape paths to extruded polygons in OpenSCAD. Before using, some objects must firat be converted to paths with the "Path &gt; Object to Path" menu item.
extruded polygons in OpenSCAD. Before
using, some objects must firat be converted to paths
with the "Path &gt; Object to Path" menu item.
Inkscape's units of pixels are converted Inkscape's units of pixels are converted to millimeters using the SVG standard's definition of 96 px = 1 inch = 25.4 mm. (Before inkscape 0.92 the standard was 90 px per inch, Adobe products often use 75 px per inch)
to millimeters using the SVG standard's
definition of 96 px = 1 inch = 25.4 mm.
(Before inkscape 0.92 the standard was 90 px per inch,
Adobe products often use 75 px per inch)
v0.25
Dan Newman (dan newman @ mtbaldy us) Dan Newman (dan newman @ mtbaldy us)
Josef Skladanka (jskladan @ redhat com) Josef Skladanka (jskladan @ redhat com)
Juergen Weigert (juergen @ fabmail org) Juergen Weigert (juergen @ fabmail org)
Find updates at https://github.com/fablabnbg/inkscape-paths2openscad/releases Find updates at</label>
<label appearance="url">https://github.com/fablabnbg/inkscape-paths2openscad/releases</label>
<!-- keep the version numnber in sync with paths2openscad.py header comments and translated copies -->
</label>
</page> </page>
</param> </param>
<effect needs-live-preview="false"> <effect needs-live-preview="false">

View File

@ -125,6 +125,7 @@ import time
import string import string
import tempfile import tempfile
import gettext import gettext
import subprocess
VERSION = "0.27" # CAUTION: Keep in sync with all *.inx files VERSION = "0.27" # CAUTION: Keep in sync with all *.inx files
DEFAULT_WIDTH = 100 DEFAULT_WIDTH = 100
@ -147,9 +148,9 @@ RE_AUTO_ZOFFSET_DESC = re.compile(
DESC_TAGS = ["desc", inkex.addNS("desc", "svg")] DESC_TAGS = ["desc", inkex.addNS("desc", "svg")]
# CAUTION: keep these defaults in sync with paths2openscad.inx # CAUTION: keep these defaults in sync with paths2openscad.inx
INX_SCADVIEW = os.getenv("INX_SCADVIEW", "openscad '{NAME}.scad'") INX_SCADVIEW = os.getenv("INX_SCADVIEW", "openscad \"{NAME}.scad\"")
INX_SCAD2STL = os.getenv("INX_SCAD2STL", "openscad '{NAME}.scad' -o '{NAME}.stl'") INX_SCAD2STL = os.getenv("INX_SCAD2STL", "openscad \"{NAME}.scad\" -o \"{NAME}.stl\"")
INX_STL_POSTPROCESSING = os.getenv("INX_STL_POSTPROCESSING", "cura '{NAME}.stl' &") INX_STL_POSTPROCESSING = os.getenv("INX_STL_POSTPROCESSING", "cura \"{NAME}.stl\" &")
def IsProcessRunning(pid): def IsProcessRunning(pid):
@ -158,19 +159,12 @@ def IsProcessRunning(pid):
""" """
sys_platform = sys.platform.lower() sys_platform = sys.platform.lower()
if sys_platform.startswith("win"): if sys_platform.startswith("win"):
import subprocess with subprocess.Popen(r'tasklist.exe /NH /FI "PID eq %d"' % (pid), shell=True, stdout=subprocess.PIPE) as ps:
output = ps.stdout.read()
ps = subprocess.Popen( ps.wait()
r'tasklist.exe /NH /FI "PID eq %d"' % (pid), if str(pid) in output:
shell=True, return True
stdout=subprocess.PIPE, return False
)
output = ps.stdout.read()
ps.stdout.close()
ps.wait()
if str(pid) in output:
return True
return False
else: else:
# OSX sys_platform.startswith('darwin'): # OSX sys_platform.startswith('darwin'):
# and Linux # and Linux
@ -433,15 +427,15 @@ class OpenSCAD(inkex.EffectExtension):
pars.add_argument( "--line_fn", type=int, default=int(4), help="Line width precision ($fn when constructing hull)", ) pars.add_argument( "--line_fn", type=int, default=int(4), help="Line width precision ($fn when constructing hull)", )
pars.add_argument( "--force_line", type=inkex.utils.Boolean, default=False, help="Force outline mode.", ) pars.add_argument( "--force_line", type=inkex.utils.Boolean, default=False, help="Force outline mode.", )
pars.add_argument( "--fname", default="{NAME}.scad", help="openSCAD output file derived from the svg file name.", ) pars.add_argument( "--fname", default="{NAME}.scad", help="openSCAD output file derived from the svg file name.", )
pars.add_argument( "--parsedesc", default="true", help="Parse zsize and other parameters from object descriptions", ) pars.add_argument( "--parsedesc", type=inkex.utils.Boolean, default=True, help="Parse zsize and other parameters from object descriptions", )
pars.add_argument( "--scadview", default="false", help="Open the file with openscad ( details see --scadviewcmd option )", ) pars.add_argument( "--scadview", type=inkex.utils.Boolean, default=False, help="Open the file with openscad ( details see --scadviewcmd option )", )
pars.add_argument( "--scadviewcmd", default=INX_SCADVIEW, help="Command used start an openscad viewer. Use {SCAD} for the openSCAD input.", ) pars.add_argument( "--scadviewcmd", default=INX_SCADVIEW, help="Command used start an openscad viewer. Use {SCAD} for the openSCAD input.", )
pars.add_argument( "--scad2stl", default="false", help="Also convert to STL ( details see --scad2stlcmd option )", ) pars.add_argument( "--scad2stl", type=inkex.utils.Boolean, default=False, help="Also convert to STL ( details see --scad2stlcmd option )", )
pars.add_argument( "--scad2stlcmd", default=INX_SCAD2STL, help="Command used to convert to STL. You can use {NAME}.scad for the openSCAD file to read and " pars.add_argument( "--scad2stlcmd", default=INX_SCAD2STL, help="Command used to convert to STL. You can use {NAME}.scad for the openSCAD file to read and "
+ "{NAME}.stl for the STL file to write.", ) + "{NAME}.stl for the STL file to write.", )
pars.add_argument( "--stlpost", default="false", help="Start e.g. a slicer. This implies the --scad2stl option. ( see --stlpostcmd )", ) pars.add_argument( "--stlpost", type=inkex.utils.Boolean, default=False, help="Start e.g. a slicer. This implies the --scad2stl option. ( see --stlpostcmd )", )
pars.add_argument( "--stlpostcmd", default=INX_STL_POSTPROCESSING, help="Command used for post processing an STL file (typically a slicer). You can use {NAME}.stl for the STL file.", ) pars.add_argument( "--stlpostcmd", default=INX_STL_POSTPROCESSING, help="Command used for post processing an STL file (typically a slicer). You can use {NAME}.stl for the STL file.", )
pars.add_argument( "--stlmodule",default="false", help="Output configured to comment out final rendering line, to create a module file for import.", ) pars.add_argument( "--stlmodule", type=inkex.utils.Boolean, default=False, help="Output configured to comment out final rendering line, to create a module file for import.", )
self.userunitsx = 1.0 # Move to pure userunits per mm for v1.0 self.userunitsx = 1.0 # Move to pure userunits per mm for v1.0
self.userunitsy = 1.0 self.userunitsy = 1.0
@ -463,7 +457,7 @@ class OpenSCAD(inkex.EffectExtension):
self.pathid = int(0) self.pathid = int(0)
# Output file # Output file
self.f = None self.outfile = None
# For handling an SVG viewbox attribute, we will need to know the # For handling an SVG viewbox attribute, we will need to know the
# values of the document's <svg> width and height attributes as well # values of the document's <svg> width and height attributes as well
@ -793,8 +787,8 @@ class OpenSCAD(inkex.EffectExtension):
) )
polypoints = polypoints[:-1] polypoints = polypoints[:-1]
polypoints += "];\n" polypoints += "];\n"
self.f.write(polycenter + ";\n") self.outfile.write(polycenter + ";\n")
self.f.write(polypoints) self.outfile.write(polypoints)
prefix += 1 prefix += 1
else: else:
# This subpath contains other subpaths # This subpath contains other subpaths
@ -824,22 +818,22 @@ class OpenSCAD(inkex.EffectExtension):
polypoints += "];\n" polypoints += "];\n"
polypaths = polypaths[:-7] + "];\n" polypaths = polypaths[:-7] + "];\n"
# write the polys and paths # write the polys and paths
self.f.write(polycenter + ";\n") self.outfile.write(polycenter + ";\n")
self.f.write(polypoints) self.outfile.write(polypoints)
self.f.write(polypaths) self.outfile.write(polypaths)
prefix += 1 prefix += 1
# #### end global data for msg_*() functions. #### # #### end global data for msg_*() functions. ####
self.f.write("module poly_" + id + "(h, w, s, res=line_fn)\n{\n") self.outfile.write("module poly_" + id + "(h, w, s, res=line_fn)\n{\n")
# Element is transformed to correct size, so scale is now just for the user to # Element is transformed to correct size, so scale is now just for the user to
# tweak after the fact # tweak after the fact
self.f.write(" scale([custom_scale_x, -custom_scale_y, 1]) union()\n {\n") self.outfile.write(" scale([custom_scale_x, -custom_scale_y, 1]) union()\n {\n")
# And add the call to the call list # And add the call to the call list
# Z-size is set by the overall module parameter # Z-size is set by the overall module parameter
# unless an extrusion zsize is parsed from the description or ID. # unless an extrusion zsize is parsed from the description or ID.
extrusion = {"zsize": "h", "zoffset": "0", "scale": 100.0, "neg": False} extrusion = {"zsize": "h", "zoffset": "0", "scale": 100.0, "neg": False}
if self.options.parsedesc == "true": if self.options.parsedesc is True:
object_merge_extrusion_values(extrusion, node) object_merge_extrusion_values(extrusion, node)
call_item = "translate ([0,0,%s]) poly_%s(%s, min_line_mm(%s), %s);\n" % ( call_item = "translate ([0,0,%s]) poly_%s(%s, min_line_mm(%s), %s);\n" % (
@ -883,11 +877,11 @@ class OpenSCAD(inkex.EffectExtension):
# This subpath contains other subpaths # This subpath contains other subpaths
poly = msg_extrude_by_hull_and_paths(id, prefix) poly = msg_extrude_by_hull_and_paths(id, prefix)
self.f.write(poly) self.outfile.write(poly)
prefix += 1 prefix += 1
# End the module # End the module
self.f.write(" }\n}\n") self.outfile.write(" }\n}\n")
def recursivelyTraverseSvg( def recursivelyTraverseSvg(
self, aNodeList, matCurrent=Transform(None), parent_visibility="visible" self, aNodeList, matCurrent=Transform(None), parent_visibility="visible"
@ -1198,11 +1192,7 @@ class OpenSCAD(inkex.EffectExtension):
pass pass
def recursivelyGetEnclosingTransform(self, node): def recursivelyGetEnclosingTransform(self, node):
# Determine the cumulative transform which node inherits from its chain of ancestors.
"""
Determine the cumulative transform which node inherits from
its chain of ancestors.
"""
node = node.getparent() node = node.getparent()
if node is not None: if node is not None:
parent_transform = self.recursivelyGetEnclosingTransform(node) parent_transform = self.recursivelyGetEnclosingTransform(node)
@ -1250,13 +1240,13 @@ class OpenSCAD(inkex.EffectExtension):
scad_fname = os.path.expanduser(self.options.fname) scad_fname = os.path.expanduser(self.options.fname)
if "/" != os.sep: if "/" != os.sep:
scad_fname = scad_fname.replace("/", os.sep) scad_fname = scad_fname.replace("/", os.sep)
self.f = open(scad_fname, "w") self.outfile = open(scad_fname, "w")
self.f.write( self.outfile.write(
"// Generated by inkscape %s + inkscape-paths2openscad %s\n" "// Generated by inkscape %s + inkscape-paths2openscad %s\n"
% (self.inkscape_version, VERSION) % (self.inkscape_version, VERSION)
) )
self.f.write('// %s from "%s.svg"\n' % (time.ctime(), self.basename)) self.outfile.write('// %s from "%s.svg"\n' % (time.ctime(), self.basename))
# for use in options.fname basename is derived from the sodipodi_docname by # for use in options.fname basename is derived from the sodipodi_docname by
# stripping the svg extension - or if there is no sodipodi_docname basename is 'inkscape'. # stripping the svg extension - or if there is no sodipodi_docname basename is 'inkscape'.
@ -1264,7 +1254,7 @@ class OpenSCAD(inkex.EffectExtension):
# options.fname by stripping an scad extension. # options.fname by stripping an scad extension.
self.basename = re.sub(r"\.scad", "", scad_fname, flags=re.I) self.basename = re.sub(r"\.scad", "", scad_fname, flags=re.I)
self.f.write( self.outfile.write(
""" """
// Module names are of the form poly_<inkscape-path-id>(). As a result, // Module names are of the form poly_<inkscape-path-id>(). As a result,
// you can associate a polygon in this OpenSCAD program with the corresponding // you can associate a polygon in this OpenSCAD program with the corresponding
@ -1280,32 +1270,32 @@ fudge = 0.1;
if self.options.chamfer < 0.001: if self.options.chamfer < 0.001:
self.options.chamfer = None self.options.chamfer = None
self.f.write("user_unit_scale_x = %s;\n" % (self.userunitsx)) self.outfile.write("user_unit_scale_x = %s;\n" % (self.userunitsx))
self.f.write("user_unit_scale_y = %s;\n" % (self.userunitsy)) self.outfile.write("user_unit_scale_y = %s;\n" % (self.userunitsy))
self.f.write("custom_scale_x = 1;\n") self.outfile.write("custom_scale_x = 1;\n")
self.f.write("custom_scale_y = 1;\n") self.outfile.write("custom_scale_y = 1;\n")
# writeout users parameters # writeout users parameters
self.f.write("zsize = %s;\n" % (self.options.zsize)) self.outfile.write("zsize = %s;\n" % (self.options.zsize))
self.f.write("line_fn = %d;\n" % (self.options.line_fn)) self.outfile.write("line_fn = %d;\n" % (self.options.line_fn))
if self.options.chamfer: if self.options.chamfer:
self.f.write("chamfer = %s;\n" % (self.options.chamfer)) self.outfile.write("chamfer = %s;\n" % (self.options.chamfer))
self.f.write("chamfer_fn = %d;\n" % (self.options.chamfer_fn)) self.outfile.write("chamfer_fn = %d;\n" % (self.options.chamfer_fn))
self.f.write("min_line_width = %s;\n" % (self.options.min_line_width)) self.outfile.write("min_line_width = %s;\n" % (self.options.min_line_width))
self.f.write( self.outfile.write(
"line_width_scale = %s;\n" % (self.options.line_width_scale_perc * 0.01) "line_width_scale = %s;\n" % (self.options.line_width_scale_perc * 0.01)
) )
self.f.write( self.outfile.write(
"function min_line_mm(w) = max(min_line_width, w * line_width_scale) * %g;\n\n" "function min_line_mm(w) = max(min_line_width, w * line_width_scale) * %g;\n\n"
% self.userunitsx % self.userunitsx
) )
for key in self.paths: for key in self.paths:
self.f.write("\n") self.outfile.write("\n")
self.convertPath(key) self.convertPath(key)
if self.options.chamfer: if self.options.chamfer:
self.f.write( self.outfile.write(
""" """
module chamfer_sphere(rad=chamfer, res=chamfer_fn) module chamfer_sphere(rad=chamfer, res=chamfer_fn)
{ {
@ -1330,81 +1320,77 @@ module chamfer_sphere(rad=chamfer, res=chamfer_fn)
badchars = string.punctuation.replace("_", "") + " " badchars = string.punctuation.replace("_", "") + " "
name = re.sub("[" + badchars + "]", "_", name) name = re.sub("[" + badchars + "]", "_", name)
self.f.write("\nmodule %s(h)\n{\n" % name) self.outfile.write("\nmodule %s(h)\n{\n" % name)
mi = "" mi = ""
if self.options.chamfer: if self.options.chamfer:
mi = " " mi = " "
self.f.write(" minkowski()\n {\n") self.outfile.write(" minkowski()\n {\n")
# Now output the list of modules to call # Now output the list of modules to call
self.f.write( self.outfile.write(
"%s difference()\n%s {\n%s union()\n%s {\n" % (mi, mi, mi, mi) "%s difference()\n%s {\n%s union()\n%s {\n" % (mi, mi, mi, mi)
) )
for call in self.call_list: for call in self.call_list:
self.f.write("%s %s" % (mi, call)) self.outfile.write("%s %s" % (mi, call))
self.f.write("%s }\n%s union()\n%s {\n" % (mi, mi, mi)) self.outfile.write("%s }\n%s union()\n%s {\n" % (mi, mi, mi))
for call in self.call_list_neg: for call in self.call_list_neg:
self.f.write("%s %s" % (mi, call)) self.outfile.write("%s %s" % (mi, call))
self.f.write("%s }\n%s }\n" % (mi, mi)) self.outfile.write("%s }\n%s }\n" % (mi, mi))
if self.options.chamfer: if self.options.chamfer:
self.f.write(" chamfer_sphere();\n }\n") self.outfile.write(" chamfer_sphere();\n }\n")
# The module that calls all the other ones. # The module that calls all the other ones.
if self.options.stlmodule == "true": if self.options.stlmodule is True:
self.f.write("}\n\n//%s(zsize);\n" % (name)) self.outfile.write("}\n\n//%s(zsize);\n" % (name))
else: else:
self.f.write("}\n\n%s(zsize);\n" % (name)) self.outfile.write("}\n\n%s(zsize);\n" % (name))
self.f.close()
except IOError as e: except IOError as e:
inkex.errormsg("Unable to write file " + self.options.fname) inkex.errormsg("Unable to write file " + self.options.fname)
inkex.errormsg("ERROR: " + str(e)) inkex.errormsg("ERROR: " + str(e))
self.outfile.close()
if self.options.scadview == "true": ################################################################
# Call OpenSCAD
################################################################
if self.options.scadview is True:
pidfile = os.path.join(tempfile.gettempdir(), "paths2openscad.pid") pidfile = os.path.join(tempfile.gettempdir(), "paths2openscad.pid")
running = False running = False
cmd = self.options.scadviewcmd.format( cmd = self.options.scadviewcmd.format(**{"SCAD": scad_fname, "NAME": self.basename})
**{"SCAD": scad_fname, "NAME": self.basename}
)
try: try:
pfile=open(pidfile) with open(pidfile) as pfile:
m = re.match(r"(\d+)\s+(.*)", pfile.read()) m = re.match(r"(\d+)\s+(.*)", pfile.read())
pfile.close() oldpid = int(m.group(1))
oldpid = int(m.group(1)) oldcmd = m.group(2)
oldcmd = m.group(2) # print >> sys.stderr, "pid {1} seen in {0}".format(pidfile, oldpid)
# print >> sys.stderr, "pid {1} seen in {0}".format(pidfile, oldpid) # print >> sys.stderr, "cmd {0}, oldcmd {1}".format(cmd, oldcmd)
# print >> sys.stderr, "cmd {0}, oldcmd {1}".format(cmd, oldcmd) if cmd == oldcmd:
if cmd == oldcmd: # we found a pidfile and the cmd in there is still identical.
# we found a pidfile and the cmd in there is still identical. # If we change the filename in the inkscape extension gui, the cmd differs, and
# If we change the filename in the inkscape extension gui, the cmd differs, and # the still running openscad would not pick up our changes.
# the still running openscad would not pick up our changes. # If the command is identical, we check if the pid in the pidfile is alive.
# If the command is identical, we check if the pid in the pidfile is alive. # If so, we assume, the still running openscad will pick up the changes.
# If so, we assume, the still running openscad will pick up the changes. #
# # WARNING: too much magic here. We cannot really test, if the last assumption holds.
# WARNING: too much magic here. We cannot really test, if the last assumption holds. # Comment out the next line to always start a new instance of openscad.
# Comment out the next line to always start a new instance of openscad. running = IsProcessRunning(oldpid)
running = IsProcessRunning(oldpid) # print >> sys.stderr, "running {0}".format(running)
# print >> sys.stderr, "running {0}".format(running)
except Exception: except Exception:
pass pass
if not running: if not running:
import subprocess
try: try:
tty = open("/dev/tty", "w") tty = open("/dev/tty", "w")
except Exception: except Exception:
tty = subprocess.PIPE tty = subprocess.PIPE
try: try:
proc = subprocess.Popen(cmd, shell=True, stdin=tty, stdout=tty, stderr=tty) with subprocess.Popen(cmd, shell=True, stdin=tty, stdout=tty, stderr=tty) as proc:
proc.wait() proc.wait()
tty.close()
except OSError as e: except OSError as e:
raise OSError("%s failed: errno=%d %s" % (cmd, e.errno, e.strerror)) raise OSError("%s failed: errno=%d %s" % (cmd, e.errno, e.strerror))
try: try:
pfile = open(pidfile, "w") with open(pidfile, "w") as pfile:
pfile.write(str(proc.pid) + "\n" + cmd + "\n") pfile.write(str(proc.pid) + "\n" + cmd + "\n")
pfile.close()
except Exception: except Exception:
pass pass
else: else:
@ -1414,71 +1400,62 @@ module chamfer_sphere(rad=chamfer, res=chamfer_fn)
# pick up the changes. and we have no way to tell the difference if it did. # pick up the changes. and we have no way to tell the difference if it did.
pass pass
if self.options.scad2stl == "true" or self.options.stlpost == "true": ################################################################
# Call OpenSCAD to STL conversion
################################################################
if self.options.scad2stl is True or self.options.stlpost is True:
stl_fname = self.basename + ".stl" stl_fname = self.basename + ".stl"
cmd = self.options.scad2stlcmd.format( scad2stlcmd = self.options.scad2stlcmd.format(**{"SCAD": scad_fname, "STL": stl_fname, "NAME": self.basename})
**{"SCAD": scad_fname, "STL": stl_fname, "NAME": self.basename}
)
try: try:
os.unlink(stl_fname) os.unlink(stl_fname)
except Exception: except Exception:
pass pass
import subprocess with subprocess.Popen(scad2stlcmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
try:
proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.wait() proc.wait()
except OSError as e:
raise OSError(
"{0} failed: errno={1} {2}".format(cmd, e.errno, e.strerror)
)
stdout, stderr = proc.communicate()
len = -1
try:
len = os.path.getsize(stl_fname)
except Exception:
pass
if len < 1000:
inkex.errormsg(
"CMD: {} WARNING: {} is very small: {} bytes.".format(
cmd, stl_fname, len
)
)
inkex.errormsg("= " * 24)
inkex.errormsg("STDOUT:\n{}".format(stdout))
inkex.errormsg("= " * 24)
inkex.errormsg("STDERR:\n{}".format(stderr))
inkex.errormsg("= " * 24)
if len <= 0: # something is wrong. better stop here
self.options.stlpost = "false"
proc.wait()
if self.options.stlpost == "true":
cmd = self.options.stlpostcmd.format(
**{"STL": self.basename + ".stl", "NAME": self.basename}
)
try:
tty = open("/dev/tty", "w")
except Exception:
tty = subprocess.PIPE
try:
proc = subprocess.Popen(cmd, shell=True, stdin=tty, stdout=tty, stderr=tty)
proc.wait()
except OSError as e:
raise OSError("%s failed: errno=%d %s" % (cmd, e.errno, e.strerror))
stdout, stderr = proc.communicate() stdout, stderr = proc.communicate()
if stdout or stderr: len = -1
inkex.errmsg("CMD: {}".format(cmd)) try:
len = os.path.getsize(stl_fname)
except Exception:
pass
if len < 1000:
inkex.errormsg("CMD: {} WARNING: {} is very small: {} bytes.".format(scad2stlcmd, stl_fname, len))
inkex.errormsg("= " * 24) inkex.errormsg("= " * 24)
if stdout: inkex.errormsg("STDOUT:\n{}".format(stdout))
inkex.errmsg("STDOUT: {}".format(stdout))
inkex.errormsg("= " * 24) inkex.errormsg("= " * 24)
if stderr: inkex.errormsg("STDERR:\n{}".format(stderr))
inkex.errmsg("STDERR: {}".format(stderr))
inkex.errormsg("= " * 24) inkex.errormsg("= " * 24)
if len <= 0: # something is wrong. better stop here
self.options.stlpost = False
################################################################
# Call OpenSCAD post processing
################################################################
if self.options.stlpost is True:
stlpostcmd = self.options.stlpostcmd.format(
**{"STL": self.basename + ".stl", "NAME": self.basename}
)
try:
tty = open("/dev/tty", "w")
except Exception:
tty = subprocess.PIPE
try:
with subprocess.Popen(stlpostcmd, shell=True, stdin=tty, stdout=tty, stderr=tty) as proc:
proc.wait()
stdout, stderr = proc.communicate()
if stdout or stderr:
inkex.errormsg("CMD: {}".format(stlpostcmd))
inkex.errormsg("= " * 24)
if stdout:
inkex.errormsg("STDOUT: {}".format(stdout))
inkex.errormsg("= " * 24)
if stderr:
inkex.errormsg("STDERR: {}".format(stderr))
inkex.errormsg("= " * 24)
except OSError as e:
raise OSError("%s failed: errno=%d %s" % (stlpostcmd, e.errno, e.strerror))
if __name__ == '__main__': if __name__ == '__main__':
OpenSCAD().run() OpenSCAD().run()