From 60789871de29ded666508280a2ef4f9dcf0b33ed Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Tue, 9 Nov 2021 16:27:19 +0100 Subject: [PATCH 1/7] add open dir extension --- .../animate_order/animate_order.py | 2 +- extensions/fablabchemnitz/open_dir/meta.json | 20 +++++++++++ .../fablabchemnitz/open_dir/open_dir.inx | 14 ++++++++ .../fablabchemnitz/open_dir/open_dir.py | 36 +++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 extensions/fablabchemnitz/open_dir/meta.json create mode 100644 extensions/fablabchemnitz/open_dir/open_dir.inx create mode 100644 extensions/fablabchemnitz/open_dir/open_dir.py diff --git a/extensions/fablabchemnitz/animate_order/animate_order.py b/extensions/fablabchemnitz/animate_order/animate_order.py index 84bd5d90..ed7501e9 100644 --- a/extensions/fablabchemnitz/animate_order/animate_order.py +++ b/extensions/fablabchemnitz/animate_order/animate_order.py @@ -36,7 +36,7 @@ DETACHED_PROCESS = 0x00000008 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): warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback" if os.name == 'nt': subprocess.Popen(args, close_fds=True, creationflags=DETACHED_PROCESS) diff --git a/extensions/fablabchemnitz/open_dir/meta.json b/extensions/fablabchemnitz/open_dir/meta.json new file mode 100644 index 00000000..399e06c7 --- /dev/null +++ b/extensions/fablabchemnitz/open_dir/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Open Extension Directory", + "id": "fablabchemnitz.de.open_dir", + "path": "open_dir", + "dependent_extensions": null, + "original_name": "Animate Order", + "original_id": "fablabchemnitz.de.open_dir", + "license": "GNU GPL v3", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "comment": "Written by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/animate_order", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Open+Dir", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/vmario89" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/open_dir/open_dir.inx b/extensions/fablabchemnitz/open_dir/open_dir.inx new file mode 100644 index 00000000..f596de70 --- /dev/null +++ b/extensions/fablabchemnitz/open_dir/open_dir.inx @@ -0,0 +1,14 @@ + + + Open Extension Directory + fablabchemnitz.de.open_dir + + all + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/open_dir/open_dir.py b/extensions/fablabchemnitz/open_dir/open_dir.py new file mode 100644 index 00000000..30375963 --- /dev/null +++ b/extensions/fablabchemnitz/open_dir/open_dir.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import subprocess +import os +import sys +import warnings +import inkex + +DETACHED_PROCESS = 0x00000008 + +class OpenExtensionDirectory(inkex.EffectExtension): + + def spawnIndependentProcess(self, args): + warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback" + if os.name == 'nt': + subprocess.Popen(args, close_fds=True, creationflags=DETACHED_PROCESS) + else: + subprocess.Popen(args, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + warnings.simplefilter("default", ResourceWarning) + + def effect(self): + extension_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', '..') + + if os.name == 'nt': + explorer = "explorer" + else: + explorer = "xdg-open" + args = [explorer, extension_dir] + try: + self.spawnIndependentProcess(args) + except FileNotFoundError as e: + inkex.utils.debug(e) + exit(1) + +if __name__ == '__main__': + OpenExtensionDirectory().run() \ No newline at end of file From 46f579af3d58ba19f9334845ac1c41dcf7540d7a Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Tue, 9 Nov 2021 16:29:58 +0100 Subject: [PATCH 2/7] fix meta --- extensions/fablabchemnitz/open_dir/meta.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/fablabchemnitz/open_dir/meta.json b/extensions/fablabchemnitz/open_dir/meta.json index 399e06c7..ad4e4ad5 100644 --- a/extensions/fablabchemnitz/open_dir/meta.json +++ b/extensions/fablabchemnitz/open_dir/meta.json @@ -4,14 +4,14 @@ "id": "fablabchemnitz.de.open_dir", "path": "open_dir", "dependent_extensions": null, - "original_name": "Animate Order", + "original_name": "Open Extension Directory", "original_id": "fablabchemnitz.de.open_dir", "license": "GNU GPL v3", "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", "comment": "Written by Mario Voigt", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/animate_order", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/open_dir", "fork_url": null, - "documentation_url": "https://stadtfabrikanten.org/display/IFM/Open+Dir", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Open+Extension+Directory", "inkscape_gallery_url": null, "main_authors": [ "github.com/vmario89" From ad73bd72a53119c8a50290b513d8c3f3968aac1e Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Tue, 9 Nov 2021 18:35:14 +0100 Subject: [PATCH 3/7] small enhancements in travel moves --- .../draw_directions_tavel_moves.inx | 6 ++++ .../draw_directions_tavel_moves.py | 35 ++++++++++++++----- .../fablabchemnitz/laser_check/laser_check.py | 1 + 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx index 7a8c7da1..84d27f4c 100644 --- a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx +++ b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx @@ -4,6 +4,12 @@ fablabchemnitz.de.draw_directions_tavel_moves + + + + + + true 10 diff --git a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py index 1df581e5..7923961b 100644 --- a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py +++ b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py @@ -32,7 +32,7 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument("--nb_main") - + pars.add_argument("--order", default="separate_groups") pars.add_argument("--draw_dots", type=inkex.Boolean, default=True, help="Start and end point of opened (red + blue) and closed paths (green + yellow)") pars.add_argument("--dotsize", type=int, default=10, help="Dot size (px)") pars.add_argument("--draw_travel_moves", type=inkex.Boolean, default=False) @@ -45,6 +45,7 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): def effect(self): global so so = self.options + dotPrefix = "dots-" linePrefix = "travelLine-" groupPrefix = "travelLines-" ignoreWord = "ignore" @@ -70,7 +71,14 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): for element in self.svg.selected.values(): parseChildren(element) - dot_group = self.svg.add(inkex.Group()) + dotGroup = self.svg.add(inkex.Group(id=self.svg.get_unique_id(dotPrefix))) + if so.order == "separate_layers": + dotGroup.set("inkscape:groupmode", "layer") + dotGroup.set("inkscape:label", dotPrefix + "layer") + if so.order == "separate_groups": + dotGroup.pop("inkscape:groupmode") + dotGroup.pop("inkscape:label") + dotGroup.style.pop("display") #if the group previously has been a layer (which was hidden), a display:none will be added. we don't want that if so.arrow_style is True: markerId = "travel_move_arrow" @@ -112,11 +120,20 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): groupName = groupPrefix + strokeColor travelGroup = self.find_group(groupName) if travelGroup is None: - self.document.getroot().append(inkex.Group(id=groupName)) + travelGroup = inkex.Group(id=groupName) + self.document.getroot().append(travelGroup) else: #exists self.document.getroot().append(travelGroup) for child in travelGroup: child.delete() + if so.order == "separate_layers": + travelGroup.set("inkscape:groupmode", "layer") + travelGroup.set("inkscape:label", groupName + "-layer") + if so.order == "separate_groups": + travelGroup.pop("inkscape:groupmode") + travelGroup.pop("inkscape:label") + travelGroup.style.pop("display") + p = element.path.transform(element.composed_transform()) points = list(p.end_points) @@ -127,11 +144,11 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if so.draw_dots is True: if start[0] == end[0] and start[1] == end[1]: - self.drawCircle(dot_group, '#00FF00', start) - self.drawCircle(dot_group, '#FFFF00', points[1]) #draw one point which gives direction of the path + self.drawCircle(dotGroup, '#00FF00', start) + self.drawCircle(dotGroup, '#FFFF00', points[1]) #draw one point which gives direction of the path else: #open contour with start and end point - self.drawCircle(dot_group, '#FF0000', start) - self.drawCircle( dot_group, '#0000FF', end) + self.drawCircle(dotGroup, '#FF0000', start) + self.drawCircle( dotGroup, '#0000FF', end) for strokeColor in strokeColors: if strokeColor in strokeColorsAndCounts: @@ -203,8 +220,8 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if so.debug is True: inkex.utils.debug("-"*40) #cleanup empty groups - if len(dot_group) == 0: - dot_group.delete() + if len(dotGroup) == 0: + dotGroup.delete() travelGroups = self.document.xpath("//svg:g[starts-with(@id, 'travelLines-')]", namespaces=inkex.NSS) for travelGroup in travelGroups: if len(travelGroup) == 0: diff --git a/extensions/fablabchemnitz/laser_check/laser_check.py b/extensions/fablabchemnitz/laser_check/laser_check.py index 70ee7616..dd343959 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.py +++ b/extensions/fablabchemnitz/laser_check/laser_check.py @@ -13,6 +13,7 @@ class LaserCheck(inkex.EffectExtension): ''' ToDos: + - check for elements which have the attribute "display:none" (some groups have this) - inx: - set speed manually or pick machine (epilog) - travel and cut speed are prefilled then - calculate cut estimation with linear or non-linear (epiloog) speeds > select formula or like this From 2929277f6f59c1d65a145f3752b4bf5e0ba6aeb2 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Wed, 10 Nov 2021 02:18:39 +0100 Subject: [PATCH 4/7] Different enhancements --- .../create_links/create_links.py | 2 +- .../draw_directions_tavel_moves.inx | 7 +- .../draw_directions_tavel_moves.py | 157 +++++++++++++----- .../fablabchemnitz/robot_boxes/robot_boxes.py | 2 +- 4 files changed, 123 insertions(+), 45 deletions(-) diff --git a/extensions/fablabchemnitz/create_links/create_links.py b/extensions/fablabchemnitz/create_links/create_links.py index 719c40df..36bc694d 100644 --- a/extensions/fablabchemnitz/create_links/create_links.py +++ b/extensions/fablabchemnitz/create_links/create_links.py @@ -69,7 +69,7 @@ class LinksCreator(inkex.EffectExtension): if element.tag == inkex.addNS('path','svg'): parent = element.getparent() idx = parent.index(element) - idSuffix = 0 + idSuffix = 0 raw = element.path.to_arrays() subPaths, prev = [], 0 for i in range(len(raw)): # Breaks compound paths into simple paths diff --git a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx index 84d27f4c..fa16accc 100644 --- a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx +++ b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.inx @@ -3,7 +3,7 @@ Draw Directions / Travel Moves fablabchemnitz.de.draw_directions_tavel_moves - + @@ -21,9 +21,12 @@ false + + + - + diff --git a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py index 7923961b..5a08520f 100644 --- a/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py +++ b/extensions/fablabchemnitz/draw_directions_tavel_moves/draw_directions_tavel_moves.py @@ -6,21 +6,22 @@ import inkex from inkex import Circle, Vector2d from inkex.paths import Path from inkex.bezier import csplength +from debugpy.common.timestamp import current ''' ToDos - draw numbers for each travel lines next to the line - - option selection: add travel lines by color groups or add them at the index of the line -> this keeps the order in total (will need transform of start point and second transform of end point, if they are in different groups) - - option to just remove all generated travelLines/dots (especially hard to delete if they are inserted at element order) ''' class DrawDirectionsTravelMoves(inkex.EffectExtension): + def drawCircle(self, group, color, point): style = inkex.Style({'stroke': 'none', 'fill': color}) startCircle = group.add(Circle(cx=str(point[0]), cy=str(point[1]), r=str(self.svg.unittouu(str(so.dotsize/2) + "px")))) startCircle.style = style + def find_group(self, groupId): ''' check if a group with a given id exists or not. Returns None if not found, else returns the group element ''' groups = self.document.xpath('//svg:g', namespaces=inkex.NSS) @@ -29,6 +30,32 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if group.get('id') == groupId: return group return None + + + def createTravelMarker(self, markerId): + #add new marker to defs (or overwrite if existent) + defs = self.svg.defs + for defi in defs: + if defi.tag == "{http://www.w3.org/2000/svg}marker" and defi.get('id') == markerId: #delete + defi.delete() + marker = inkex.Marker(id=markerId) + marker.set("inkscape:isstock", "true") + marker.set("inkscape:stockid", markerId) + marker.set("orient", "auto") + marker.set("refY", "0.0") + marker.set("refX", "0.0") + marker.set("style", "overflow:visible;") + + markerPath = inkex.PathElement(id=self.svg.get_unique_id('markerId-')) + markerPath.style = {"fill-rule": "evenodd", "fill": "context-stroke", "stroke-width": str(self.svg.unittouu('1px'))} + markerPath.transform = "scale(1.0,1.0) rotate(0) translate(-6.0,0)" + arrowHeight = 6.0 + markerPath.attrib["transform"] = "scale({},{}) rotate(0) translate(-{},0)".format(so.arrow_size, so.arrow_size, arrowHeight) + markerPath.path = "M {},0.0 L -3.0,5.0 L -3.0,-5.0 L {},0.0 z".format(arrowHeight, arrowHeight) + + marker.append(markerPath) + defs.append(marker) #do not append before letting contain it one path. Otherwise Inkscape is going to crash immediately + def add_arguments(self, pars): pars.add_argument("--nb_main") @@ -46,26 +73,46 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): global so so = self.options dotPrefix = "dots-" - linePrefix = "travelLine-" + lineSuffix = "-travelLine" groupPrefix = "travelLines-" ignoreWord = "ignore" + if so.nb_main == "tab_remove": + #remove dots + dots = self.document.xpath("//svg:g[starts-with(@id, '" + dotPrefix + "')]", namespaces=inkex.NSS) + for dot in dots: + dot.delete() + + #remove travel lines + travelLines = self.document.xpath("//svg:path[contains(@id, '" + lineSuffix + "')]", namespaces=inkex.NSS) + for travelLine in travelLines: + travelLine.delete() + + #remove travel groups/layers + travelGroups = self.document.xpath("//svg:g[starts-with(@id, '" + groupPrefix + "')]", namespaces=inkex.NSS) + for travelGroup in travelGroups: + if len(travelGroup) == 0: + travelGroup.delete() + return + + #else ... + selectedPaths = [] #total list of elements to parse def parseChildren(element): - if isinstance(element, inkex.PathElement) and element not in selectedPaths and linePrefix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): + if isinstance(element, inkex.PathElement) and element not in selectedPaths and lineSuffix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): selectedPaths.append(element) children = element.getchildren() if children is not None: for child in children: - if isinstance(child, inkex.PathElement) and child not in selectedPaths and linePrefix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): + if isinstance(child, inkex.PathElement) and child not in selectedPaths and lineSuffix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): selectedPaths.append(child) parseChildren(child) #go deeper and deeper #check if we have selectedPaths elements or if we should parse the whole document instead if len(self.svg.selected) == 0: for element in self.document.getroot().iter(tag=etree.Element): - if isinstance(element, inkex.PathElement) and element != self.document.getroot() and linePrefix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): + if isinstance(element, inkex.PathElement) and element != self.document.getroot() and lineSuffix not in element.get('id') and not isinstance(element.getparent(), inkex.Marker): selectedPaths.append(element) else: for element in self.svg.selected.values(): @@ -78,37 +125,23 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if so.order == "separate_groups": dotGroup.pop("inkscape:groupmode") dotGroup.pop("inkscape:label") - dotGroup.style.pop("display") #if the group previously has been a layer (which was hidden), a display:none will be added. we don't want that + if dotGroup.style.get('display') is not None: + dotGroup.style.pop("display") #if the group previously has been a layer (which was hidden), a display:none will be added. we don't want that if so.arrow_style is True: markerId = "travel_move_arrow" - #add new marker to defs (or overwrite if existent) - defs = self.svg.defs - for defi in defs: - if defi.tag == "{http://www.w3.org/2000/svg}marker" and defi.get('id') == markerId: #delete - defi.delete() - marker = inkex.Marker(id=markerId) - marker.set("inkscape:isstock", "true") - marker.set("inkscape:stockid", markerId) - marker.set("orient", "auto") - marker.set("refY", "0.0") - marker.set("refX", "0.0") - marker.set("style", "overflow:visible;") + self.createTravelMarker(markerId) - markerPath = inkex.PathElement(id=self.svg.get_unique_id('markerId-')) - markerPath.style = {"fill-rule": "evenodd", "fill": "context-stroke", "stroke-width": str(self.svg.unittouu('1px'))} - markerPath.transform = "scale(1.0,1.0) rotate(0) translate(-6.0,0)" - arrowHeight = 6.0 - markerPath.attrib["transform"] = "scale({},{}) rotate(0) translate(-{},0)".format(so.arrow_size, so.arrow_size, arrowHeight) - markerPath.path = "M {},0.0 L -3.0,5.0 L -3.0,-5.0 L {},0.0 z".format(arrowHeight, arrowHeight) - - marker.append(markerPath) - defs.append(marker) #do not append before letting contain it one path. Otherwise Inkscape is going to crash immediately - #collect all different stroke colors to distinguish by groups strokeColors = [] strokeColorsAndCounts = {} - startEndPath = [] #the container for all paths we will loop on + ''' + the container for all paths we will loop on. Index: + 0 = element + 1 = start point + 2 = end point + ''' + startEndPath = [] for element in selectedPaths: strokeColor = element.style.get('stroke') @@ -132,7 +165,8 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if so.order == "separate_groups": travelGroup.pop("inkscape:groupmode") travelGroup.pop("inkscape:label") - travelGroup.style.pop("display") + if travelGroup.style.get('display') is not None: + travelGroup.style.pop("display") p = element.path.transform(element.composed_transform()) @@ -166,8 +200,22 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): for i in range(0, ran): #loop through the item selection. nested loop. so we loop over alle elements again for each color if i < ran - 1: elementStroke = startEndPath[i][0].style.get('stroke') + currentElement = startEndPath[i][0] + idx = currentElement.getparent().index(currentElement) + travelLineId = currentElement.get('id') + lineSuffix + ("-begin" if firstOccurence is True else "") if i == ran or createdMoves == colorCount: elementStroke = startEndPath[i-1][0].style.get('stroke') + currentElement = startEndPath[i-1][0] + idx = currentElement.getparent().index(currentElement) + 1 + travelLineId = currentElement.get('id') + lineSuffix + "-end" + + if i < ran - 2: + nextElement = startEndPath[i+1][0] + elif i < ran - 1: + nextElement = startEndPath[i][0] + else: + nextElement = None + if elementStroke is None or elementStroke == "none": elementStroke = "none" @@ -194,26 +242,53 @@ class DrawDirectionsTravelMoves(inkex.EffectExtension): if i == ran - 1: inkex.utils.debug("segment={},{}".format(startEndPath[i-1][1], travelEnd)) - travelLine = inkex.PathElement(id=self.svg.get_unique_id(linePrefix) + element.get('id')) + travelLine = inkex.PathElement(id=travelLineId) #if some objects are at svg:svg level this may cause errors #if element.getparent() != self.document.getroot(): # travelLine.transform = element.getparent().composed_transform() - travelLine.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(travelStart[0],travelStart[1],travelEnd[0],travelEnd[1])) travelLine.style = {'stroke': ("#000000" if so.ignore_colors is True else sc), 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px')), 'marker-end': 'url(#marker1426)'} if so.dashed_style is True: travelLine.style['stroke-dasharray'] = '1,1' travelLine.style['stroke-dashoffset'] = '0' if so.arrow_style is True: travelLine.style["marker-end"] = "url(#{})".format(markerId) - - #check the length. if zero we do not add - slengths, stotal = csplength(travelLine.path.transform(element.composed_transform()).to_superpath()) #get segment lengths and total length of path in document's internal unit - if stotal > 0: - #finally add the line - travelGroup = self.find_group(groupPrefix + sc) - travelGroup.add(travelLine) + + #this is a really dirty block of code + if so.order == "element_index": + #adding the lines at element index requires to apply transformations for start point and end point (in case they are in different groups) + pseudo1 = inkex.PathElement() + pseudo1.set('d', "M{:0.6f},{:0.6f}".format(travelStart[0],travelStart[1])) + pseudo2 = inkex.PathElement() + pseudo2.set('d', "M{:0.6f},{:0.6f}".format(travelEnd[0],travelEnd[1])) + if nextElement is not None: + if currentElement.getparent() == nextElement.getparent(): + pseudo1.path = pseudo1.path.transform(-currentElement.composed_transform()).to_superpath() + pseudo2.path = pseudo2.path.transform(-nextElement.composed_transform()).to_superpath() + else: + pseudo1.path = pseudo1.path.transform(-currentElement.composed_transform()).to_superpath() + pseudo2.path = pseudo2.path.transform(-currentElement.composed_transform()).to_superpath() + else: + pseudo1.path = pseudo1.path.transform(-currentElement.composed_transform()).to_superpath() + pseudo2.path = pseudo2.path.transform(-currentElement.composed_transform()).to_superpath() + travelLine.path = pseudo1.path + pseudo2.get('d').replace("M", "L") + if so.debug is True: self.msg("travelLine={}".format(travelLine.path)) + + #check the length. if zero we do not add + slengths, stotal = csplength(travelLine.path.transform(currentElement.composed_transform()).to_superpath()) #get segment lengths and total length of path in document's internal unit + if stotal > 0: + #finally add the line + currentElement.getparent().insert(idx, travelLine) + else: + if so.debug is True: inkex.utils.debug("Line has length of zero") else: - if so.debug is True: inkex.utils.debug("Line has length of zero") + travelLine.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(travelStart[0],travelStart[1],travelEnd[0],travelEnd[1])) + #check the length. if zero we do not add + slengths, stotal = csplength(travelLine.path.transform(currentElement.composed_transform()).to_superpath()) #get segment lengths and total length of path in document's internal unit + if stotal > 0: + #finally add the line + self.find_group(groupPrefix + sc).add(travelLine) + else: + if so.debug is True: inkex.utils.debug("Line has length of zero") createdMoves += 1 #each time we created a move we count up. we want to compare against the total count of that color if so.debug is True: inkex.utils.debug("createdMoves={}".format(createdMoves)) diff --git a/extensions/fablabchemnitz/robot_boxes/robot_boxes.py b/extensions/fablabchemnitz/robot_boxes/robot_boxes.py index a3932a6c..d7e9f934 100644 --- a/extensions/fablabchemnitz/robot_boxes/robot_boxes.py +++ b/extensions/fablabchemnitz/robot_boxes/robot_boxes.py @@ -307,7 +307,7 @@ class RobotBoxes(inkex.EffectExtension): # Translate group #transform = 'translate(' + str( self.svg.namedview.center[0] ) + ',' + str( self.svg.namedview.center[1] ) + ')' g = etree.SubElement(self.svg.get_current_layer(), 'g', {inkex.addNS('label','inkscape'):'RobotBox'}) - g.transform = transform + #g.transform = transform # Create SVG Path for box bounds style = { 'stroke': '#000000', 'fill': 'none', 'stroke-width':str(self.svg.unittouu("1px")) } From 7c3339b1111d07cb4ce675fd06378443b5014cf7 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Thu, 11 Nov 2021 00:34:20 +0100 Subject: [PATCH 5/7] adjustments in lasercheck --- .../laser_check/laser_check.inx | 5 +-- .../fablabchemnitz/laser_check/laser_check.py | 40 +++++++++++-------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/extensions/fablabchemnitz/laser_check/laser_check.inx b/extensions/fablabchemnitz/laser_check/laser_check.inx index 79e4277a..3fa8db53 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.inx +++ b/extensions/fablabchemnitz/laser_check/laser_check.inx @@ -59,9 +59,8 @@ - 120 - 500 - 0.60 + 120.0 + 500.0 2.0 12.0 diff --git a/extensions/fablabchemnitz/laser_check/laser_check.py b/extensions/fablabchemnitz/laser_check/laser_check.py index dd343959..4b08c852 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.py +++ b/extensions/fablabchemnitz/laser_check/laser_check.py @@ -16,15 +16,13 @@ class LaserCheck(inkex.EffectExtension): - check for elements which have the attribute "display:none" (some groups have this) - inx: - set speed manually or pick machine (epilog) - travel and cut speed are prefilled then - - calculate cut estimation with linear or non-linear (epiloog) speeds > select formula or like this + - calculate cut estimation with linear or non-linear (epilog) speeds > select formula or like this - select time estimation for specific speed percentage for for all speeds (100,90, ...) - select material (parameters -> how to???) - select power of CO² source - add fields for additional costs like configuring the machine or grabbing parts out of the machine (weeding), etc. - add mode select: cut, engrave - - look for lines especially containing id "travelLines-" and sum up travel lines - add some extra seconds for start, stop, removing parts, attaching material, ... - - maybe remove totalTravelLength and set manually ... - Handlungsempfehlungen einbauen - verweisen auf diverse plugins, die man nutzen kann: - migrate ungrouper @@ -66,9 +64,8 @@ class LaserCheck(inkex.EffectExtension): pars.add_argument('--tab') pars.add_argument('--machine_size', default="812x508") - pars.add_argument('--max_cutting_speed', type=float, default=500) - pars.add_argument('--max_travel_speed', type=float, default=150) - pars.add_argument('--cut_travel_factor', type=float, default=0.60) + pars.add_argument('--max_cutting_speed', type=float, default=120) + pars.add_argument('--max_travel_speed', type=float, default=500) pars.add_argument('--price_per_minute_gross', type=float, default=2.0) pars.add_argument('--vector_grid_xy', type=float, default=12.0) #TODO @@ -573,27 +570,36 @@ class LaserCheck(inkex.EffectExtension): if so.checks == "check_all" or so.cutting_estimation is True: inkex.utils.debug("\n---------- Cutting time estimation (Epilog Lasers)") totalCuttingLength = 0 - pathCount = 0 + totalTravelLength = 0 + cuttingPathCount = 0 + travelPathCount = 0 + for element in shapes: if isinstance(element, inkex.PathElement): slengths, stotal = csplength(element.path.transform(element.composed_transform()).to_superpath()) - totalCuttingLength += stotal - pathCount += 1 #each path has one start and one end (if open path) or start=end on closed path. For cutting the we calculate with 2 points because we enter and leave each path ALWAYS - totalTravelLength = totalCuttingLength * so.cut_travel_factor + if "-travelLine" in element.get('id'): #we use that id scheme together with the extension "Draw Directions / Travel Moves" + totalTravelLength += stotal + travelPathCount += 1 + elif "markerId-" in element.get('id'): + pass #we skip the path "markerId-", possibly generated by the extension "Draw Directions / Travel Moves + else: + totalCuttingLength += stotal + cuttingPathCount += 1 totalLength = totalCuttingLength + totalTravelLength - inkex.utils.debug("total paths={}".format(pathCount)) + inkex.utils.debug("total cutting paths={}".format(cuttingPathCount)) + inkex.utils.debug("total travel paths={}".format(travelPathCount)) inkex.utils.debug("(measured) cutting length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalCuttingLength), "mm"), self.svg.uutounit(str(totalCuttingLength), "mm"))) - inkex.utils.debug("(estimated) travel length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalTravelLength), "mm"), self.svg.uutounit(str(totalTravelLength), "mm"))) - inkex.utils.debug("(estimated) total length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalLength), "mm"), self.svg.uutounit(str(totalLength), "mm"))) + inkex.utils.debug("(measured) travel length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalTravelLength), "mm"), self.svg.uutounit(str(totalTravelLength), "mm"))) + inkex.utils.debug("(measured) total length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalLength), "mm"), self.svg.uutounit(str(totalLength), "mm"))) ''' from https://www.epiloglaser.com/assets/downloads/fusion-material-settings.pdf - Speed Settings: The speed setting scale of 1% to 100% is not linear – + "Speed Settings: The speed setting scale of 1% to 100% is not linear – i.e. 100% speed will not be twice as fast as 50% speed. This non-linear - scale is very useful in compensating for the different factors that affect engraving time. + scale is very useful in compensating for the different factors that affect engraving time." ''' for speedFactor in [100,90,80,70,60,50,40,30,20,10,9,8,7,6,5,4,3,2,1]: speedFactorR = speedFactor / 100.0 - adjusted_speed = 482.4000 / so.max_cutting_speed #empiric - found out by trying for hours ... - empiric_scale = 1 + (speedFactorR**2) / 19.0 #empiric - found out by trying for hours ... + adjusted_speed = 480.0 / so.max_cutting_speed #empiric - found out by trying for hours ... + empiric_scale = 1 + (speedFactorR**2) / 15.25 #empiric - found out by trying for hours ... v_cut = so.max_cutting_speed * speedFactorR v_travel = so.max_travel_speed #this is always at maximum tsec_cut = (self.svg.uutounit(str(totalCuttingLength)) / (adjusted_speed * so.max_cutting_speed * speedFactorR)) * empiric_scale From 534838fe2dc645b085de9a6b3330f846b7fd3684 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Sun, 21 Nov 2021 02:03:36 +0100 Subject: [PATCH 6/7] total break option for filter --- .../filter_by_length_area.inx | 3 +- .../filter_by_length_area.py | 53 +++++++++++++------ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.inx b/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.inx index 91759a86..1670740f 100644 --- a/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.inx +++ b/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.inx @@ -7,7 +7,8 @@ false false - true + true + true diff --git a/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.py b/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.py index ad2250e7..5b366099 100644 --- a/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.py +++ b/extensions/fablabchemnitz/filter_by_length_area/filter_by_length_area.py @@ -30,7 +30,8 @@ class FilterByLengthArea(inkex.EffectExtension): pars.add_argument('--tab') pars.add_argument('--debug', type=inkex.Boolean, default=False) 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("--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("--breakapart", type=inkex.Boolean, default=True, help="Break apart selected path(s) into segments") + pars.add_argument("--breakapart_total", type=inkex.Boolean, default=True, help="Gives the best results for nodes/ filtering") pars.add_argument("--cleanup", type=inkex.Boolean, default = True, help = "Cleanup all unused groups/layers (requires separate extension)") pars.add_argument('--unit') pars.add_argument('--min_filter_enable', type=inkex.Boolean, default=True, help='Enable filtering min.') @@ -63,21 +64,40 @@ class FilterByLengthArea(inkex.EffectExtension): 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: - subPaths.append(raw[prev:i]) - prev = i - subPaths.append(raw[prev:]) + if self.options.breakapart_total is False: + for i in range(len(raw)): # Breaks compound paths into simple paths + if raw[i][0] == 'M' and i != 0: + subPaths.append(raw[prev:i]) + prev = i + subPaths.append(raw[prev:]) + else: + rawCopy = element.path.to_arrays() #we need another set of the same path + for i in range(len(raw)): # Breaks compound paths into simple paths + if i != 0: + if raw[i][0] == 'C': + rawCopy[i][1] = [raw[i][1][-2], raw[i][1][-1]] + elif raw[i][0] == 'L': + rawCopy[i][1] = [raw[i][1][0], raw[i][1][1]] + elif raw[i][0] == 'Z': #replace Z with another L command (which moves to the coordinates of the first M command in path) to have better overview + raw[-1][0] = 'L' + raw[-1][1] = raw[0][1] + rawCopy[i][0] = 'M' #we really need M. Does not matter if 'L' or 'C'. + #self.msg("s1={},s2={}".format(rawCopy[i-1], raw[i])) + subPaths.append([rawCopy[i-1], raw[i]]) + prev = i + subPaths = subPaths[::-1] + for subpath in subPaths: + #self.msg(subpath) 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.set('d', csp) if len(subPaths) == 1: - replacedelement.set('id', oldId) + replacedelement.set('id', "{}".format(oldId)) else: - replacedelement.set('id', oldId + str(idSuffix)) + replacedelement.set('id', "{}-{}".format(oldId, str(idSuffix))) idSuffix += 1 parent.insert(idx, replacedelement) breakelements.append(replacedelement) @@ -116,7 +136,7 @@ class FilterByLengthArea(inkex.EffectExtension): elements.extend(self.breakContours(element, None)) if so.debug is True: - inkex.utils.debug("Collecting elements ...") + inkex.utils.debug("Collecting svg:path elements ...") for element in elements: # additional option to apply transformations. As we clear up some groups to form new layers, we might lose translations, rotations, etc. @@ -229,12 +249,15 @@ class FilterByLengthArea(inkex.EffectExtension): if so.group is True: group.append(element) - if so.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 len(group) == 0: + # group.delete() + + if so.cleanup is 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__': FilterByLengthArea().run() \ No newline at end of file From c9bbf6061a1f48a10953f3e8c1777778d879f147 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Mon, 22 Nov 2021 12:56:02 +0100 Subject: [PATCH 7/7] update frame animation sequence --- .../frame_animation_sequence.inx | 1 + .../frame_animation_sequence.py | 86 +++++++++++-------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.inx b/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.inx index b031e9df..9f55eb64 100644 --- a/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.inx +++ b/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.inx @@ -5,6 +5,7 @@ 0 indefinite 9.1 + true all diff --git a/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.py b/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.py index a9ae57ce..bd640e8e 100644 --- a/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.py +++ b/extensions/fablabchemnitz/frame_animation_sequence/frame_animation_sequence.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# coding=utf-8 +#!/usr/bin/env python3 # # Copyright (C) 2021 roberta bennett repeatingshadow@protonmail.com # @@ -18,15 +17,18 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # """ -Create svg path animation from frames. +Create svg path animation as SMIL from frames. Place each frame of the animation in a layer named 'frame'. Each of these layers should have the same number of paths, and each path should have the same number of points as the corresponding path in other layers. +Note if there are more than one path in the frames, the Z order of the paths +must match. It helps to use the XML editor option to observe the z order. The animation is applied to the paths in the first layer in the sequence, so the -properties of that layer are used. +properties of that layer are used. In particular, the first layer ought to be set +visible. Animations with different numbers of frames can be put into different sequences, named 'sequence', using sub-groups: @@ -51,9 +53,9 @@ Layers: frame frame +Layer names must contain 'frame' and groups names contain 'sequence', +eg, frame1 frame 2 frame 30, sequence 1, rythm sequence, rocket sequence -use layer named exactly 'frame' and groups named exactly 'sequence', -not, eg, frame1 frame2 frame3 ! """ @@ -70,36 +72,52 @@ class AnimateElement(inkex.BaseElement): class AnimationExtension(inkex.EffectExtension): def add_arguments(self, pars): + pars.add_argument("--delete", type=inkex.Boolean, help="Remove frames") pars.add_argument("--begin_str", default="0", help="begin string: eg 0;an2.end;an3.begin") - pars.add_argument("--repeat_str", default="indefinite", help="indefinite or an integer") - pars.add_argument("--dur_str", default="7.9", help="duration in seconds. Do not decorate with units") - + pars.add_argument("--repeat_str", default="indefinite", help="indefinite or an integer") + pars.add_argument("--dur_str", default="7.9", help="duration in seconds. Do not decorate with units") + + + def crunchFrames(self,frames): + if frames is None: + raise inkex.AbortExtension("layer named frame does not exist.") + frame0paths = frames[0].findall('svg:path') + Dlists = [p.get_path() for p in frame0paths] + for frame in frames[1:]: + paths = frame.findall("svg:path") + for i,p in enumerate(paths): + Dlists[i] += ";\n"+p.get_path() + for i,dl in enumerate(Dlists): + animel = AnimateElement( + attributeName="d", + attributeType="XML", + begin=self.options.begin_str, + dur=self.options.dur_str, + repeatCount=self.options.repeat_str, + values=dl) + frame0paths[i].append(animel) + for frame in frames[1:]: + if self.options.delete: + frame.delete() + return + def effect(self): - sequences = self.svg.findall("svg:g[@inkscape:label='sequence']") - if len(sequences) == 0: - raise inkex.AbortExtension("layer named sequence does not exist.") + sequences = [ elem for elem in self.svg.findall("svg:g[@inkscape:label]") + if "sequence" in (elem.get('inkscape:label')) + ] + if len(sequences)==0: + frames = [ elem for elem in self.svg.findall("svg:g[@inkscape:label]") + if "frame" in (elem.get('inkscape:label')) + ] + self.crunchFrames(frames) + return for sequence in sequences: - frames = sequence.findall("svg:g[@inkscape:label='frame']") - if len(frames) == 0: - raise inkex.AbortExtension("layer named frame does not exist.") - frame0paths = frames[0].findall('svg:path') - Dlists = [p.get_path() for p in frame0paths] - for frame in frames[1:]: - paths = frame.findall("svg:path") - for i,p in enumerate(paths): - Dlists[i] += ";\n"+p.get_path() - for i,dl in enumerate(Dlists): - animel = AnimateElement( - attributeName="d", - attributeType="XML", - begin=self.options.begin_str, - dur=self.options.dur_str, - repeatCount=self.options.repeat_str, - values=dl) - frame0paths[i].append(animel) - + frames = [ elem for elem in sequence.findall("svg:g[@inkscape:label]") + if "frame" in (elem.get('inkscape:label')) + ] + self.crunchFrames(frames) + + if __name__ == '__main__': - AnimationExtension().run() - - + AnimationExtension().run() \ No newline at end of file