This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.py
2021-10-28 03:42:25 +02:00

200 lines
10 KiB
Python

#!/usr/bin/env python3
'''
Extension for InkScape 1.0
Features
- This tool is a helper to adjust the document border including an offset value, which is added.
Sending vector data to Epilog Dashboard often results in trimmed paths. This leads to wrong geometry where the laser misses to cut them.
So we add a default (small) amount of 1.0 doc units to expand the document's canvas
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
Date: 21.04.2021
Last patch: 28.10.2021
License: GNU GPL v3
#known bugs:
- viewbox/width/height do not correctly apply if documents only contain an object (not a path). After converting it to path it works. Seems to be a bbox problem
- note from 07.05.2021: seems if the following order is viewBox/width/height, or width/height/viewBox, the units are not respected. So me mess around a little bit
#Todo
- add some way to handle translations properly
'''
import math
import sys
import inkex
from inkex import Transform
sys.path.append("../apply_transformations")
class EpilogDashboardBboxAdjust(inkex.EffectExtension):
def getElementChildren(self, element, elements = None):
if elements == None:
elements = []
if element.tag != inkex.addNS('g','svg'):
elements.append(element)
for child in element.getchildren():
self.getElementChildren(child, elements)
return elements
def add_arguments(self, pars):
pars.add_argument("--tab")
pars.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting")
pars.add_argument("--offset", type=float, default="1.0", help="XY Offset (mm) from top left corner")
pars.add_argument("--removal", default="none", help="Remove all elements outside the bounding box or selection")
pars.add_argument("--use_machine_size", type=inkex.Boolean, default=False, help="Use machine size")
pars.add_argument("--machine_size", default="812x508", help="Machine/Size (mm)")
pars.add_argument("--debug", type=inkex.Boolean, default=False, help="Debug output")
pars.add_argument("--skip_errors", type=inkex.Boolean, default=False, help="Skip on errors")
def effect(self):
applyTransformationsAvailable = False # at first we apply external extension
try:
import apply_transformations
applyTransformationsAvailable = True
except Exception as e:
# self.msg(e)
self.msg("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...")
if self.options.apply_transformations is True and applyTransformationsAvailable is True:
apply_transformations.ApplyTransformations().recursiveFuseTransform(self.document.getroot())
offset = self.options.offset
units = self.svg.unit
scale_factor = self.svg.unittouu("1px")
#https://wiki.inkscape.org/wiki/Units_In_Inkscape
# create a new bounding box and get the bbox size of all elements of the document (we cannot use the page's bbox)
bbox = inkex.BoundingBox()
if len(self.svg.selected) > 0:
#bbox = self.svg.selection.bounding_box() #it could be so easy! But ...
for element in self.svg.selected.values():
'''
...rectangles cause some strangle scaling issue, offendingly caused by namedview units.
The rectangle attributes are set in px. They ignore the real units from namedview.
Strange fact: ellipses, spirals and other primitives work flawlessly.
'''
if isinstance (element, inkex.Rectangle) or \
isinstance (element, inkex.Circle) or \
isinstance (element, inkex.Ellipse):
bbox += element.bounding_box() * scale_factor
elif isinstance (element, inkex.TextElement) or \
isinstance (element, inkex.Tspan):
if self.options.skip_errors is False:
self.msg("Text elements are not supported!")
return
else:
continue
else:
bbox += element.bounding_box()
else:
#for element in self.svg.root.getchildren():
for element in self.document.getroot().iter("*"):
if isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('use','svg') and element.get('inkscape:groupmode') != 'layer': #bbox fails for svg:use elements and layers:
if isinstance (element, inkex.Rectangle) or \
isinstance (element, inkex.Circle) or \
isinstance (element, inkex.Ellipse):
bbox += element.bounding_box() * scale_factor
elif isinstance (element, inkex.TextElement) or \
isinstance (element, inkex.Tspan):
if self.options.skip_errors is False:
self.msg("Text elements are not supported!")
return
else:
continue
else:
bbox += element.bounding_box()
if abs(bbox.width) == math.inf or abs(bbox.height) == math.inf:
inkex.utils.debug("Error while calculating overall bounding box! Check your element types. Things like svg:text or svg:use are not supported. Impossible to continue!")
return
#if len(self.svg.selected) > 0:
# selected = self.svg.selected
#else:
# selected = self.svg.root.getchildren()
#for element in selected:
# transform = inkex.Transform()
# parent = element.getparent()
# if parent is not None and isinstance(parent, inkex.ShapeElement):
# transform = parent.composed_transform()
# try:
# '''
# ...rectangles cause some strangle scaling issue, offendingly caused by namedview units.
# The rectangle attributes are set in px. They ignore the real units from namedview.
# Strange fact: ellipses, spirals and other primitives work flawlessly.
# '''
# if isinstance (element, inkex.Rectangle) or isinstance (element, inkex.TextElement):
# bbox += element.bounding_box(transform) * scale_factor
# else:
# bbox += element.bounding_box(transform)
# except Exception:
# logger.exception("Bounding box not computed")
# logger.info("Skipping bounding box")
# transform = element.composed_transform()
# x1, y1 = transform.apply_to_point([0, 0])
# x2, y2 = transform.apply_to_point([1, 1])
# bbox += inkex.BoundingBox((x1, x2), (y1, y2))
# adjust the viewBox to the bbox size and add the desired offset
if self.options.use_machine_size is True:
machineWidth = self.svg.unittouu(self.options.machine_size.split('x')[0] + "mm")
machineHeight = self.svg.unittouu(self.options.machine_size.split('x')[1] + "mm")
width = f'{machineWidth}' + units
height = f'{machineHeight}' + units
viewBoxXmin = -offset
viewBoxYmin = -offset
viewBoxXmax = machineWidth
viewBoxYmax = machineHeight
else:
width = f'{bbox.width + offset * 2}' + units
height = f'{bbox.height + offset * 2}' + units
viewBoxXmin = -offset
viewBoxYmin = -offset
viewBoxXmax = bbox.width + offset * 2
viewBoxYmax = bbox.height + offset * 2
self.document.getroot().attrib['width'] = width
self.document.getroot().attrib['viewBox'] = f'{viewBoxXmin} {viewBoxYmin} {viewBoxXmax} {viewBoxYmax}'
self.document.getroot().attrib['height'] = height
# translate all elements to fit the adjusted viewBox
mat = Transform("translate(%f, %f)" % (-bbox.left,-bbox.top))
for element in self.document.getroot().iter("*"):
if isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('g', 'svg'):
element.transform = Transform(mat) * element.composed_transform()
if self.options.removal == "outside_canvas":
for element in self.document.getroot().iter("*"):
if isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('g', 'svg'):
ebbox = element.bounding_box()
#self.msg("{} | bbox: left = {:0.3f} right = {:0.3f} top = {:0.3f} bottom = {:0.3f}".format(element.get('id'), ebbox.left, ebbox.right, ebbox.top, ebbox.bottom))
#check if the element's bbox is inside the view canvas. If not: delete it!
if ebbox.right < viewBoxXmin or \
ebbox.left > viewBoxXmax or \
ebbox.top < viewBoxYmin or \
ebbox.bottom > viewBoxYmax:
if self.options.debug is True:
self.msg("Removing {} {}".format(element.get('id'), ebbox))
element.delete()
elif self.options.removal == "outside_selection":
if len(self.svg.selected) == 0:
inkex.utils.debug("Your selection is empty but you have chosen the option to remove all elements outside selection!")
return
allElements = []
for selected in self.svg.selection:
allElements = self.getElementChildren(selected, allElements)
for element in self.document.getroot().iter("*"):
if element not in allElements and isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('g', 'svg'):
if self.options.debug is True:
self.msg("Removing {}".format(element.get('id')))
element.delete()
if __name__ == '__main__':
EpilogDashboardBboxAdjust().run()