diff --git a/extensions/fablabchemnitz/braille-l18n/braille-l18n.inx b/extensions/fablabchemnitz/braille_l18n/braille_l18n.inx similarity index 86% rename from extensions/fablabchemnitz/braille-l18n/braille-l18n.inx rename to extensions/fablabchemnitz/braille_l18n/braille_l18n.inx index 92ad17c9..e36574e0 100644 --- a/extensions/fablabchemnitz/braille-l18n/braille-l18n.inx +++ b/extensions/fablabchemnitz/braille_l18n/braille_l18n.inx @@ -1,7 +1,7 @@ Convert To Localized Braille - fablabchemnitz.de.braille-l18n + fablabchemnitz.de.braille_l18n @@ -20,6 +20,6 @@ \ No newline at end of file diff --git a/extensions/fablabchemnitz/braille-l18n/braille-l18n.py b/extensions/fablabchemnitz/braille_l18n/braille_l18n.py similarity index 100% rename from extensions/fablabchemnitz/braille-l18n/braille-l18n.py rename to extensions/fablabchemnitz/braille_l18n/braille_l18n.py diff --git a/extensions/fablabchemnitz/braille-l18n/meta.json b/extensions/fablabchemnitz/braille_l18n/meta.json similarity index 91% rename from extensions/fablabchemnitz/braille-l18n/meta.json rename to extensions/fablabchemnitz/braille_l18n/meta.json index 5a66fb74..57eb291d 100644 --- a/extensions/fablabchemnitz/braille-l18n/meta.json +++ b/extensions/fablabchemnitz/braille_l18n/meta.json @@ -1,8 +1,8 @@ [ { "name": "Convert To Localized Braille", - "id": "fablabchemnitz.de.braille-l18n", - "path": "braille-l18n", + "id": "fablabchemnitz.de.braille_l18n", + "path": "braille_l18n", "original_name": "Convert to localized Braille", "original_id": "org.inkscape.text.braille-l18n", "license": "BSD-3-Clause License", diff --git a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py index c19d9fee..e3e45c0a 100644 --- a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py +++ b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py @@ -111,9 +111,12 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): csp = CubicSuperPath(subPath) if len(subPath) > 1 and csp[0][0] != csp[0][1]: #avoids pointy paths like M "31.4794 57.6024 Z" replacedelement.path = subPath - replacedelement.set('id', oldId + str(idSuffix)) + if len(subPaths) == 1: + replacedelement.set('id', oldId) + else: + replacedelement.set('id', oldId + str(idSuffix)) + idSuffix += 1 parent.insert(idx, replacedelement) - idSuffix += 1 breakelements.append(replacedelement) element.delete() for child in element.getchildren(): diff --git a/extensions/fablabchemnitz/create_links/create_links.inx b/extensions/fablabchemnitz/create_links/create_links.inx index c17a30f3..85f08dc3 100644 --- a/extensions/fablabchemnitz/create_links/create_links.inx +++ b/extensions/fablabchemnitz/create_links/create_links.inx @@ -20,7 +20,6 @@ - @@ -44,7 +43,6 @@ - @@ -52,7 +50,7 @@ false false - false + true false false diff --git a/extensions/fablabchemnitz/create_links/create_links.py b/extensions/fablabchemnitz/create_links/create_links.py index 351942f5..d432e28f 100644 --- a/extensions/fablabchemnitz/create_links/create_links.py +++ b/extensions/fablabchemnitz/create_links/create_links.py @@ -59,7 +59,7 @@ class LinksCreator(inkex.EffectExtension): pars.add_argument("--length_filter_unit", default="mm", help="Length filter unit") pars.add_argument("--keep_selected", type=inkex.Boolean, default=False, help="Keep selected elements") pars.add_argument("--no_convert", type=inkex.Boolean, default=False, help="Do not create segments (cosmetic gaps only)") - pars.add_argument("--breakapart", type=inkex.Boolean, default=False, help="Performs CTRL + SHIFT + K to break the new output path into it's parts") + pars.add_argument("--breakapart", type=inkex.Boolean, default=True, help="Performs CTRL + SHIFT + K to break the new output path into it's parts. Recommended to enable because default break apart of Inkscape might produce pointy paths.") pars.add_argument("--show_info", type=inkex.Boolean, default=False, help="Print some length and pattern information") pars.add_argument("--skip_errors", type=inkex.Boolean, default=False, help="Skip errors") @@ -83,9 +83,12 @@ class LinksCreator(inkex.EffectExtension): csp = CubicSuperPath(subpath) if len(subpath) > 1 and csp[0][0] != csp[0][1]: #avoids pointy paths like M "31.4794 57.6024 Z" replacedelement.set('d', csp) - replacedelement.set('id', oldId + str(idSuffix)) + if len(subPaths) == 1: + replacedelement.set('id', oldId) + else: + replacedelement.set('id', oldId + str(idSuffix)) + idSuffix += 1 parent.insert(idx, replacedelement) - idSuffix += 1 breakelements.append(replacedelement) parent.remove(element) for child in element.getchildren(): @@ -280,15 +283,12 @@ class LinksCreator(inkex.EffectExtension): length = length - dash idash = (idash + 1) % len(dashes) dash = dashes[idash] - if sub[-1] != sub[i] and sub[i][0] != sub[i][1]: #avoid pointy paths - if idash % 2: - new.append([sub[i]]) - else: - new[-1].append(sub[i]) + if idash % 2: + new.append([sub[i]]) + else: + new[-1].append(sub[i]) i += 1 - if new[-1][0] == new[-1][0]: #avoid pointy paths - new.remove(new[-1]) - + style.pop('stroke-dasharray') element.pop('sodipodi:type') csp = CubicSuperPath(new) diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx new file mode 100644 index 00000000..54d39779 --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx @@ -0,0 +1,48 @@ + + + Grey to MonoAlpha + fablabchemnitz.de.grey_to_monoalpha + + + + + 0x000000ff + + + + + + + + + + + 0 + 1 + + + + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py new file mode 100644 index 00000000..1bbe1ae4 --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# +# Copyright (C) [2021] [Matt Cottam], [mpcottam@raincloud.co.uk] +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +############################################################################## +# Grey To Mono Alpha *** Convert Greys to Monochrome with varying Opacity +############################################################################## + +import math + +import inkex +from inkex import Color + +# Python Standard Libary + +from statistics import mean + + +def get_attributes(self): + for att in dir(self): + inkex.errormsg((att, getattr(self, att))) + + +def rgba_to_bw_rgba(self, my_objects): + apply_to = self.options.apply_to_type_radio + mono_color = self.options.color_picker_mono.to_rgba() + opacity_lower_threshold = self.options.opacity_lower_threshold + opacity_upper_threshold = self.options.opacity_upper_threshold + opacity_range = opacity_upper_threshold - opacity_lower_threshold + + for my_object in my_objects: + + if 'fill' in apply_to and ('fill:none' not in str(my_object.style)): + my_fill_color = my_object.style.get_color(name='fill').to_rgba() + my_fill_color_red = my_fill_color[0] + my_fill_color_green = my_fill_color[1] + my_fill_color_blue = my_fill_color[2] + mean_fill_component_value = mean([my_fill_color_red, my_fill_color_blue, my_fill_color_green]) + + if mean_fill_component_value > 0: + mono_opacity = (1 - (mean_fill_component_value / 256)) * opacity_range + mono_opacity = mono_opacity + opacity_lower_threshold + else: + mono_opacity = opacity_upper_threshold + + my_object.style['fill'] = str(mono_color) + my_object.style['fill-opacity'] = str(mono_opacity) + + if 'stroke' in apply_to and (';stroke:none' not in str(my_object.style)) and ('stroke:' in str(my_object.style)): + my_stroke_color = my_object.style.get_color(name='stroke').to_rgba() + my_stroke_color_red = my_stroke_color[0] + my_stroke_color_green = my_stroke_color[1] + my_stroke_color_blue = my_stroke_color[2] + mean_stroke_component_value = mean([my_stroke_color_red, my_stroke_color_blue, my_stroke_color_green]) + + if mean_stroke_component_value > 0: + mono_opacity = (1 - (mean_stroke_component_value / 256)) * opacity_range + mono_opacity = mono_opacity + opacity_lower_threshold + else: + mono_opacity = opacity_upper_threshold + + my_object.style['stroke'] = str(mono_color) + my_object.style['stroke-opacity'] = str(mono_opacity) + + +class GreyToMonoAlpha(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tab") + pars.add_argument("--color_picker_mono", type=inkex.colors.Color, default=0) + pars.add_argument("--apply_to_type_radio", default=None) + pars.add_argument("--opacity_lower_threshold", type=float, default=0) + pars.add_argument("--opacity_upper_threshold", type=float, default=1) + + def effect(self): + my_objects = self.svg.selected + if len(my_objects) < 1: + self.msg('Please select some paths first.') + return + rgba_to_bw_rgba(self, my_objects) + + +if __name__ == '__main__': + GreyToMonoAlpha().run() diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/meta.json b/extensions/fablabchemnitz/grey_to_monoalpha/meta.json new file mode 100644 index 00000000..9c32922e --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Grey To MonoAlpha", + "id": "fablabchemnitz.de.grey_to_monoalpha", + "path": "grey_to_monoalpha", + "original_name": "Grey to MonoAlpha", + "original_id": "org.inkscape.grey_to_monoalpha", + "license": "GNU GPL v3", + "license_url": "https://gitlab.com/inklinea/grey-to-mono-alpha/-/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/grey_to_monoalpha", + "fork_url": "https://gitlab.com/inklinea/grey-to-mono-alpha", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Grey+to+MonoAlpha", + "inkscape_gallery_url": null, + "main_authors": [ + "gitlab.com/inklinea", + "github.com/vmario89" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/join_paths/join_paths.inx b/extensions/fablabchemnitz/join_paths/join_paths.inx index 46ec34bb..0e263103 100644 --- a/extensions/fablabchemnitz/join_paths/join_paths.inx +++ b/extensions/fablabchemnitz/join_paths/join_paths.inx @@ -1,38 +1,95 @@ - Join Paths / Create Dimples + Join Paths / Create Tabs And Dimples fablabchemnitz.de.join_paths - - true - - - false - - - - - false - false - false - - - - - 45.000 - 4.000 - - - - - - - - false + + + + + true + 0.0100 + + + + false + false + + + + + + + false + false + false + true + + + + + + + + + 45.000 + 4.000 + 45.000 + + + + + + + + + + false + 1 + 40 + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + ../000_about_fablabchemnitz.svg diff --git a/extensions/fablabchemnitz/join_paths/join_paths.py b/extensions/fablabchemnitz/join_paths/join_paths.py index d500bb25..087fd7c9 100644 --- a/extensions/fablabchemnitz/join_paths/join_paths.py +++ b/extensions/fablabchemnitz/join_paths/join_paths.py @@ -74,6 +74,9 @@ def getArrangedIds(pathMap, startPathId): minDist = 9e+100 #A large float closestId = None np = pathMap[nextPathId] + if np[-1] == []: + inkex.utils.debug("Warning. Selection seems to contain invalid paths, e.g. pointy paths like M 54,54 Z. Please check and try again!") + exit(1) npPts = [np[-1][-1][-1]] if(len(orderPathIds) == 1):#compare both the ends for the first path npPts.append(np[0][0][0]) @@ -123,8 +126,10 @@ class JoinPaths(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument("--optimized", type=inkex.Boolean, default=True) + pars.add_argument("--margin", type=float, default=0.0100) pars.add_argument("--add_dimples", type=inkex.Boolean, default=False) pars.add_argument("--draw_dimple_centers", type=inkex.Boolean, default=False) + pars.add_argument("--draw_arcs_as_paths", type=inkex.Boolean, default=False) pars.add_argument("--dimple_invert", type=inkex.Boolean, default=False) pars.add_argument("--dimple_type", default="lines") pars.add_argument("--dimples_to_group", type=inkex.Boolean, default=False) @@ -132,13 +137,21 @@ class JoinPaths(inkex.EffectExtension): pars.add_argument("--dimple_height_mode", default="by_height") pars.add_argument("--dimple_height", type=float, default=4) pars.add_argument("--dimple_angle", type=float, default=45) + pars.add_argument("--dimple_tab_angle", type=float, default=45) + pars.add_argument("--dimple_gap_filter", type=inkex.Boolean, default=False) + pars.add_argument("--dimple_min_gap", type=float, default=1) + pars.add_argument("--dimple_max_gap", type=float, default=40) + pars.add_argument("--dimple_gap_filter_units", default="mm") pars.add_argument("--dimple_height_units", default="mm") pars.add_argument("--tab", default="sampling", help="Tab") def effect(self): - selections = self.svg.selected + selections = self.svg.selected + if len(self.svg.selected) == 0: + self.msg('Please select some paths first.') + return pathNodes = self.document.xpath('//svg:path',namespaces=inkex.NSS) - paths = {p.get('id'): getPartsFromCubicSuper(CubicSuperPath(p.get('d'))) for p in pathNodes } + paths = {p.get('id'): getPartsFromCubicSuper(CubicSuperPath(p.get('d'))) for p in pathNodes } #paths.keys() Order disturbed pathIds = [p.get('id') for p in pathNodes] @@ -163,7 +176,7 @@ class JoinPaths(inkex.EffectExtension): newParts += parts[:] firstElem = elem else: - if(vectCmpWithMargin(start, newParts[-1][-1][-1], margin = .01)): + if(vectCmpWithMargin(start, newParts[-1][-1][-1], margin = self.options.margin)) and self.options.add_dimples is False: newParts[-1] += parts[0] else: if self.options.add_dimples is True: @@ -179,20 +192,7 @@ class JoinPaths(inkex.EffectExtension): newParts[-1].append([newParts[-1][-1][-1], newParts[-1][-1][-1], p2, p2]) newParts[-1] += parts[0] - #angle=self.options.dimple_angle - #p3 = rotate(midPoint, p2, math.radians(angle)) - #p4 = rotate(midPoint, p2, math.radians(360-angle)) - - #add a new dimple - #line = self.svg.get_current_layer().add(inkex.PathElement(id=self.svg.get_unique_id('dimple'))) - #line.set('d', "m{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p3[0], p3[1])) - #line.style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))} - - #add a new dimple - #line = self.svg.get_current_layer().add(inkex.PathElement(id=self.svg.get_unique_id('dimple'))) - #line.set('d', "m{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p4[0], p4[1])) - #line.style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))} - + #get slope, distance and norm slope dx = midPoint[0]-p1[0] dy = midPoint[1]-p1[1] dist = math.sqrt(dx*dx + dy*dy) @@ -202,97 +202,162 @@ class JoinPaths(inkex.EffectExtension): dx2 = p2[0]-p1[0] dy2 = p2[1]-p1[1] dist2 = math.sqrt(dx2*dx2 + dy2*dy2) - if dx2 == 0: slope=sys.float_info.max #vertical else: slope=(p2[1] - p1[1]) / dx2 slope_angle = 90 + math.degrees(math.atan(slope)) - - if self.options.dimple_height_mode == "by_height": - dimple_height = self.svg.unittouu(str(self.options.dimple_height) + self.options.dimple_height_units) - else: - dimple_height = dist * math.sin(math.radians(self.options.dimple_angle)) - - x3 = midPoint[0] + (dimple_height)*dy - y3 = midPoint[1] - (dimple_height)*dx - x4 = midPoint[0] - (dimple_height)*dy - y4 = midPoint[1] + (dimple_height)*dx - - dimple_style = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))} - - if self.options.draw_dimple_centers is True: - #add a new dimple center cross (4 segments) - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp1'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x3, y3)) - line.style = dimple_style - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp2'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x4, y4)) - line.style = dimple_style - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], midPoint[0], midPoint[1])) - line.style = dimple_style - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p2[0], p2[1])) - line.style = dimple_style + if (self.options.dimple_gap_filter is True \ + and dist2 >= self.svg.unittouu(str(self.options.dimple_min_gap) + self.options.dimple_gap_filter_units) \ + and dist2 < self.svg.unittouu(str(self.options.dimple_max_gap) + self.options.dimple_gap_filter_units) + ) \ + or self.options.dimple_gap_filter is False: + if self.options.dimple_height_mode == "by_height": + dimple_height = self.svg.unittouu(str(self.options.dimple_height) + self.options.dimple_height_units) + else: + dimple_height = dist * math.sin(math.radians(self.options.dimple_angle)) + + x3 = midPoint[0] + (dimple_height)*dy + y3 = midPoint[1] - (dimple_height)*dx + x4 = midPoint[0] - (dimple_height)*dy + y4 = midPoint[1] + (dimple_height)*dx + + dimple_style = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))} + + if self.options.draw_dimple_centers is True: + #add a new dimple center cross (4 segments) + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp1'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x3, y3)) + line.style = dimple_style + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp2'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x4, y4)) + line.style = dimple_style + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], midPoint[0], midPoint[1])) + line.style = dimple_style + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p2[0], p2[1])) + line.style = dimple_style + + if self.options.dimple_type == "lines": + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_line'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], p2[0], p2[1])) + line.style = dimple_style + + if self.options.dimple_type == "peaks": + if self.options.dimple_invert is True: + x5 = x3 + y5 = y3 + x3 = x4 + y3 = y4 + x4 = x5 + y4 = y5 + #add a new dimple center + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_peak'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x3, y3, p2[0], p2[1])) + line.style = dimple_style + + if self.options.draw_both_sides is True: + #add a new opposite dimple center + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_peak'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x4, y4, p2[0], p2[1])) + line.style = dimple_style - if self.options.dimple_type == "lines": - if self.options.dimple_invert is True: - x5 = x3 - y5 = y3 - x3 = x4 - y3 = y4 - x4 = x5 - y4 = y5 - #add a new dimple center - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x3, y3, p2[0], p2[1])) - line.style = dimple_style - - if self.options.draw_both_sides is True: - #add a new opposite dimple center - line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple'))) - line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x4, y4, p2[0], p2[1])) - line.style = dimple_style - else: - ellipse = dimpleGroup.add(inkex.Ellipse(id=self.svg.get_unique_id('dimple'))) - ellipse.set('transform', "rotate({:0.6f} {:0.6f} {:0.6f})".format(slope_angle, midPoint[0], midPoint[1])) - ellipse.set('sodipodi:arc-type', "arc") - ellipse.set('sodipodi:type', "arc") - ellipse.set('sodipodi:cx', "{:0.6f}".format(midPoint[0])) - ellipse.set('sodipodi:cy', "{:0.6f}".format(midPoint[1])) - ellipse.set('sodipodi:rx', "{:0.6f}".format(dimple_height)) - ellipse.set('sodipodi:ry', "{:0.6f}".format(dist2 / 2)) - if self.options.dimple_invert is True: - ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(90.0))) - ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(270.0))) - else: - ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(270.0))) - ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(90.0))) - ellipse.style = dimple_style - - if self.options.draw_both_sides is True: - ellipse = dimpleGroup.add(inkex.Ellipse(id=self.svg.get_unique_id('dimple'))) - ellipse.set('transform', "rotate({:0.6f} {:0.6f} {:0.6f})".format(slope_angle, midPoint[0], midPoint[1])) - ellipse.set('sodipodi:arc-type', "arc") - ellipse.set('sodipodi:type', "arc") - ellipse.set('sodipodi:cx', "{:0.6f}".format(midPoint[0])) - ellipse.set('sodipodi:cy', "{:0.6f}".format(midPoint[1])) - ellipse.set('sodipodi:rx', "{:0.6f}".format(dimple_height)) - ellipse.set('sodipodi:ry', "{:0.6f}".format(dist2 / 2)) - if self.options.dimple_invert is True: - ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(270.0))) - ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(90.0))) - else: - ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(90.0))) - ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(270.0))) - ellipse.style = dimple_style - - #cleanup groups - if len(dimpleGroup) == 1: ##move up child if group has only one child - for child in dimpleGroup: - dimpleGroup.getparent().insert(elem.getparent().index(elem), child) - dimpleGroup.delete() #delete the empty group now + elif self.options.dimple_type == "arcs": + if self.options.draw_arcs_as_paths is False: + ellipse = dimpleGroup.add(inkex.Ellipse(id=self.svg.get_unique_id('dimple_arc'))) + ellipse.set('transform', "rotate({:0.6f} {:0.6f} {:0.6f})".format(slope_angle, midPoint[0], midPoint[1])) + ellipse.set('sodipodi:arc-type', "arc") + ellipse.set('sodipodi:type', "arc") + ellipse.set('sodipodi:cx', "{:0.6f}".format(midPoint[0])) + ellipse.set('sodipodi:cy', "{:0.6f}".format(midPoint[1])) + ellipse.set('sodipodi:rx', "{:0.6f}".format(dimple_height)) + ellipse.set('sodipodi:ry', "{:0.6f}".format(dist2 / 2)) + if self.options.dimple_invert is True: + ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(90.0))) + ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(270.0))) + else: + ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(270.0))) + ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(90.0))) + ellipse.style = dimple_style + + if self.options.draw_both_sides is True: + ellipse = dimpleGroup.add(inkex.Ellipse(id=self.svg.get_unique_id('dimple_arc'))) + ellipse.set('transform', "rotate({:0.6f} {:0.6f} {:0.6f})".format(slope_angle, midPoint[0], midPoint[1])) + ellipse.set('sodipodi:arc-type', "arc") + ellipse.set('sodipodi:type', "arc") + ellipse.set('sodipodi:cx', "{:0.6f}".format(midPoint[0])) + ellipse.set('sodipodi:cy', "{:0.6f}".format(midPoint[1])) + ellipse.set('sodipodi:rx', "{:0.6f}".format(dimple_height)) + ellipse.set('sodipodi:ry', "{:0.6f}".format(dist2 / 2)) + if self.options.dimple_invert is True: + ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(270.0))) + ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(90.0))) + else: + ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(90.0))) + ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(270.0))) + ellipse.style = dimple_style + else: #if draw_arcs_as_paths is True + # +--- x-end point + # | + # counterclockwise ---+ | +--- y-end point + # | | | + # + # | | | | + # 1 Radius x-Axis ---+ | | +--- 4 short / long way + # | | + # 2 Radius y-Axis ---+ +--- 3 Rotation x + if self.options.dimple_invert is True: + b1 = 1 + b2 = 0 + else: + b1 = 0 + b2 = 1 + ellipse = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_arc'))) + ellipse.set('d', "M {:0.6f} {:0.6f} A {:0.6f} {:0.6f} {:0.6f} 0 {} {:0.6f} {:0.6f}".format(p1[0], p1[1], dimple_height, dist2 / 2, slope_angle, b1, p2[0], p2[1])) + ellipse.style = dimple_style + if self.options.draw_both_sides is True: + ellipse = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_arc'))) + ellipse.set('d', "M {:0.6f} {:0.6f} A {:0.6f} {:0.6f} {:0.6f} 0 {} {:0.6f} {:0.6f}".format(p1[0], p1[1], dimple_height, dist2 / 2, slope_angle, b2, p2[0], p2[1])) + ellipse.style = dimple_style + + elif self.options.dimple_type == "tabs": + pbottom1 = [p1[0] + (dimple_height)*dy, p1[1] - (dimple_height)*dx] + pbottom2 = [p2[0] + (dimple_height)*dy, p2[1] - (dimple_height)*dx] + ptop1 = [p1[0] - (dimple_height)*dy, p1[1] + (dimple_height)*dx] + ptop2 = [p2[0] - (dimple_height)*dy, p2[1] + (dimple_height)*dx] + + l_hypo = dimple_height / (math.cos(math.radians(90.0 - self.options.dimple_tab_angle))) + pbottom1 = rotate(p1, [p1[0] + l_hypo * dx, p1[1] + l_hypo * dy], math.radians(-self.options.dimple_tab_angle)) + pbottom2 = rotate(p2, [p2[0] - l_hypo * dx, p2[1] - l_hypo * dy], math.radians(-360.0 + self.options.dimple_tab_angle)) + ptop1 = rotate(p1, [p1[0] + l_hypo * dx, p1[1] + l_hypo * dy], math.radians(self.options.dimple_tab_angle)) + ptop2 = rotate(p2, [p2[0] - l_hypo * dx, p2[1] - l_hypo * dy], math.radians(360.0 - self.options.dimple_tab_angle)) + + + if self.options.dimple_invert is True: + ptemp1 = pbottom1 + ptemp2 = pbottom2 + pbottom1 = ptop1 + pbottom2 = ptop2 + ptop1 = ptemp1 + ptop2 = ptemp2 + + #add a new tab + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_tab'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format( + p1[0], p1[1], pbottom1[0], pbottom1[1], pbottom2[0], pbottom2[1], p2[0], p2[1])) + line.style = dimple_style + if self.options.draw_both_sides is True: + line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_tab'))) + line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format( + p1[0], p1[1], ptop1[0], ptop1[1], ptop2[0], ptop2[1], p2[0], p2[1])) + line.style = dimple_style + + #cleanup groups + if len(dimpleGroup) == 1: ##move up child if group has only one child + for child in dimpleGroup: + dimpleGroup.getparent().insert(elem.getparent().index(elem), child) + dimpleGroup.delete() #delete the empty group now else: newParts[-1].append([newParts[-1][-1][-1], newParts[-1][-1][-1], start, start]) diff --git a/extensions/fablabchemnitz/join_paths/meta.json b/extensions/fablabchemnitz/join_paths/meta.json index cf2e3771..8e0e02f0 100644 --- a/extensions/fablabchemnitz/join_paths/meta.json +++ b/extensions/fablabchemnitz/join_paths/meta.json @@ -1,6 +1,6 @@ [ { - "name": "Join Paths / Create Dimples", + "name": "Join Paths / Create Tabs And Dimples", "id": "fablabchemnitz.de.join_paths", "path": "join_paths", "original_name": "Join Paths Optimized", diff --git a/extensions/fablabchemnitz/paths_to_lowlevel_strokes/meta.json b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/meta.json new file mode 100644 index 00000000..28ca7913 --- /dev/null +++ b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/meta.json @@ -0,0 +1,19 @@ +[ + { + "name": "Paths To Lowlevel Strokes", + "id": "fablabchemnitz.de.paths_to_lowlevel_strokes", + "path": "paths_to_lowlevel_strokes", + "original_name": "Paths To Lowlevel Strokes", + "original_id": "fablabchemnitz.de.paths_to_lowlevel_strokes", + "license": "GNU GPL v3", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "comment": "Created by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/paths_to_strokes", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Paths+To+Lowlevel+Strokes", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/vmario89" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.inx b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.inx new file mode 100644 index 00000000..1b7ce685 --- /dev/null +++ b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.inx @@ -0,0 +1,20 @@ + + + Paths To Lowlevel Strokes + fablabchemnitz.de.paths_to_lowlevel_strokes + true + 0.100 + 3 + true + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.py b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.py new file mode 100644 index 00000000..9d9489ef --- /dev/null +++ b/extensions/fablabchemnitz/paths_to_lowlevel_strokes/paths_to_lowlevel_strokes.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + + +from lxml import etree +import inkex +from inkex import bezier, PathElement +from inkex.paths import CubicSuperPath, Path +import copy + +class PathsToStrokes(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--flattenbezier", type=inkex.Boolean, default=False, help="Flatten bezier curves to polylines") + pars.add_argument("--flatness", type=float, default=0.1, help="Minimum flatness = 0.1. The smaller the value the more fine segments you will get (quantization).") + pars.add_argument("--decimals", type=int, default=3) + pars.add_argument("--keep_style", type=inkex.Boolean, default=False) + + def effect(self): + + def flatten(node): + path = node.path.transform(node.composed_transform()).to_superpath() + bezier.cspsubdiv(path, self.options.flatness) + newpath = [] + for subpath in path: + first = True + for csp in subpath: + cmd = 'L' + if first: + cmd = 'M' + first = False + newpath.append([cmd, [csp[1][0], csp[1][1]]]) + node.path = newpath + + def break_contours(element, breakelements = None): + if breakelements == None: + breakelements = [] + if element.tag == inkex.addNS('path','svg'): + if self.options.flattenbezier is True: + flatten(element) + parent = element.getparent() + idx = parent.index(element) + idSuffix = 0 + raw = element.path.to_arrays() + subPaths = [] + prev = 0 + for i in range(len(raw)): # Breaks compound paths into simple paths + if raw[i][0] == 'M' and i != 0: + subPath = raw[prev:i] + subPaths.append(Path(subPath)) + prev = i + subPaths.append(Path(raw[prev:])) #finally add the last path + for subPath in subPaths: + replacedelement = copy.copy(element) + oldId = replacedelement.get('id') + csp = CubicSuperPath(subPath) + if len(subPath) > 1 and csp[0][0] != csp[0][1]: #avoids pointy paths like M "31.4794 57.6024 Z" + replacedelement.path = subPath + if len(subPaths) == 1: + replacedelement.set('id', oldId) + else: + replacedelement.set('id', oldId + str(idSuffix)) + idSuffix += 1 + parent.insert(idx, replacedelement) + breakelements.append(replacedelement) + element.delete() + for child in element.getchildren(): + break_contours(child, breakelements) + return breakelements + + if len(self.svg.selected) == 0: + elementsToWork = break_contours(self.document.getroot()) + else: + elementsToWork = None + for element in self.svg.selected.values(): + elementsToWork = break_contours(element, elementsToWork) + + for element in elementsToWork: + oldId = element.get('id') + oldStyle = element.style + path = element.path.to_absolute().to_arrays() #to_arrays() is deprecated. How to make more modern? + pathIsClosed = False + if path[-1][0] == 'Z' or \ + (path[-1][0] == 'L' and path[0][1] == path[-1][1]) or \ + (path[-1][0] == 'C' and path[0][1] == [path[-1][1][-2], path[-1][1][-1]]) \ + : #if first is last point the path is also closed. The "Z" command is not required + pathIsClosed = True + parent = element.getparent() + idx = parent.index(element) + element.delete() + + if len(path) == 2 and pathIsClosed is False: + ll = inkex.Line(id=oldId) + ll.set('x1', '{:0.{dec}f}'.format(path[0][1][0], dec=self.options.decimals)) + ll.set('y1', '{:0.{dec}f}'.format(path[0][1][1], dec=self.options.decimals)) + ll.set('x2', '{:0.{dec}f}'.format(path[1][1][0], dec=self.options.decimals)) + ll.set('y2', '{:0.{dec}f}'.format(path[1][1][1], dec=self.options.decimals)) + + if len(path) > 2 and pathIsClosed is False: + ll = inkex.Polyline(id=oldId) + points = "" + for i in range(0, len(path)): + points += '{:0.{dec}f},{:0.{dec}f} '.format(path[i][1][0], path[i][1][1], dec=self.options.decimals) + ll.set('points', points) + + if len(path) > 2 and pathIsClosed is True: + ll = inkex.Polygon(id=oldId) + points = "" + for i in range(0, len(path) - 1): + points += '{:0.{dec}f},{:0.{dec}f} '.format(path[i][1][0], path[i][1][1], dec=self.options.decimals) + ll.set('points', points) + if self.options.keep_style is True: + ll.style = oldStyle + else: + ll.style = "fill:none;stroke:#0000FF;stroke-width:" + str(self.svg.unittouu("1px")) + parent.insert(idx, ll) + +if __name__ == '__main__': + PathsToStrokes().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.inx b/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.inx index c480fe8b..168e516c 100644 --- a/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.inx +++ b/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.inx @@ -5,6 +5,7 @@ + diff --git a/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.py b/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.py index cc055605..ac63536c 100644 --- a/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.py +++ b/extensions/fablabchemnitz/styles_to_layers/styles_to_layers.py @@ -8,7 +8,7 @@ Features Author: Mario Voigt / FabLab Chemnitz Mail: mario.voigt@stadtfabrikanten.org Date: 19.08.2020 -Last patch: 11.04.2021 +Last patch: 17.10.2021 License: GNU GPL v3 """ import inkex @@ -115,7 +115,11 @@ class StylesToLayers(inkex.EffectExtension): #the Styles to Layers extension still might brick the gradients (some tests failed) if style and element.tag != inkex.addNS('stop','svg') and element.tag != inkex.addNS('tspan','svg'): - if self.options.separateby == "stroke": + if self.options.separateby == "element_tag": + neutral_value = 1 + layer_name = "element_tag-" + element.tag.replace("{http://www.w3.org/2000/svg}", "") + + elif self.options.separateby == "stroke": stroke = re.search('(;|^)stroke:(.*?)(;|$)', style) if stroke is not None: stroke = stroke[0] @@ -287,12 +291,18 @@ class StylesToLayers(inkex.EffectExtension): for newLayerNode in topLevelLayerNodeList: newLayerNode[0].append(newLayerNode[1]) #append newlayer to layer + #clean all empty layers from node list. Please note that the following remove_empty_groups + #call does not apply for this so we need to do it as PREVIOUS step before! + for i in range(0, len(layerNodeList)): + if len(layerNodeList[i][0]) == 0: + layerNodeList[i][0].getparent().remove(layerNodeList[i][0]) + if self.options.cleanup == True: try: import remove_empty_groups remove_empty_groups.RemoveEmptyGroups.effect(self) except: self.msg("Calling 'Remove Empty Groups' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...") - + if __name__ == '__main__': StylesToLayers().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/unwind_paths/unwind_paths.inx b/extensions/fablabchemnitz/unwind_paths/unwind_paths.inx index ad2bc66c..e698177e 100644 --- a/extensions/fablabchemnitz/unwind_paths/unwind_paths.inx +++ b/extensions/fablabchemnitz/unwind_paths/unwind_paths.inx @@ -5,7 +5,7 @@ - false + false false false diff --git a/extensions/fablabchemnitz/unwind_paths/unwind_paths.py b/extensions/fablabchemnitz/unwind_paths/unwind_paths.py index 85dfb564..046ed252 100644 --- a/extensions/fablabchemnitz/unwind_paths/unwind_paths.py +++ b/extensions/fablabchemnitz/unwind_paths/unwind_paths.py @@ -39,7 +39,7 @@ class UnwindPaths(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument('--tab') - pars.add_argument('--keep_original', type=inkex.Boolean, default=False, help="If selected, the original paths get deleted") + pars.add_argument('--keep_original', type=inkex.Boolean, default=False, help="If not selected, the original paths get deleted") pars.add_argument('--break_apart', type=inkex.Boolean, default=False, help="Split each path into single curve segments") pars.add_argument('--break_only', type=inkex.Boolean, default=False, help="Only splits root paths into segments (no unwinding)") pars.add_argument('--colorize', type=inkex.Boolean, default=False, help="Colorize original paths and glue pairs") @@ -73,9 +73,12 @@ class UnwindPaths(inkex.EffectExtension): csp = CubicSuperPath(subpath) if len(subpath) > 1 and csp[0][0] != csp[0][1]: #avoids pointy paths like M "31.4794 57.6024 Z" replacedelement.set('d', csp) - replacedelement.set('id', oldId + str(idSuffix)) + if len(subPaths) == 1: + replacedelement.set('id', oldId) + else: + replacedelement.set('id', oldId + str(idSuffix)) + idSuffix += 1 parent.insert(idx, replacedelement) - idSuffix += 1 breakelements.append(replacedelement) parent.remove(element) else: @@ -228,7 +231,7 @@ class UnwindPaths(inkex.EffectExtension): self.drawline(d, "segmented-" + element.get('id'), newOriginalPathGroup, new_original_line_style) if self.options.keep_original is False: - element.delete() + element.delete() else: self.msg('Please select some paths first.')