.
This commit is contained in:
123
extensions/fablabchemnitz/slic3r_stl_input/slic3r_stl_input.inx
Normal file
123
extensions/fablabchemnitz/slic3r_stl_input/slic3r_stl_input.inx
Normal file
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Slic3r STL Input</name>
|
||||
<id>fablabchemnitz.de.slic3r_stl_input</id>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="tab_options" gui-text="Options">
|
||||
<label appearance="header">Processor</label>
|
||||
<param name="slic3r_cmd" type="path" mode="file" gui-text="Slic3r-1.3.1-dev command">/path/to/slic3r/v1.3.1-dev</param>
|
||||
<label appearance="header">Input</label>
|
||||
<param name="inputfile" type="path" gui-text="Input file (OBJ/OFF/PLY/STL)" filetypes="obj,off,ply,stl" mode="file">/your/object/file.stl</param>
|
||||
<param name="max_num_faces" type="int" min="1" max="99999" gui-text="Maximum allowed faces" gui-description="If the STL file has too much detail it contains a large number of faces. This will make processing extremely slow. So we can limit it.">200</param>
|
||||
<param name="layer_height" type="float" min="0.001" max="99999.000" precision="3" gui-text="Layer height [mm]">1.000</param>
|
||||
<hbox>
|
||||
<vbox>
|
||||
<label appearance="header">Transforms</label>
|
||||
<param name="scalefactor" type="float" precision="3" min="0.0001" max="10000.0" gui-text="Manual scale factor" gui-description="default is 1.0">1.0</param>
|
||||
<param name="rx" type="float" precision="1" min="-360.0" max="360.0" gui-text="3D-Rotate X-Axis [deg]">0.0</param>
|
||||
<param name="ry" type="float" precision="1" min="-360.0" max="360.0" gui-text="3D-Rotate Y-Axis [deg]">0.0</param>
|
||||
<param name="rz" type="float" precision="1" min="-360.0" max="360.0" gui-text="3D-Rotate Z-Axis [deg]">0.0</param>
|
||||
<param name="mirrorx" type="bool" gui-text="Mirror X">false</param>
|
||||
<param name="mirrory" type="bool" gui-text="Mirror Y">false</param>
|
||||
</vbox>
|
||||
<separator />
|
||||
<vbox>
|
||||
<label appearance="header">Output</label>
|
||||
<param name="resizetoimport" type="bool" gui-text="Resize canvas" gui-description="Resize the canvas to the imported drawing's bounding box">true</param>
|
||||
<param name="extraborder" type="float" precision="3" gui-text="Extra border" gui-description="Add extra border around fitted canvas">0.0</param>
|
||||
<param name="extraborder_units" type="optiongroup" appearance="combo" gui-text="Border offset units">
|
||||
<option value="mm">mm</option>
|
||||
<option value="cm">cm</option>
|
||||
<option value="in">in</option>
|
||||
<option value="pt">pt</option>
|
||||
<option value="px">px</option>
|
||||
</param>
|
||||
<param name="numbers" type="bool" gui-text="Add layer numbers">false</param>
|
||||
<param name="center" type="bool" gui-text="Add center marks">false</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
<page name="tab_style" gui-text="Style">
|
||||
<hbox>
|
||||
<vbox>
|
||||
<label appearance="header">Fill Style</label>
|
||||
<param name="use_fill_color" type="bool" gui-text="Use fill color">false</param>
|
||||
<param name="fill_color" type="color" appearance="colorbutton" gui-text="Fill color">1943148287</param>
|
||||
<param name="min_fill_opacity" type="float" precision="3" min="0.000" max="1.000" gui-text="Min fill opacity">0.0</param>
|
||||
<param name="max_fill_opacity" type="float" precision="3" min="0.000" max="1.000" gui-text="Max fill opacity">1.0</param>
|
||||
<label>Per Layer Settings</label>
|
||||
<param name="diffuse_fill_opacity" type="optiongroup" appearance="combo" gui-text="Diffuse fill opacity">
|
||||
<option value="no_diffuse">no diffuse (use max. opacity)</option>
|
||||
<option value="front_to_back">front to back</option>
|
||||
<option value="back_to_front">back to front</option>
|
||||
</param>
|
||||
<separator/>
|
||||
<label appearance="header">Stroke Color</label>
|
||||
<param name="use_stroke_color" type="bool" gui-text="Use stroke color">true</param>
|
||||
<param name="stroke_color" type="color" appearance="colorbutton" gui-text="Stroke color">879076607</param>
|
||||
</vbox>
|
||||
<separator/>
|
||||
<vbox>
|
||||
<label appearance="header">Stroke Width</label>
|
||||
<param name="min_stroke_width" type="float" precision="3" min="0.000" max="1000.000" gui-text="Min stroke width">1.0</param>
|
||||
<param name="max_stroke_width" type="float" precision="3" min="0.000" max="1000.000" gui-text="Max stroke width">1.0</param>
|
||||
<label>Per Layer Settings</label>
|
||||
<param name="diffuse_stroke_width" type="optiongroup" appearance="combo" gui-text="Diffuse stroke width">
|
||||
<option value="no_diffuse">no diffuse (use max. width)</option>
|
||||
<option value="front_to_back">front to back</option>
|
||||
<option value="back_to_front">back to front</option>
|
||||
</param>
|
||||
<label appearance="header">Stroke Opacity</label>
|
||||
<param name="min_stroke_opacity" type="float" precision="3" min="0.000" max="1.000" gui-text="Min stroke opacity">0.0</param>
|
||||
<param name="max_stroke_opacity" type="float" precision="3" min="0.000" max="1.000" gui-text="Max stroke opacity">1.0</param>
|
||||
<label>Per Layer Settings</label>
|
||||
<param name="diffuse_stroke_opacity" type="optiongroup" appearance="combo" gui-text="Diffuse stroke opacity">
|
||||
<option value="no_diffuse">no diffuse (use max. opacity)</option>
|
||||
<option value="front_to_back">front to back</option>
|
||||
<option value="back_to_front">back to front</option>
|
||||
</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
<page name="tab_about" gui-text="About">
|
||||
<label appearance="header">Slic3r STL Input</label>
|
||||
<label>This extension is highly based on the work of Jürgen Weigert. It projects an STL file on the X-Y plane by cutting the objects into "slices". Each "slice" is a group of polygons, with a label indicating it's z position. The polygons are converted to paths for better editing in inkscape. Use Object -> Rows & Columns to distribute the slices in a grid. Possible input files are STL, Wavefront OBJ, PLY and OFF.</label>
|
||||
<label>2021 / written by Mario Voigt (Stadtfabrikanten e.V. / FabLab Chemnitz)</label>
|
||||
<spacer />
|
||||
<label appearance="header">Online Documentation</label>
|
||||
<label appearance="url">https://y.stadtfabrikanten.org/slic3rstlinput</label>
|
||||
<spacer />
|
||||
<label appearance="header">Contributing</label>
|
||||
<label appearance="url">https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X</label>
|
||||
<label appearance="url">mailto:mario.voigt@stadtfabrikanten.org</label>
|
||||
<spacer />
|
||||
<label appearance="header">Third Party Modules</label>
|
||||
<label appearance="url">https://slic3r.org/download</label>
|
||||
<spacer />
|
||||
<label appearance="header">MightyScape Extension Collection</label>
|
||||
<label>This piece of software is part of the MightyScape for Inkscape Extension Collection and is licensed under GNU GPL v3</label>
|
||||
<label appearance="url">https://y.stadtfabrikanten.org/mightyscape-overview</label>
|
||||
</page>
|
||||
<page name="tab_donate" gui-text="Donate">
|
||||
<label appearance="header">Coffee + Pizza</label>
|
||||
<label>We are the Stadtfabrikanten, running the FabLab Chemnitz since 2016. A FabLab is an open workshop that gives people access to machines and digital tools like 3D printers, laser cutters and CNC milling machines.</label>
|
||||
<spacer />
|
||||
<label>You like our work and want to support us? You can donate to our non-profit organization by different ways:</label>
|
||||
<label appearance="url">https://y.stadtfabrikanten.org/donate</label>
|
||||
<spacer />
|
||||
<label>Thanks for using our extension and helping us!</label>
|
||||
<image>../000_about_fablabchemnitz.svg</image>
|
||||
</page>
|
||||
</param>
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Import/Export/Transfer" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">slic3r_stl_input.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
||||
419
extensions/fablabchemnitz/slic3r_stl_input/slic3r_stl_input.py
Normal file
419
extensions/fablabchemnitz/slic3r_stl_input/slic3r_stl_input.py
Normal file
@@ -0,0 +1,419 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
(C) 2018 Juergen Weigert <juergen@fabmail.org>, distribute under GPLv2 or ask
|
||||
|
||||
This is an input extension for inkscape to read STL files.
|
||||
|
||||
Requires: (python-lxml | python3-lxml), slic3r
|
||||
For optional(!) rotation support:
|
||||
Requires: (python-numpy-stl | python3-numpy-stl)
|
||||
If you get ImportError: cannot import name 'mesh'
|
||||
although an stl module is installed, then you have the wrong stl module.
|
||||
Try 'pip3 uninstall stl; pip3 install numpy-stl'
|
||||
|
||||
2018-12-22 jw, v0.1 Initial draught
|
||||
v0.1 First working standalone tool.
|
||||
2018-12-26 jw, v0.3 Mesh rotation support via numpy-stl. Fully optional.
|
||||
v0.4 Works fine as an inkscape input extension under Linux.
|
||||
2019-03-01 jw, v0.5 numbers and center option added.
|
||||
2019-07-17 jw, v0.6 fixed ry rotation.
|
||||
|
||||
2021-05-14 - Mario Voigt:
|
||||
- changed extension to support ply,off,obj,stl by using OpenMesh
|
||||
- moved extension to sub menu structure (allows preview)
|
||||
- added different options
|
||||
|
||||
#Notes
|
||||
* requires exactly Slic3r-1.3.1-dev
|
||||
-> https://dl.slic3r.org/dev/linux/
|
||||
-> https://dl.slic3r.org/dev/win/
|
||||
* Notes to other Slic3r forks
|
||||
* does not work with Slic3r PE (Prusa Edition) (no export-svg option)
|
||||
* does not work with PrusaSlicer (no export-svg option)
|
||||
* does not work with IceSL slicer (https://icesl.loria.fr; no command line options)
|
||||
|
||||
#ToDos
|
||||
* use svg_pathstats(path_d): to compute bounding boxes.
|
||||
* fix bbox calc in Linux systems: ValueError: b'/tmp/.mount_Slic3rQi5kIt/AppRun: line 65: 10415 Segmentation fault
|
||||
(core dumped) LD_LIBRARY_PATH="$DIR/usr/lib:${LD_LIBRARY_PATH}" "${DIR}/usr/bin/perl-local" -I"${DIR}/usr/lib/local-lib/lib/perl5"
|
||||
"${DIR}/usr/bin/slic3r.pl" --gui "$@"\n'
|
||||
* add some algorithm to handle fill handling for alternating paths. Issue at the moment: Imagine a char "A" in 3D.
|
||||
It conatains an outline and a line path. Because those two paths are not combined both get filled but they do not render the char A correctly
|
||||
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from lxml import etree
|
||||
from subprocess import Popen, PIPE
|
||||
import inkex
|
||||
from inkex import Color, Transform
|
||||
import tempfile
|
||||
import openmesh as om
|
||||
|
||||
|
||||
sys_platform = sys.platform.lower()
|
||||
if sys_platform.startswith('win'):
|
||||
slic3r = 'slic3r-console.exe'
|
||||
elif sys_platform.startswith('darwin'):
|
||||
slic3r = 'slic3r'
|
||||
else: # Linux
|
||||
slic3r = os.environ['HOME']+ '/Downloads/Slic3r-1.3.1-dev-x86_64.AppImage'
|
||||
if not os.path.exists(slic3r):
|
||||
slic3r = 'slic3r'
|
||||
|
||||
class SlicerSTLInput(inkex.EffectExtension):
|
||||
|
||||
def add_arguments(self, pars):
|
||||
pars.add_argument('--tab')
|
||||
|
||||
#Processor
|
||||
pars.add_argument('--slic3r_cmd', default="slic3r", help="Command to invoke slic3r.")
|
||||
|
||||
#Input
|
||||
pars.add_argument('--inputfile', help='Input file (OBJ/OFF/PLY/STL)')
|
||||
pars.add_argument('--scalefactor', type=float, default=1.0, help='Scale the model to custom size')
|
||||
pars.add_argument("--max_num_faces", type=int, default=200, help="If the STL file has too much detail it contains a large number of faces. This will make processing extremely slow. So we can limit it.")
|
||||
pars.add_argument('--layer_height', type=float, default=1.000, help='slic3r layer height, probably in mm. Default: per slic3r config')
|
||||
|
||||
#Transforms
|
||||
pars.add_argument('--rx', type=float, default=None, help='Rotate STL object around X-Axis before importing.')
|
||||
pars.add_argument('--ry', type=float, default=None, help='Rotate STL object around Y-Axis before importing.')
|
||||
pars.add_argument('--rz', type=float, default=None, help='Rotate STL object around Z-Axis before importing.')
|
||||
pars.add_argument('--mirrorx', type=inkex.Boolean, default=False, help='Mirror X')
|
||||
pars.add_argument('--mirrory', type=inkex.Boolean, default=False, help='Mirror Y')
|
||||
|
||||
#Output
|
||||
pars.add_argument('--numbers', type=inkex.Boolean, default=False, help='Add layer numbers.')
|
||||
pars.add_argument('--center', type=inkex.Boolean, default=False, help='Add center marks.')
|
||||
pars.add_argument("--resizetoimport", type=inkex.Boolean, default=True, help="Resize the canvas to the imported drawing's bounding box")
|
||||
pars.add_argument("--extraborder", type=float, default=0.0)
|
||||
pars.add_argument("--extraborder_units")
|
||||
|
||||
#Style
|
||||
pars.add_argument("--use_fill_color", type=inkex.Boolean, default=False, help="Use fill color")
|
||||
pars.add_argument("--fill_color", type=Color, default='1943148287', help="Fill color")
|
||||
pars.add_argument("--min_fill_opacity", type=float, default=0.0, help="Min fill opacity")
|
||||
pars.add_argument("--max_fill_opacity", type=float, default=1.0, help="Max fill opacity")
|
||||
pars.add_argument("--diffuse_fill_opacity", default="regular", help="Diffuse fill opacity per layer")
|
||||
|
||||
pars.add_argument("--use_stroke_color", type=inkex.Boolean, default=True, help="Use stroke color")
|
||||
pars.add_argument('--stroke_color', type=Color, default='879076607', help="Stroke color")
|
||||
|
||||
pars.add_argument("--min_stroke_width", type=float, default=1.0, help="Min stroke width")
|
||||
pars.add_argument("--max_stroke_width", type=float, default=1.0, help="Max stroke width")
|
||||
pars.add_argument("--diffuse_stroke_width", default="regular", help="Diffuse stroke width per layer")
|
||||
|
||||
pars.add_argument("--min_stroke_opacity", type=float, default=0.0, help="Min stroke opacity")
|
||||
pars.add_argument("--max_stroke_opacity", type=float, default=1.0, help="Max stroke opacity")
|
||||
pars.add_argument("--diffuse_stroke_opacity", default="regular", help="Diffuse stroke opacity per layer")
|
||||
|
||||
def effect(self):
|
||||
args = self.options
|
||||
|
||||
if args.min_fill_opacity > args.max_fill_opacity:
|
||||
inkex.utils.debug("Min fill opacity may not be larger than max fill opacity. Adjust and try again!")
|
||||
exit(1)
|
||||
if args.min_stroke_width > args.max_stroke_width:
|
||||
inkex.utils.debug("Min stroke width may not be larger than max stroke width. Adjust and try again!")
|
||||
exit(1)
|
||||
if args.min_stroke_opacity > args.max_stroke_opacity:
|
||||
inkex.utils.debug("Min stroke opacity may not be larger than max stroke opacity. Adjust and try again!")
|
||||
exit(1)
|
||||
|
||||
#############################################
|
||||
# test slic3r command
|
||||
#############################################
|
||||
if sys_platform.startswith('win'):
|
||||
# assert we run the commandline version of slic3r
|
||||
args.slic3r_cmd = re.sub('slic3r(\.exe)?$', 'slic3r-console.exe', args.slic3r_cmd, flags=re.I)
|
||||
cmd = [args.slic3r_cmd, '--version']
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
hint="Maybe use --slic3r-cmd option?"
|
||||
inkex.utils.debug("{0}\nCommand failed: errno={1} {2}\n\n{3}".format(' '.join(cmd), e.errno, e.strerror, hint), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
#############################################
|
||||
# prepare STL input (also accept and convert obj, stl, ply, off)
|
||||
#############################################
|
||||
outputfilebase = os.path.splitext(os.path.basename(args.inputfile))[0]
|
||||
converted_inputfile = os.path.join(tempfile.gettempdir(), outputfilebase + ".stl")
|
||||
if not os.path.exists(args.inputfile):
|
||||
inkex.utils.debug("The input file does not exist.")
|
||||
exit(1)
|
||||
input_mesh = om.read_trimesh(args.inputfile)
|
||||
if input_mesh.n_faces() > args.max_num_faces:
|
||||
inkex.utils.debug("Aborted. Target STL file has " + str(input_mesh.n_faces()) + " faces, but only " + str(args.max_num_faces) + " are allowed.")
|
||||
exit(1)
|
||||
om.write_mesh(converted_inputfile, input_mesh) #read + convert, might throw errors; warning. output is ASCII but cannot controlled to be set to binary because om.Options() is not available in python binding yet
|
||||
if not os.path.exists(converted_inputfile):
|
||||
inkex.utils.debug("The converted input file does not exist.")
|
||||
exit(1)
|
||||
args.inputfile = converted_inputfile #overwrite
|
||||
|
||||
#############################################
|
||||
# create the layer slices
|
||||
#############################################
|
||||
svgfile = re.sub('\.stl', '.svg', args.inputfile, flags=re.IGNORECASE)
|
||||
# option --layer-height does not work. We use --scale instead...
|
||||
scale = 1.0 / args.layer_height
|
||||
cmd = [
|
||||
args.slic3r_cmd,
|
||||
'--no-gui',
|
||||
'--scale', str(scale),
|
||||
'--rotate-x', str(args.rx),
|
||||
'--rotate-y', str(args.ry),
|
||||
'--rotate', str(args.rz),
|
||||
'--first-layer-height', '0.1mm',
|
||||
'--export-svg', '-o', svgfile, args.inputfile]
|
||||
|
||||
def scale_points(pts, scale):
|
||||
""" str='276.422496,309.4 260.209984,309.4 260.209984,209.03 276.422496,209.03' """
|
||||
return re.sub('\d*\.\d*', lambda x: str(float(x.group(0))*scale), pts)
|
||||
|
||||
## CAUTION: keep svg_pathstats() in sync with inkscape-centerlinetrace
|
||||
def svg_pathstats(path_d):
|
||||
""" calculate statistics from an svg path:
|
||||
length (measuring bezier splines as straight lines through the handles).
|
||||
points (all, including duplicates)
|
||||
segments (number of not-connected!) path segments.
|
||||
simple bounding box (ignoring curves of splines, but inclding handles.)
|
||||
"""
|
||||
xmin = 1e99
|
||||
ymin = 1e99
|
||||
xmax = -1e99
|
||||
ymax = -1e99
|
||||
p_points = 0
|
||||
p_length = 0
|
||||
p_segments = 0
|
||||
|
||||
path_d = path_d.lower()
|
||||
for p in path_d.split('m'):
|
||||
|
||||
pp = re.sub('[cl,]', ' ', p)
|
||||
pp,closed = re.subn('z\s*$','',pp)
|
||||
xy = pp.split()
|
||||
if len(xy) < 2:
|
||||
# inkex.utils.debug(len(pp))
|
||||
# inkex.utils.debug("short path error")
|
||||
continue
|
||||
x0 = float(xy[0])
|
||||
y0 = float(xy[1])
|
||||
if x0 > xmax: xmax = x0
|
||||
if x0 < xmin: xmin = x0
|
||||
if y0 > ymax: ymax = y0
|
||||
if y0 < ymin: ymin = y0
|
||||
|
||||
p_points += 1
|
||||
x = xy[2::2]
|
||||
y = xy[3::2]
|
||||
if len(x):
|
||||
p_segments += 1
|
||||
if closed:
|
||||
x.extend(x0)
|
||||
y.extend(y0)
|
||||
|
||||
for i in range(len(x)):
|
||||
p_points += 1
|
||||
dx = float(x[i]) - x0
|
||||
dy = float(y[i]) - y0
|
||||
p_length += math.sqrt( dx * dx + dy * dy )
|
||||
x0,y0 = float(x[i]),float(y[i])
|
||||
if x0 > xmax: xmax = x0
|
||||
if x0 < xmin: xmin = x0
|
||||
if y0 > ymax: ymax = y0
|
||||
if y0 < ymin: ymin = y0
|
||||
|
||||
return { 'points':p_points, 'segments':p_segments, 'length':p_length, 'bbox': (xmin,ymin, xmax, ymax) }
|
||||
|
||||
|
||||
def bbox_info(slic3r, file):
|
||||
cmd = [ slic3r, '--no-gui', '--info', file ]
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
out, err = p.communicate()
|
||||
if len(err) > 0:
|
||||
raise ValueError(err)
|
||||
|
||||
bb = {}
|
||||
for l in out.decode().split("\n"):
|
||||
m = re.match('((min|max)_[xyz])\s*=\s*(.*)', l)
|
||||
if m: bb[m.group(1)] = float(m.group(3))
|
||||
if (len(bb) != 6):
|
||||
raise ValueError("slic3r --info did not return 6 elements for bbox")
|
||||
return bb
|
||||
|
||||
if args.center is True:
|
||||
bb = bbox_info(args.slic3r_cmd, args.inputfile)
|
||||
# Ouch: bbox info gives us stl coordinates. slic3r translates them into svg px using 75dpi.
|
||||
cx = (-bb['min_x'] + bb['max_x']) * 0.5 * 1/scale * 25.4 / 75
|
||||
cy = (-bb['min_y'] + bb['max_y']) * 0.5 * 1/scale * 25.4 / 75
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
raise OSError("{0}\nCommand failed: errno={1} {2}".format(' '.join(cmd), e.errno, e.strerror))
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
if not b'Done.' in stdout:
|
||||
inkex.utils.debug("Command failed: {0}".format(' '.join(cmd)))
|
||||
inkex.utils.debug("OUT: " + str(stdout))
|
||||
inkex.utils.debug("ERR: " + str(stderr))
|
||||
sys.exit(1)
|
||||
|
||||
# slic3r produces correct svg files, but with polygons instead of paths, and with undefined strokes.
|
||||
# When opened with inkscape, most lines are invisible and polygons cannot be edited.
|
||||
# To fix these issues, we postprocess the svg file:
|
||||
# * replace polygon nodes with corresponding path nodes.
|
||||
# * replace style attribute in polygon nodes with one that has a black stroke
|
||||
|
||||
stream = open(svgfile, 'r')
|
||||
p = etree.XMLParser(huge_tree=True)
|
||||
doc = etree.parse(stream, parser=p)
|
||||
stream.close()
|
||||
|
||||
## To change the document units to mm, insert directly after the root node:
|
||||
# e.tag = '{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview'
|
||||
# e.attrib['id'] = "base"
|
||||
# e.attrib['{http://www.inkscape.org/namespaces/inkscape}document-units'] = "mm"
|
||||
|
||||
totalPolygoncount = 0
|
||||
for e in doc.iterfind('//{*}polygon'):
|
||||
totalPolygoncount += 1
|
||||
|
||||
polygoncount = 0
|
||||
|
||||
if args.use_fill_color is False:
|
||||
fill = "none"
|
||||
else:
|
||||
fill = args.fill_color
|
||||
|
||||
for e in doc.iterfind('//{*}polygon'):
|
||||
polygoncount += 1
|
||||
|
||||
if args.diffuse_fill_opacity == "front_to_back":
|
||||
fill_opacity = (args.max_fill_opacity - (polygoncount / totalPolygoncount) * (args.max_fill_opacity - args.min_fill_opacity)) + args.min_fill_opacity
|
||||
elif args.diffuse_fill_opacity == "back_to_front":
|
||||
fill_opacity = ((polygoncount / totalPolygoncount) * (args.max_fill_opacity - args.min_fill_opacity)) + args.min_fill_opacity
|
||||
elif args.diffuse_fill_opacity == "no_diffuse":
|
||||
fill_opacity = args.max_fill_opacity
|
||||
else:
|
||||
inkex.utils.debug("Error: unkown diffuse fill opacity option")
|
||||
exit(1)
|
||||
|
||||
if args.diffuse_stroke_width == "front_to_back":
|
||||
stroke_width = (args.max_stroke_width - (polygoncount / totalPolygoncount) * (args.max_stroke_width - args.min_stroke_width)) + args.min_stroke_width
|
||||
elif args.diffuse_stroke_width == "back_to_front":
|
||||
stroke_width = ((polygoncount / totalPolygoncount) * (args.max_stroke_width - args.min_stroke_width)) + args.min_stroke_width
|
||||
elif args.diffuse_stroke_width == "no_diffuse":
|
||||
stroke_width = args.max_stroke_width
|
||||
else:
|
||||
inkex.utils.debug("Error: unkown diffuse fill opacity option")
|
||||
exit(1)
|
||||
|
||||
if args.diffuse_stroke_opacity == "front_to_back":
|
||||
stroke_opacity = (args.max_stroke_opacity - (polygoncount / totalPolygoncount) * (args.max_stroke_opacity - args.min_stroke_opacity)) + args.min_stroke_opacity
|
||||
elif args.diffuse_stroke_opacity == "back_to_front":
|
||||
stroke_opacity = ((polygoncount / totalPolygoncount) * (args.max_stroke_opacity - args.min_stroke_opacity)) + args.min_stroke_opacity
|
||||
elif args.diffuse_stroke_opacity == "no_diffuse":
|
||||
stroke_opacity = args.max_stroke_opacity
|
||||
else:
|
||||
inkex.utils.debug("Error: unkown diffuse fill opacity option")
|
||||
exit(1)
|
||||
|
||||
if args.use_stroke_color is False:
|
||||
stroke = ""
|
||||
else:
|
||||
stroke = "stroke:{}".format(args.stroke_color)
|
||||
# e.tag = '{http://www.w3.org/2000/svg}polygon'
|
||||
# e.attrib = {'{http://slic3r.org/namespaces/slic3r}type': 'contour', 'points': '276.422496,309.4 260.209984,309.4 260.209984,209.03 276.422496,209.03', 'style': 'fill: white'}
|
||||
e.tag = re.sub('polygon$', 'path', e.tag)
|
||||
e.attrib['id'] = 'polygon%d' % polygoncount
|
||||
e.attrib['{http://www.inkscape.org/namespaces/inkscape}connector-curvature'] = '0'
|
||||
e.attrib['style'] = 'fill:{};fill-opacity:{};{};stroke-opacity:{};stroke-width:{}'.format(fill, fill_opacity, stroke, stroke_opacity, stroke_width)
|
||||
e.attrib['d'] = 'M ' + re.sub(' ', ' L ', scale_points(e.attrib['points'], 1/scale)) + ' Z'
|
||||
|
||||
if args.mirrorx is True:
|
||||
mx = -1
|
||||
else:
|
||||
mx = +1
|
||||
if args.mirrory is True:
|
||||
my = -1
|
||||
else:
|
||||
my = +1
|
||||
|
||||
if args.mirrorx is True or args.mirrory is True:
|
||||
e.attrib['transform'] = 'scale({},{})'.format(mx, my)
|
||||
del e.attrib['points']
|
||||
if e.attrib.get('{http://slic3r.org/namespaces/slic3r}type') == 'contour':
|
||||
# remove contour, but keep all slic3r:type='hole', whatever it is worth later.
|
||||
del e.attrib['{http://slic3r.org/namespaces/slic3r}type']
|
||||
|
||||
layercount = 0
|
||||
for e in doc.iterfind('//{*}g'):
|
||||
if e.attrib['{http://slic3r.org/namespaces/slic3r}z'] and e.attrib['id']:
|
||||
e.attrib['{http://www.inkscape.org/namespaces/inkscape}label'] = e.attrib['id'] + ' slic3r:z=' + e.attrib['{http://slic3r.org/namespaces/slic3r}z']
|
||||
del e.attrib['{http://slic3r.org/namespaces/slic3r}z']
|
||||
# for some fun with our inkscape-paths2openscad extension, add sibling to e:
|
||||
# <svg:desc id="descpoly60">Depth: 1mm\nOffset: 31mm</svg:desc>
|
||||
desc = etree.Element('{http://www.w3.org/2000/svg}desc')
|
||||
desc.attrib['id'] = 'descl'+str(layercount)
|
||||
desc.text = "Depth: %.2fmm\nRaise: %.2fmm\n" % (1/scale, layercount/scale)
|
||||
e.append(desc)
|
||||
layercount+=1
|
||||
if args.numbers is True:
|
||||
num = etree.Element('{http://www.w3.org/2000/svg}text')
|
||||
num.attrib['id'] = 'textnum'+str(layercount)
|
||||
num.attrib['x'] = str(layercount*2)
|
||||
num.attrib['y'] = str(layercount*4+10)
|
||||
num.attrib['style'] = 'fill:#00FF00;fill-opacity:1;stroke:#00FF00;font-family:FreeSans;font-size:10pt;stroke-opacity:1;stroke-width:0.1'
|
||||
num.text = "%d" % layercount
|
||||
e.append(num)
|
||||
if args.center is True:
|
||||
cc = etree.Element('{http://www.w3.org/2000/svg}path')
|
||||
cc.attrib['id'] = 'ccross'+str(layercount)
|
||||
cc.attrib['style'] = 'fill:none;fill-opacity:1;stroke:#0000FF;font-family:FreeSans;font-size:10pt;stroke-opacity:1;stroke-width:0.1'
|
||||
cc.attrib['d'] = 'M %s,%s v 10 M %s,%s h 10 M %s,%s h 4' % (cx, cy-5, cx-5, cy, cx-2, cy+5)
|
||||
e.append(cc)
|
||||
|
||||
etree.cleanup_namespaces(doc.getroot(), top_nsmap={
|
||||
'inkscape': 'http://www.inkscape.org/namespaces/inkscape',
|
||||
'sodipodi': 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'})
|
||||
|
||||
#inkex.utils.debug("{0}: {1} polygons in {2} layers converted to paths.".format(svgfile, polygoncount, layercount))
|
||||
|
||||
if layercount == 0:
|
||||
inkex.utils.debug("No layers imported. Try to lower your layer height")
|
||||
exit(1)
|
||||
#inkex.utils.debug(totalPolygoncount)
|
||||
if totalPolygoncount == 0:
|
||||
inkex.utils.debug("No polygons imported (empty groups). Try to lower your layer height")
|
||||
exit(1)
|
||||
|
||||
stl_group = self.document.getroot().add(inkex.Group(id=self.svg.get_unique_id("slic3r-stl-input-"))) #make a new group at root level
|
||||
for element in doc.getroot().iter("{http://www.w3.org/2000/svg}g"):
|
||||
stl_group.append(element)
|
||||
|
||||
#apply scale factor
|
||||
translation_matrix = [[args.scalefactor, 0.0, 0.0], [0.0, args.scalefactor, 0.0]]
|
||||
stl_group.transform = Transform(translation_matrix) * stl_group.transform
|
||||
|
||||
#adjust canvas to the inserted unfolding
|
||||
if args.resizetoimport:
|
||||
bbox = stl_group.bounding_box() #seems the bbox is not calculated if only one element is in the group. Thats a bug of Inkscape!
|
||||
if bbox is not None:
|
||||
namedView = self.document.getroot().find(inkex.addNS('namedview', 'sodipodi'))
|
||||
root = self.svg.getElement('//svg:svg');
|
||||
offset = self.svg.unittouu(str(args.extraborder) + args.extraborder_units)
|
||||
root.set('viewBox', '%f %f %f %f' % (bbox.left - offset, bbox.top - offset, bbox.width + 2 * offset, bbox.height + 2 * offset))
|
||||
root.set('width', "{:0.6f}{}".format(bbox.width + 2 * offset, self.svg.unit))
|
||||
root.set('height', "{:0.6f}{}".format(bbox.height + 2 * offset, self.svg.unit))
|
||||
else:
|
||||
inkex.utils.debug("Error resizing to canvas.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
SlicerSTLInput().run()
|
||||
Reference in New Issue
Block a user