added functions to render travel moves
This commit is contained in:
parent
2787147b23
commit
84ba33f2de
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
|
||||||
<name>Draw Directions</name>
|
|
||||||
<id>fablabchemnitz.de.draw_directions</id>
|
|
||||||
<effect>
|
|
||||||
<object-type>path</object-type>
|
|
||||||
<effects-menu>
|
|
||||||
<submenu name="FabLab Chemnitz">
|
|
||||||
<submenu name="Shape/Pattern from existing Path(s)"/>
|
|
||||||
</submenu>
|
|
||||||
</effects-menu>
|
|
||||||
</effect>
|
|
||||||
<script>
|
|
||||||
<command location="inx" interpreter="python">draw_directions.py</command>
|
|
||||||
</script>
|
|
||||||
</inkscape-extension>
|
|
@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import inkex
|
|
||||||
from inkex.paths import Path
|
|
||||||
from inkex import Circle
|
|
||||||
|
|
||||||
class DrawDirections(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(self.options.dotsize/2) + "px"))))
|
|
||||||
startCircle.style = style
|
|
||||||
|
|
||||||
def add_arguments(self, pars):
|
|
||||||
pars.add_argument("--dotsize", type=int, default=10, help="Dot size (px) for self-intersecting points")
|
|
||||||
|
|
||||||
def effect(self):
|
|
||||||
dot_group = self.svg.add(inkex.Group())
|
|
||||||
|
|
||||||
for node in self.svg.selection.filter(inkex.PathElement).values():
|
|
||||||
points = list(node.path.end_points)
|
|
||||||
start = points[0]
|
|
||||||
end = points[len(points) - 1]
|
|
||||||
|
|
||||||
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
|
|
||||||
else: #open contour with start and end point
|
|
||||||
self.drawCircle(dot_group, '#FF0000', start)
|
|
||||||
self.drawCircle(dot_group, '#0000FF', end)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
DrawDirections().run()
|
|
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
|
<name>Draw Directions / Travel Moves</name>
|
||||||
|
<id>fablabchemnitz.de.draw_directions_tavel_moves</id>
|
||||||
|
<param name="nb_main" type="notebook">
|
||||||
|
<page name="tab_settings" gui-text="Settings">
|
||||||
|
<param name="draw_dots" type="bool" gui-text="Draw dots" gui-description="Start and end point of opened (red + blue) and closed paths (green + yellow)">true</param>
|
||||||
|
<param name="dotsize" type="int" min="1" max="9999" gui-text="Dot size (px)">10</param>
|
||||||
|
<separator/>
|
||||||
|
<param name="draw_travel_moves" type="bool" gui-text="Draw travel moves">false</param>
|
||||||
|
<param name="ignore_colors" type="bool" gui-text="Ignore stroke colors" gui-description="If enabled we connect all lines by order, ignoring the stroke color (normally we have one travel line group per color)">false</param>
|
||||||
|
<param name="dashed_style" type="bool" gui-text="Dashed style">true</param>
|
||||||
|
<param name="arrow_style" type="bool" gui-text="Arrow style">true</param>
|
||||||
|
<param name="arrow_size" type="float" min="0.1" max="10.0" precision="2" gui-text="Arrow size (scale)">1.0</param>
|
||||||
|
<separator/>
|
||||||
|
<param name="debug" type="bool" gui-text="Print debug info">false</param>
|
||||||
|
</page>
|
||||||
|
<page name="tab_about" gui-text="About">
|
||||||
|
<label appearance="header">Draw Directions / Travel Moves</label>
|
||||||
|
<label>Draw travel lines and/or dots to mark start and end points of paths</label>
|
||||||
|
<label>2021 / written by Mario Voigt (Stadtfabrikanten e.V. / FabLab Chemnitz)</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">Online Documentation</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/drawdirectionstravelmoves</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">Contributing</label>
|
||||||
|
<label appearance="url">https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X</label>
|
||||||
|
<label appearance="url">mailto:mario.voigt@stadtfabrikanten.org</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">MightyScape Extension Collection</label>
|
||||||
|
<label>This piece of software is part of the MightyScape for Inkscape Extension Collection and is licensed under GNU GPL v3</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/mightyscape-overview</label>
|
||||||
|
</page>
|
||||||
|
<page name="tab_donate" gui-text="Donate">
|
||||||
|
<label appearance="header">Coffee + Pizza</label>
|
||||||
|
<label>We are the Stadtfabrikanten, running the FabLab Chemnitz since 2016. A FabLab is an open workshop that gives people access to machines and digital tools like 3D printers, laser cutters and CNC milling machines.</label>
|
||||||
|
<spacer/>
|
||||||
|
<label>You like our work and want to support us? You can donate to our non-profit organization by different ways:</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/donate</label>
|
||||||
|
<spacer/>
|
||||||
|
<label>Thanks for using our extension and helping us!</label>
|
||||||
|
<image>../000_about_fablabchemnitz.svg</image>
|
||||||
|
</page>
|
||||||
|
</param>
|
||||||
|
<effect>
|
||||||
|
<object-type>path</object-type>
|
||||||
|
<effects-menu>
|
||||||
|
<submenu name="FabLab Chemnitz">
|
||||||
|
<submenu name="Shape/Pattern from existing Path(s)"/>
|
||||||
|
</submenu>
|
||||||
|
</effects-menu>
|
||||||
|
</effect>
|
||||||
|
<script>
|
||||||
|
<command location="inx" interpreter="python">draw_directions_tavel_moves.py</command>
|
||||||
|
</script>
|
||||||
|
</inkscape-extension>
|
@ -0,0 +1,214 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
import itertools
|
||||||
|
import inkex
|
||||||
|
from inkex import Circle, Vector2d
|
||||||
|
from inkex.paths import Path
|
||||||
|
from inkex.bezier import csplength
|
||||||
|
|
||||||
|
class DrawDirectionsTravelMoves(inkex.EffectExtension):
|
||||||
|
|
||||||
|
def drawCircle(self, transform, 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
|
||||||
|
startCircle.transform = transform
|
||||||
|
|
||||||
|
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)
|
||||||
|
for group in groups:
|
||||||
|
#inkex.utils.debug(str(layer.get('inkscape:label')) + " == " + layerName)
|
||||||
|
if group.get('id') == groupId:
|
||||||
|
return group
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_arguments(self, pars):
|
||||||
|
pars.add_argument("--nb_main")
|
||||||
|
|
||||||
|
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)
|
||||||
|
pars.add_argument("--ignore_colors", type=inkex.Boolean, default=False, help="If enabled we connect all lines by order, ignoring the stroke color (normally we have one travel line group per color)")
|
||||||
|
pars.add_argument("--dashed_style", type=inkex.Boolean, default=True)
|
||||||
|
pars.add_argument("--arrow_style", type=inkex.Boolean, default=True)
|
||||||
|
pars.add_argument("--arrow_size", type=float, default=True)
|
||||||
|
pars.add_argument("--debug", type=inkex.Boolean, default=False)
|
||||||
|
|
||||||
|
def effect(self):
|
||||||
|
global so
|
||||||
|
so = self.options
|
||||||
|
linePrefix = "travelLine-"
|
||||||
|
groupPrefix = "travelLines-"
|
||||||
|
ignoreWord = "ignore"
|
||||||
|
|
||||||
|
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):
|
||||||
|
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):
|
||||||
|
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):
|
||||||
|
selectedPaths.append(element)
|
||||||
|
else:
|
||||||
|
for element in self.svg.selected.values():
|
||||||
|
parseChildren(element)
|
||||||
|
|
||||||
|
dot_group = self.svg.add(inkex.Group())
|
||||||
|
|
||||||
|
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;")
|
||||||
|
|
||||||
|
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 = {}
|
||||||
|
|
||||||
|
for element in selectedPaths:
|
||||||
|
strokeColor = element.style.get('stroke')
|
||||||
|
if strokeColor is None or strokeColor == "none":
|
||||||
|
strokeColor = "none"
|
||||||
|
if so.ignore_colors is True:
|
||||||
|
strokeColor = ignoreWord
|
||||||
|
strokeColors.append(strokeColor)
|
||||||
|
groupName = groupPrefix + strokeColor
|
||||||
|
travelGroup = self.find_group(groupName)
|
||||||
|
if travelGroup is None:
|
||||||
|
self.document.getroot().append(inkex.Group(id=groupName))
|
||||||
|
else: #exists
|
||||||
|
self.document.getroot().append(travelGroup)
|
||||||
|
for child in travelGroup:
|
||||||
|
child.delete()
|
||||||
|
|
||||||
|
for strokeColor in strokeColors:
|
||||||
|
if strokeColor in strokeColorsAndCounts:
|
||||||
|
strokeColorsAndCounts[strokeColor] = strokeColorsAndCounts[strokeColor] + 1
|
||||||
|
else:
|
||||||
|
strokeColorsAndCounts[strokeColor] = 1
|
||||||
|
|
||||||
|
startEndPath = [] #the container for all paths we will loop on
|
||||||
|
for element in selectedPaths:
|
||||||
|
#points = list(element.path.end_points)
|
||||||
|
p = element.path.transform(element.composed_transform())
|
||||||
|
points = list(p.end_points)
|
||||||
|
start = points[0]
|
||||||
|
end = points[len(points) - 1]
|
||||||
|
|
||||||
|
startEndPath.append([element, start, end])
|
||||||
|
|
||||||
|
#if element.getparent() != self.document.getroot():
|
||||||
|
# transform = element.getparent().composed_transform()
|
||||||
|
#else:
|
||||||
|
# transform = None
|
||||||
|
transform = None
|
||||||
|
if so.draw_dots is True:
|
||||||
|
if start[0] == end[0] and start[1] == end[1]:
|
||||||
|
self.drawCircle(transform, dot_group, '#00FF00', start)
|
||||||
|
self.drawCircle(transform, dot_group, '#FFFF00', points[1]) #draw one point which gives direction of the path
|
||||||
|
else: #open contour with start and end point
|
||||||
|
self.drawCircle(transform, dot_group, '#FF0000', start)
|
||||||
|
self.drawCircle(transform, dot_group, '#0000FF', end)
|
||||||
|
|
||||||
|
if so.draw_travel_moves is True:
|
||||||
|
for sc in strokeColorsAndCounts: #loop through all unique stroke colors
|
||||||
|
colorCount = strokeColorsAndCounts[sc] #the total color count per stroke color
|
||||||
|
ran = len(startEndPath) + 1 #one more because the last travel moves goes back to 0,0 (so for 3 elements (1,2,3) the range is 0 to 3 -> makes 4)
|
||||||
|
firstOccurence = True
|
||||||
|
createdMoves = 0
|
||||||
|
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')
|
||||||
|
if i == ran or createdMoves == colorCount:
|
||||||
|
elementStroke = startEndPath[i-1][0].style.get('stroke')
|
||||||
|
if elementStroke is None or elementStroke == "none":
|
||||||
|
elementStroke = "none"
|
||||||
|
|
||||||
|
if so.debug is True: inkex.utils.debug("current stroke color {}, element stroke color{}".format(sc, elementStroke))
|
||||||
|
if sc == elementStroke or sc == ignoreWord:
|
||||||
|
if firstOccurence is True:
|
||||||
|
travelStart = Vector2d(0,0)
|
||||||
|
firstOccurence = False
|
||||||
|
else:
|
||||||
|
if i <= ran - 1:
|
||||||
|
travelStart = startEndPath[i-1][2] #end point from this path
|
||||||
|
|
||||||
|
if so.debug is True: inkex.utils.debug("travelStart={}".format(travelStart))
|
||||||
|
if i < ran - 1:
|
||||||
|
travelEnd = startEndPath[i][1]
|
||||||
|
if createdMoves == colorCount:
|
||||||
|
travelEnd = Vector2d(0,0)
|
||||||
|
|
||||||
|
if so.debug is True: inkex.utils.debug("travelEnd={}".format(travelEnd))
|
||||||
|
|
||||||
|
if so.debug is True:
|
||||||
|
if i < ran - 1:
|
||||||
|
inkex.utils.debug("segment={},{}".format(startEndPath[i][2], startEndPath[i][1]))
|
||||||
|
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'))
|
||||||
|
#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)
|
||||||
|
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))
|
||||||
|
if so.debug is True: inkex.utils.debug("-"*40)
|
||||||
|
|
||||||
|
#cleanup empty groups
|
||||||
|
if len(dot_group) == 0:
|
||||||
|
dot_group.delete()
|
||||||
|
travelGroups = self.document.xpath("//svg:g[starts-with(@id, 'travelLines-')]", namespaces=inkex.NSS)
|
||||||
|
for travelGroup in travelGroups:
|
||||||
|
if len(travelGroup) == 0:
|
||||||
|
travelGroup.delete()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
DrawDirectionsTravelMoves().run()
|
@ -1,17 +1,17 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Draw Directions",
|
"name": "Draw Directions / Travel Moves",
|
||||||
"id": "fablabchemnitz.de.draw_directions",
|
"id": "fablabchemnitz.de.draw_directions_travel_moces",
|
||||||
"path": "draw_directions",
|
"path": "draw_directions",
|
||||||
"dependent_extensions": null,
|
"dependent_extensions": null,
|
||||||
"original_name": "Draw Directions",
|
"original_name": "Draw Directions / Travel Moves",
|
||||||
"original_id": "fablabchemnitz.de.draw_directions",
|
"original_id": "fablabchemnitz.de.draw_directions_travel_moces",
|
||||||
"license": "GNU GPL v3",
|
"license": "GNU GPL v3",
|
||||||
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE",
|
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE",
|
||||||
"comment": "Written by Mario Voigt",
|
"comment": "Written by Mario Voigt",
|
||||||
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/draw_directions",
|
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/draw_directions",
|
||||||
"fork_url": null,
|
"fork_url": null,
|
||||||
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Draw+Directions",
|
"documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=120524682",
|
||||||
"inkscape_gallery_url": null,
|
"inkscape_gallery_url": null,
|
||||||
"main_authors": [
|
"main_authors": [
|
||||||
"github.com/vmario89"
|
"github.com/vmario89"
|
@ -161,7 +161,7 @@ class FilterByLengthArea(inkex.EffectExtension):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
for i in range(0, len(to_sort)):
|
for i in range(0, len(to_sort)):
|
||||||
element = to_sort[i].get('element')
|
element = to_sort[i].get('element')
|
||||||
if so.delete is True:
|
if so.delete is True:
|
||||||
element.delete()
|
element.delete()
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ class LaserCheck(inkex.EffectExtension):
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
ToDos:
|
ToDos:
|
||||||
|
- look for lines especially containing id "travelLines-" and sum up travel lines
|
||||||
- add some extra seconds for start, stop, removing parts, attaching material, ...
|
- add some extra seconds for start, stop, removing parts, attaching material, ...
|
||||||
- maybe remove totalTravelLength and set manually ...
|
- maybe remove totalTravelLength and set manually ...
|
||||||
- Handlungsempfehlungen einbauen
|
- Handlungsempfehlungen einbauen
|
||||||
@ -36,7 +37,9 @@ class LaserCheck(inkex.EffectExtension):
|
|||||||
- statistics
|
- statistics
|
||||||
- export as PDF
|
- export as PDF
|
||||||
- run as script to generate quick results for users
|
- run as script to generate quick results for users
|
||||||
- check for old styles which should be upgraded
|
- check for old styles which should be upgraded (cleanup styles tool)
|
||||||
|
- check for elements which have no style attribute (should be created) -> (cleanup styles tool)
|
||||||
|
- migrate styles from groups/layers to path styles (cleanup styles tool)
|
||||||
- self-intersecting paths
|
- self-intersecting paths
|
||||||
- number of parts (isles) to weed in total
|
- number of parts (isles) to weed in total
|
||||||
- number of parts which are smaller than vector grid
|
- number of parts which are smaller than vector grid
|
||||||
|
Reference in New Issue
Block a user