From ad5e3886ba4e202891efc4e20e61e72c54644eb0 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Mon, 3 Aug 2020 02:15:03 +0200 Subject: [PATCH] Added rotation extensions from LaserPrep --- extensions/fablabchemnitz_rotate_helper.py | 59 +++++++++++++++++ extensions/fablabchemnitz_rotate_min_all.inx | 16 +++++ extensions/fablabchemnitz_rotate_min_all.py | 66 +++++++++++++++++++ extensions/fablabchemnitz_rotate_min_bbox.inx | 16 +++++ extensions/fablabchemnitz_rotate_min_bbox.py | 41 ++++++++++++ .../fablabchemnitz_rotate_min_width.inx | 16 +++++ extensions/fablabchemnitz_rotate_min_width.py | 42 ++++++++++++ 7 files changed, 256 insertions(+) create mode 100644 extensions/fablabchemnitz_rotate_helper.py create mode 100644 extensions/fablabchemnitz_rotate_min_all.inx create mode 100644 extensions/fablabchemnitz_rotate_min_all.py create mode 100644 extensions/fablabchemnitz_rotate_min_bbox.inx create mode 100644 extensions/fablabchemnitz_rotate_min_bbox.py create mode 100644 extensions/fablabchemnitz_rotate_min_width.inx create mode 100644 extensions/fablabchemnitz_rotate_min_width.py diff --git a/extensions/fablabchemnitz_rotate_helper.py b/extensions/fablabchemnitz_rotate_helper.py new file mode 100644 index 00000000..776755b9 --- /dev/null +++ b/extensions/fablabchemnitz_rotate_helper.py @@ -0,0 +1,59 @@ +#! /usr/bin/env python3 +''' +Copyright (C) 2019 Grant Patterson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +from math import copysign, cos, pi, sin +from inkex import Transform + +STEPS = 360 + +def rotate_matrix(node, a): + bbox = node.bounding_box() + cx = bbox.center_x / 2.0 + cy = bbox.center_y / 2.0 + return Transform([[cos(a), -sin(a), cx], [sin(a), cos(a), cy]]) * Transform([[1, 0, -cx], [0, 1, -cy]]) + +def optimal_rotations(node): + step = pi / float(STEPS) + bbox = node.bounding_box() + min_width = bbox.right - bbox.left + min_width_angle = None + min_bbox_area = min_width * (bbox.bottom - bbox.top) + min_bbox_area_angle = None + + for i in range(STEPS): + angle = -pi/2.0 + i*step + rotated = node.bounding_box(rotate_matrix(node, angle)) + + width = rotated.width + height = rotated.height + bbox_area = width * height + + if width < min_width: + min_width = width + min_width_angle = angle + if bbox_area < min_bbox_area: + if width > height: + # To keep results similar to min_width_angle, rotate by an + # additional 90 degrees which doesn't affect bbox area. + angle -= copysign(pi/2.0, angle) + + min_bbox_area = bbox_area + min_bbox_area_angle = angle + + return min_width_angle, min_bbox_area_angle \ No newline at end of file diff --git a/extensions/fablabchemnitz_rotate_min_all.inx b/extensions/fablabchemnitz_rotate_min_all.inx new file mode 100644 index 00000000..a45ea511 --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_all.inx @@ -0,0 +1,16 @@ + + + <_name>Rotations - Find All Optimal + fablabchemnitz.de.rotateminall + + all + + + + + + + + diff --git a/extensions/fablabchemnitz_rotate_min_all.py b/extensions/fablabchemnitz_rotate_min_all.py new file mode 100644 index 00000000..fa830a52 --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_all.py @@ -0,0 +1,66 @@ +#! /usr/bin/env python3 +''' +Copyright (C) 2019 Grant Patterson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +import gettext +import sys +import inkex +import fablabchemnitz_rotate_helper +from inkex import Transform +import copy + +debug = False + +error = lambda msg: inkex.errormsg(gettext.gettext(msg)) +if debug: + stderr = lambda msg: sys.stderr.write(msg + '\n') +else: + stderr = lambda msg: None + +class RotateMinAll(inkex.Effect): + def effect(self): + def duplicateNodes(aList): + clones={} + for id,node in aList.items(): + clone=copy.deepcopy(node) + #!!!--> should it be given an id? + #seems to work without this!?! + myid = node.tag.split('}')[-1] + clone.set("id", self.svg.get_unique_id(myid)) + node.getparent().append(clone) + clones[clone.get("id")]=clone + return(clones) + + for nid, node in self.svg.selected.items(): + # set() removes duplicates + angles = set( + # and remove Nones + [x for x in fablabchemnitz_rotate_helper.optimal_rotations(node) + if x is not None]) + # Go backwards so we know if we need to duplicate the node for + # multiple rotations. (We don't want to rotate the main node + # before duplicating it.) + for i, angle in reversed(list(enumerate(angles))): + if i > 0: + # Rotate a duplicate of the node + rotate_node = list(duplicateNodes({nid: node}).items())[0][1] + else: + rotate_node = node + rotate_node.transform = fablabchemnitz_rotate_helper.rotate_matrix(rotate_node, angle) * rotate_node.transform +if __name__ == '__main__': + RotateMinAll().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz_rotate_min_bbox.inx b/extensions/fablabchemnitz_rotate_min_bbox.inx new file mode 100644 index 00000000..4414acfc --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_bbox.inx @@ -0,0 +1,16 @@ + + + <_name>Rotations - Minimum Bounding Box Area + fablabchemnitz.de.rotateminbbox + + all + + + + + + + + diff --git a/extensions/fablabchemnitz_rotate_min_bbox.py b/extensions/fablabchemnitz_rotate_min_bbox.py new file mode 100644 index 00000000..e2817f6f --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_bbox.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python3 +''' +Copyright (C) 2019 Grant Patterson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +import gettext +import sys +import inkex +import fablabchemnitz_rotate_helper +from inkex import Transform + +debug = False + +error = lambda msg: inkex.errormsg(gettext.gettext(msg)) +if debug: + stderr = lambda msg: sys.stderr.write(msg + '\n') +else: + stderr = lambda msg: None + +class RotateMinBBox(inkex.Effect): + def effect(self): + for node in self.svg.selected.values(): + min_bbox_angle = fablabchemnitz_rotate_helper.optimal_rotations(node)[1] + if min_bbox_angle is not None: + node.transform = Transform(fablabchemnitz_rotate_helper.rotate_matrix(node, min_bbox_angle)) * node.transform +if __name__ == '__main__': + RotateMinBBox().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz_rotate_min_width.inx b/extensions/fablabchemnitz_rotate_min_width.inx new file mode 100644 index 00000000..95e857c5 --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_width.inx @@ -0,0 +1,16 @@ + + + <_name>Rotations - Minimum Width + fablabchemnitz.de.rotateminwidth + + all + + + + + + + + diff --git a/extensions/fablabchemnitz_rotate_min_width.py b/extensions/fablabchemnitz_rotate_min_width.py new file mode 100644 index 00000000..a2a0cdb5 --- /dev/null +++ b/extensions/fablabchemnitz_rotate_min_width.py @@ -0,0 +1,42 @@ +#! /usr/bin/env python3 +''' +Copyright (C) 2019 Grant Patterson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +import gettext +import sys +import inkex +import fablabchemnitz_rotate_helper +from inkex import Transform + +debug = False + +error = lambda msg: inkex.errormsg(gettext.gettext(msg)) +if debug: + stderr = lambda msg: sys.stderr.write(msg + '\n') +else: + stderr = lambda msg: None + +class RotateMinWidth(inkex.Effect): + def effect(self): + for node in self.svg.selected.values(): + min_width_angle = fablabchemnitz_rotate_helper.optimal_rotations(node)[0] + if min_width_angle is not None: + node.transform = fablabchemnitz_rotate_helper.rotate_matrix(node, min_width_angle) * node.transform + +if __name__ == '__main__': + RotateMinWidth().run() \ No newline at end of file