Merge branch 'master' of lastycah.fablabchemnitz.de:MarioVoigt/mightyscape-1.X
This commit is contained in:
commit
8c8614e4f8
@ -6,6 +6,7 @@ import shutil
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
import shlex
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Extension for InkScape 1.X
|
Extension for InkScape 1.X
|
||||||
@ -36,15 +37,14 @@ DETACHED_PROCESS = 0x00000008
|
|||||||
class AnimateOrder (inkex.EffectExtension):
|
class AnimateOrder (inkex.EffectExtension):
|
||||||
|
|
||||||
def spawnIndependentProcess(self, args): #function to spawn non-blocking inkscape instance. the inkscape command is available because it is added to ENVIRONMENT when Inkscape main instance is started
|
def spawnIndependentProcess(self, args): #function to spawn non-blocking inkscape instance. the inkscape command is available because it is added to ENVIRONMENT when Inkscape main instance is started
|
||||||
|
warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
|
|
||||||
subprocess.Popen(args, close_fds=True, creationflags=DETACHED_PROCESS)
|
subprocess.Popen(args, close_fds=True, creationflags=DETACHED_PROCESS)
|
||||||
warnings.simplefilter("default", ResourceWarning)
|
|
||||||
else:
|
else:
|
||||||
pid = os.fork()
|
cmd = "nohup " + " ".join(args) + " &"
|
||||||
if pid == 0: # new process
|
cmds = shlex.split(cmd)
|
||||||
os.system("nohup " + args + " &")
|
subprocess.Popen(cmds, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
exit()
|
warnings.simplefilter("default", ResourceWarning)
|
||||||
|
|
||||||
def add_arguments(self, pars):
|
def add_arguments(self, pars):
|
||||||
pars.add_argument("--tab")
|
pars.add_argument("--tab")
|
||||||
|
@ -8,6 +8,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
import shlex
|
||||||
import warnings
|
import warnings
|
||||||
import inkex
|
import inkex
|
||||||
import inkex.command
|
import inkex.command
|
||||||
@ -22,144 +23,142 @@ logger = logging.getLogger(__name__)
|
|||||||
DETACHED_PROCESS = 0x00000008
|
DETACHED_PROCESS = 0x00000008
|
||||||
GROUP_ID = 'export_selection_transform'
|
GROUP_ID = 'export_selection_transform'
|
||||||
|
|
||||||
|
|
||||||
class ExportObject(inkex.EffectExtension):
|
class ExportObject(inkex.EffectExtension):
|
||||||
|
|
||||||
def add_arguments(self, pars):
|
def add_arguments(self, pars):
|
||||||
pars.add_argument("--wrap_transform", type=inkex.Boolean, default=False, help="Wrap final document in transform")
|
pars.add_argument("--wrap_transform", type=inkex.Boolean, default=False, help="Wrap final document in transform")
|
||||||
pars.add_argument("--border_offset", type=float, default=1.000, help="Add border offset around selection")
|
pars.add_argument("--border_offset", type=float, default=1.000, help="Add border offset around selection")
|
||||||
pars.add_argument("--export_dir", default="~/inkscape_export/", help="Location to save exported documents")
|
pars.add_argument("--export_dir", default="~/inkscape_export/", help="Location to save exported documents")
|
||||||
pars.add_argument("--opendir", type=inkex.Boolean, default=False, help="Open containing output directory after export")
|
pars.add_argument("--opendir", type=inkex.Boolean, default=False, help="Open containing output directory after export")
|
||||||
pars.add_argument("--dxf_exporter_path", default="/usr/share/inkscape/extensions/dxf_outlines.py", help="Location of dxf_outlines.py")
|
pars.add_argument("--dxf_exporter_path", default="/usr/share/inkscape/extensions/dxf_outlines.py", help="Location of dxf_outlines.py")
|
||||||
pars.add_argument("--export_dxf", type=inkex.Boolean, default=False, help="Create a dxf file")
|
pars.add_argument("--export_dxf", type=inkex.Boolean, default=False, help="Create a dxf file")
|
||||||
pars.add_argument("--export_pdf", type=inkex.Boolean, default=False, help="Create a pdf file")
|
pars.add_argument("--export_pdf", type=inkex.Boolean, default=False, help="Create a pdf file")
|
||||||
pars.add_argument("--newwindow", type=inkex.Boolean, default=False, help="Open file in new Inkscape window")
|
pars.add_argument("--newwindow", type=inkex.Boolean, default=False, help="Open file in new Inkscape window")
|
||||||
|
|
||||||
|
|
||||||
def openExplorer(self, dir):
|
def openExplorer(self, dir):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
Popen(["explorer", dir], close_fds=True, creationflags=DETACHED_PROCESS).wait()
|
Popen(["explorer", dir], close_fds=True, creationflags=DETACHED_PROCESS).wait()
|
||||||
else:
|
else:
|
||||||
Popen(["xdg-open", dir], close_fds=True, start_new_session=True).wait()
|
Popen(["xdg-open", dir], close_fds=True, start_new_session=True).wait()
|
||||||
|
|
||||||
def spawnIndependentInkscape(self, file): #function to spawn non-blocking inkscape instance. the inkscape command is available because it is added to ENVIRONMENT when Inkscape main instance is started
|
def spawnIndependentInkscape(self, file): #function to spawn non-blocking inkscape instance. the inkscape command is available because it is added to ENVIRONMENT when Inkscape main instance is started
|
||||||
if os.name == 'nt':
|
warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
|
||||||
warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
|
if os.name == 'nt':
|
||||||
Popen(["inkscape", file], close_fds=True, creationflags=DETACHED_PROCESS)
|
Popen(["inkscape", file], close_fds=True, creationflags=DETACHED_PROCESS)
|
||||||
warnings.simplefilter("default", ResourceWarning)
|
else:
|
||||||
else:
|
cmd = "nohup inkscape " + file + " &"
|
||||||
pid = os.fork()
|
cmds = shlex.split(cmd)
|
||||||
if pid == 0: # new process
|
subprocess.Popen(cmds, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
os.system("nohup inkscape " + file + " &")
|
warnings.simplefilter("default", ResourceWarning)
|
||||||
exit()
|
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
if not self.svg.selected:
|
if not self.svg.selected:
|
||||||
inkex.errormsg("Selection is empty. Please select some objects first!")
|
inkex.errormsg("Selection is empty. Please select some objects first!")
|
||||||
return
|
return
|
||||||
|
|
||||||
#preflight check for DXF input dir
|
#preflight check for DXF input dir
|
||||||
if not os.path.exists(self.options.dxf_exporter_path):
|
if not os.path.exists(self.options.dxf_exporter_path):
|
||||||
inkex.utils.debug("Location of dxf_outlines.py does not exist. Please select a proper file and try again.")
|
inkex.utils.debug("Location of dxf_outlines.py does not exist. Please select a proper file and try again.")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
export_dir = Path(self.absolute_href(self.options.export_dir))
|
export_dir = Path(self.absolute_href(self.options.export_dir))
|
||||||
os.makedirs(export_dir, exist_ok=True)
|
os.makedirs(export_dir, exist_ok=True)
|
||||||
|
|
||||||
offset = self.options.border_offset
|
offset = self.options.border_offset
|
||||||
|
|
||||||
bbox = inkex.BoundingBox()
|
bbox = inkex.BoundingBox()
|
||||||
|
|
||||||
for elem in self.svg.selected.values():
|
for elem in self.svg.selected.values():
|
||||||
transform = inkex.Transform()
|
transform = inkex.Transform()
|
||||||
parent = elem.getparent()
|
parent = elem.getparent()
|
||||||
if parent is not None and isinstance(parent, inkex.ShapeElement):
|
if parent is not None and isinstance(parent, inkex.ShapeElement):
|
||||||
transform = parent.composed_transform()
|
transform = parent.composed_transform()
|
||||||
try:
|
try:
|
||||||
bbox += elem.bounding_box(transform)
|
bbox += elem.bounding_box(transform)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Bounding box not computed")
|
logger.exception("Bounding box not computed")
|
||||||
logger.info("Skipping bounding box")
|
logger.info("Skipping bounding box")
|
||||||
transform = elem.composed_transform()
|
transform = elem.composed_transform()
|
||||||
x1, y1 = transform.apply_to_point([0, 0])
|
x1, y1 = transform.apply_to_point([0, 0])
|
||||||
x2, y2 = transform.apply_to_point([1, 1])
|
x2, y2 = transform.apply_to_point([1, 1])
|
||||||
bbox += inkex.BoundingBox((x1, x2), (y1, y2))
|
bbox += inkex.BoundingBox((x1, x2), (y1, y2))
|
||||||
|
|
||||||
template = self.create_document()
|
template = self.create_document()
|
||||||
filename = None
|
filename = None
|
||||||
|
|
||||||
group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g')
|
group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g')
|
||||||
group.attrib['id'] = GROUP_ID
|
group.attrib['id'] = GROUP_ID
|
||||||
group.attrib['transform'] = str(inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top))))
|
group.attrib['transform'] = str(inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top))))
|
||||||
|
|
||||||
for elem in self.svg.selected.values():
|
for elem in self.svg.selected.values():
|
||||||
elem_copy = deepcopy(elem)
|
elem_copy = deepcopy(elem)
|
||||||
elem_copy.attrib['transform'] = str(elem.composed_transform())
|
elem_copy.attrib['transform'] = str(elem.composed_transform())
|
||||||
elem_copy.attrib['style'] = str(elem.composed_style())
|
elem_copy.attrib['style'] = str(elem.composed_style())
|
||||||
group.append(elem_copy)
|
group.append(elem_copy)
|
||||||
|
|
||||||
template.attrib['viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}'
|
template.attrib['viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}'
|
||||||
template.attrib['width'] = f'{bbox.width + offset * 2}' + self.svg.unit
|
template.attrib['width'] = f'{bbox.width + offset * 2}' + self.svg.unit
|
||||||
template.attrib['height'] = f'{bbox.height + offset * 2}' + self.svg.unit
|
template.attrib['height'] = f'{bbox.height + offset * 2}' + self.svg.unit
|
||||||
|
|
||||||
if filename is None:
|
if filename is None:
|
||||||
filename = elem.attrib.get('id', None)
|
filename = elem.attrib.get('id', None)
|
||||||
if filename:
|
if filename:
|
||||||
filename = filename.replace(os.sep, '_') + '.svg'
|
filename = filename.replace(os.sep, '_') + '.svg'
|
||||||
if not filename: #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG
|
if not filename: #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG
|
||||||
filename = self.svg.get_unique_id("selection") + '.svg'
|
filename = self.svg.get_unique_id("selection") + '.svg'
|
||||||
|
|
||||||
template.append(group)
|
template.append(group)
|
||||||
|
|
||||||
if not self.options.wrap_transform:
|
if not self.options.wrap_transform:
|
||||||
self.load(inkscape_command(template.tostring(), select=GROUP_ID, verbs=['SelectionUnGroup']))
|
self.load(inkscape_command(template.tostring(), select=GROUP_ID, verbs=['SelectionUnGroup']))
|
||||||
template = self.svg
|
template = self.svg
|
||||||
for child in template.getchildren():
|
for child in template.getchildren():
|
||||||
if child.tag == '{http://www.w3.org/2000/svg}metadata':
|
if child.tag == '{http://www.w3.org/2000/svg}metadata':
|
||||||
template.remove(child)
|
template.remove(child)
|
||||||
|
|
||||||
self.save_document(template, export_dir / filename)
|
self.save_document(template, export_dir / filename)
|
||||||
|
|
||||||
if self.options.opendir is True:
|
if self.options.opendir is True:
|
||||||
self.openExplorer(export_dir)
|
self.openExplorer(export_dir)
|
||||||
|
|
||||||
if self.options.newwindow is True:
|
if self.options.newwindow is True:
|
||||||
#inkscape(os.path.join(export_dir, filename)) #blocking
|
#inkscape(os.path.join(export_dir, filename)) #blocking
|
||||||
self.spawnIndependentInkscape(os.path.join(export_dir, filename)) #non-blocking
|
self.spawnIndependentInkscape(os.path.join(export_dir, filename)) #non-blocking
|
||||||
|
|
||||||
if self.options.export_dxf is True:
|
if self.options.export_dxf is True:
|
||||||
#ensure that python command is available #we pass 25.4/96 which stands for unit mm. See inkex.units.UNITS and dxf_outlines.inx
|
#ensure that python command is available #we pass 25.4/96 which stands for unit mm. See inkex.units.UNITS and dxf_outlines.inx
|
||||||
cmd = [
|
cmd = [
|
||||||
sys.executable, #the path of the python interpreter which is used for this script
|
sys.executable, #the path of the python interpreter which is used for this script
|
||||||
self.options.dxf_exporter_path,
|
self.options.dxf_exporter_path,
|
||||||
'--output=' + os.path.join(export_dir, filename + '.dxf'),
|
'--output=' + os.path.join(export_dir, filename + '.dxf'),
|
||||||
r'--units=25.4/96',
|
r'--units=25.4/96',
|
||||||
os.path.join(export_dir, filename)
|
os.path.join(export_dir, filename)
|
||||||
]
|
]
|
||||||
proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE)
|
proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE)
|
||||||
stdout, stderr = proc.communicate()
|
stdout, stderr = proc.communicate()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
inkex.utils.debug("%d %s %s" % (proc.returncode, stdout, stderr))
|
inkex.utils.debug("%d %s %s" % (proc.returncode, stdout, stderr))
|
||||||
|
|
||||||
if self.options.export_pdf is True:
|
if self.options.export_pdf is True:
|
||||||
cli_output = inkscape(os.path.join(export_dir, filename), actions='export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'.format(file_name=os.path.join(export_dir, filename + '.pdf')))
|
cli_output = inkscape(os.path.join(export_dir, filename), actions='export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'.format(file_name=os.path.join(export_dir, filename + '.pdf')))
|
||||||
if len(cli_output) > 0:
|
if len(cli_output) > 0:
|
||||||
self.msg("Inkscape returned the following output when trying to run the file export; the file export may still have worked:")
|
self.msg("Inkscape returned the following output when trying to run the file export; the file export may still have worked:")
|
||||||
self.msg(cli_output)
|
self.msg(cli_output)
|
||||||
|
|
||||||
def create_document(self):
|
def create_document(self):
|
||||||
document = self.svg.copy()
|
document = self.svg.copy()
|
||||||
for child in document.getchildren():
|
for child in document.getchildren():
|
||||||
if child.tag == '{http://www.w3.org/2000/svg}defs':
|
if child.tag == '{http://www.w3.org/2000/svg}defs':
|
||||||
continue
|
continue
|
||||||
document.remove(child)
|
document.remove(child)
|
||||||
return document
|
return document
|
||||||
|
|
||||||
def save_document(self, document, filename):
|
def save_document(self, document, filename):
|
||||||
with open(filename, 'wb') as fp:
|
with open(filename, 'wb') as fp:
|
||||||
document = document.tostring()
|
document = document.tostring()
|
||||||
fp.write(scourString(document).encode('utf8'))
|
fp.write(scourString(document).encode('utf8'))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ExportObject().run()
|
ExportObject().run()
|
@ -36,6 +36,15 @@ Module licenses
|
|||||||
|
|
||||||
possible import file types -> https://www.graphics.rwth-aachen.de/media/openmesh_static/Documentations/OpenMesh-8.0-Documentation/a04096.html
|
possible import file types -> https://www.graphics.rwth-aachen.de/media/openmesh_static/Documentations/OpenMesh-8.0-Documentation/a04096.html
|
||||||
|
|
||||||
|
todo:
|
||||||
|
- debug coplanar color for edges for some cases
|
||||||
|
- remove empty groups (text)
|
||||||
|
- abort if 0 faces
|
||||||
|
- give hints for joinery preparations (apply transform, ungroup, ...)
|
||||||
|
- update documentation accordingly
|
||||||
|
- make angleRange global for complete unfolding (to match glue pairs between multiple unfoldings)
|
||||||
|
- add angleRange to stats
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Compute the third point of a triangle when two points and all edge lengths are given
|
# Compute the third point of a triangle when two points and all edge lengths are given
|
||||||
|
Reference in New Issue
Block a user