diff --git a/inkscape_driver/eggbot_hatch.inx b/inkscape_driver/eggbot_hatch.inx
index 5b68137..b24e2f3 100755
--- a/inkscape_driver/eggbot_hatch.inx
+++ b/inkscape_driver/eggbot_hatch.inx
@@ -8,58 +8,71 @@
<_param name="Header" type="description" xml:space="preserve">
-This extension fills each closed figure in your drawing
-with a path consisting of back and forth drawn "hatch" lines.
-If any objects are selected, then only those selected objects
-will be filled.
+Hatch Fill: An extension to fill all or selected closed figures in your
+drawing with paths consisting of back-and-forth hatch line.
+
-Hatched figures will be grouped with their fills.
-
- 3.0
- 45
- false
+3.0
- true
- 3.0
- true
- 1.0
- 3.0
+
+ <_option value="2">px
+ <_option value="3">mm
+ <_option value="4">inch
+
-
- (v2.3.0, June 11, 2019)
+45
+false
-
-
- <_param name="aboutpage" type="description" xml:space="preserve">
-Hatch spacing is the distance between hatch lines,
-measured in units of screen pixels (px). Angles are in
-degrees from horizontal; for example 90 is vertical.
+
-The Crosshatch option will apply a second set of
-hatches, perpendicular to the first.
+true
+3.0
-The "Connect nearby ends" option will attempt to connect
-nearby line ends with a smoothly flowing curve, to improve
-the smoothness of plotting.
+
+true
+0.5
-The Range parameter sets the distance (in hatch widths)
-over which that option searches for segments to join.
-Large values may result in hatches where you don't want
-them. Consider using a value in the range of 2-4.
+
+2.0
-The Inset option allows you to hold back the edges of the
-fill somewhat from the edge of your original object.
-This can improve performance, as it allows you to more
-reliably "color inside the lines" when using pens.
+
+
+
+Hatch spacing is the distance between hatch lines, measured in selected
+units. Hatch angle is in degrees from horizontal; e.g., 90 for vertical.
-The hatches will be the same color and width
-as the original object.
+The Crosshatch option will add a second set of hatches, perpendicular
+to the first.
-The Tolerance parameter affects how precisely
-the hatches try to fill the input paths.
+The Connect Nearby Ends option will join nearby line ends with a
+smooth curve, giving a differnt appearance and reducing the number of
+pen lifts when using robotic tools like pen plotters. The Range
+parameter sets the distance (in hatch spacing) over which end
+connections may be made. Large values may result in hatches where you
+don't want them.
+
+The Inset Fill from Edges option allows you to hold back the hatches
+away from the edge of your original object. This can allow you to more
+reliably "color inside the lines".
+
+The Tolerance parameter affects how precisely the hatches try
+to fill the input paths.
+
+
+Hatches will have the same stroke color and width as the original
+object. When filling more than one object, the hatches will be grouped
+with their original (filled) objects.
+
+
+v2.4.0, August 8, 2021
+
all
diff --git a/inkscape_driver/eggbot_hatch.py b/inkscape_driver/eggbot_hatch.py
old mode 100755
new mode 100644
index c2e92a6..12653db
--- a/inkscape_driver/eggbot_hatch.py
+++ b/inkscape_driver/eggbot_hatch.py
@@ -84,8 +84,12 @@
# This prevents extremely complex plots from generating glitches
# Modifications are limited to recursivelyTraverseSvg and effect methods
+# Updated by Windell H. Oskay, 2021
+# Add option for selecting units.
+# Make inset settable in selected units as well.
+
# Current software version:
-# (v2.3.2, September 29, 2019)
+# (v2.4.0, August 8, 2021)
#
# 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
@@ -104,7 +108,7 @@
import math
from lxml import etree
-from plot_utils_import import from_dependency_import # plotink
+from axidrawinternal.plot_utils_import import from_dependency_import # plotink
inkex = from_dependency_import('ink_extensions.inkex')
simplepath = from_dependency_import('ink_extensions.simplepath')
simpletransform = from_dependency_import('ink_extensions.simpletransform')
@@ -471,7 +475,7 @@ def interstices(self, p1, p2, paths, hatches, b_hold_back_hatches, f_hold_back_s
# remove it from consideration by marking it as already drawn - a
# fiction, but is much quicker than actually removing the hatch from the list.
- f_min_allowed_hatch_length = self.options.hatchSpacing * MIN_HATCH_FRACTION
+ f_min_allowed_hatch_length = self.hatch_spacing_px * MIN_HATCH_FRACTION
f_initial_hatch_length = math.hypot(x2 - x1, y2 - y1)
# We did as much as possible of the inset operation back when we were finding intersections.
# We did it back then because at that point we knew more about the geometry than we know now.
@@ -610,19 +614,19 @@ class Eggbot_Hatch(inkex.Effect):
self.docTransform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
self.OptionParser.add_option(
- "--holdBackSteps", action="store", type="float",
- dest="holdBackSteps", default=3.0,
- help="How far hatch strokes stay from boundary (steps)")
+ "--inset_dist", action="store", type="float",
+ dest="inset_dist", default=3.0,
+ help="How far hatch strokes stay from boundary")
self.OptionParser.add_option(
"--hatchScope", action="store", type="float",
dest="hatchScope", default=3.0,
help="Radius searched for segments to join (units of hatch width)")
self.OptionParser.add_option(
- "--holdBackHatchFromEdges", action="store", dest="holdBackHatchFromEdges",
+ "--inset_bool", action="store", dest="inset_bool",
type="inkbool", default=True,
help="Stay away from edges, so no need for inset")
self.OptionParser.add_option(
- "--reducePenLifts", action="store", dest="reducePenLifts",
+ "--connect_bool", action="store", dest="connect_bool",
type="inkbool", default=True,
help="Reduce plotting time by joining some hatches")
self.OptionParser.add_option(
@@ -639,11 +643,18 @@ class Eggbot_Hatch(inkex.Effect):
help="Spacing between hatch lines")
self.OptionParser.add_option(
"--tolerance", action="store", type="float",
- dest="tolerance", default=20.0,
+ dest="tolerance", default=3.0,
help="Allowed deviation from original paths")
+ self.OptionParser.add_option(
+ "--units",
+ action="store", type="int",
+ dest="units", default=1,
+ help="Units to use for hatches. 1: line width. 2: px. 3: mm. 4: inch")
+
self.OptionParser.add_option("--tab", # NOTE: value is not used.
- action="store", type="string", dest="tab", default="splash",
+ action="store", type="string", dest="_tab", default="splash",
help="The active tab when Apply was pressed")
+
def getDocProps(self):
@@ -753,11 +764,11 @@ class Eggbot_Hatch(inkex.Effect):
for vertex in subpath:
if vertex[0] < self.xmin:
self.xmin = vertex[0]
- elif vertex[0] > self.xmax:
+ if vertex[0] > self.xmax:
self.xmax = vertex[0]
if vertex[1] < self.ymin:
self.ymin = vertex[1]
- elif vertex[1] > self.ymax:
+ if vertex[1] > self.ymax:
self.ymax = vertex[1]
def recursivelyTraverseSvg(self, a_node_list, mat_current=None, parent_visibility='visible'):
@@ -803,7 +814,17 @@ class Eggbot_Hatch(inkex.Effect):
if v == 'inherit':
v = parent_visibility
if v == 'hidden' or v == 'collapse':
- pass
+ continue
+
+ style = simplestyle.parseStyle(node.get('style'))
+
+ # Check for "display:none" in the node's style attribute:
+ if 'display' in style.keys() and style['display'] == 'none':
+ continue # Do not hatch this object or its children
+
+ # The node may have a display="none" attribute as well:
+ if node.get('display') == 'none':
+ continue # Do not hatch this object or its children
# first apply the current matrix transform to this node's transform
mat_new = simpletransform.composeTransform(mat_current, simpletransform.parseTransform(node.get("transform")))
@@ -849,13 +870,13 @@ class Eggbot_Hatch(inkex.Effect):
self.addPathVertices(path_data, node, mat_new)
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('rect', 'svg'), 'rect']:
@@ -885,13 +906,13 @@ class Eggbot_Hatch(inkex.Effect):
self.addPathVertices(simplepath.formatPath(a), node, mat_new)
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('line', 'svg'), 'line']:
@@ -914,13 +935,13 @@ class Eggbot_Hatch(inkex.Effect):
self.addPathVertices(simplepath.formatPath(a), node, mat_new)
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('polyline', 'svg'), 'polyline']:
@@ -955,13 +976,13 @@ class Eggbot_Hatch(inkex.Effect):
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('polygon', 'svg'), 'polygon']:
# Convert
@@ -982,13 +1003,13 @@ class Eggbot_Hatch(inkex.Effect):
self.addPathVertices(d, node, mat_new)
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('ellipse', 'svg'), 'ellipse',
inkex.addNS('circle', 'svg'), 'circle']:
@@ -1033,34 +1054,33 @@ class Eggbot_Hatch(inkex.Effect):
self.addPathVertices(d, node, mat_new)
# We now have a path we want to apply a (cross)hatch to
# Apply appropriate functions
- b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True)
+ b_have_grid = self.makeHatchGrid(float(self.options.hatchAngle), float(self.hatch_spacing_px), True)
if b_have_grid:
if self.options.crossHatch:
- self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False)
+ self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.hatch_spacing_px), False)
# Now loop over our hatch lines looking for intersections
for h in self.grid:
- interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.holdBackHatchFromEdges, self.options.holdBackSteps)
+ interstices(self, (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.options.inset_bool, self.inset_dist_px)
elif node.tag in [inkex.addNS('pattern', 'svg'), 'pattern']:
- pass
+ continue
elif node.tag in [inkex.addNS('metadata', 'svg'), 'metadata']:
- pass
+ continue
elif node.tag in [inkex.addNS('defs', 'svg'), 'defs']:
- pass
+ continue
elif node.tag in [inkex.addNS('namedview', 'sodipodi'), 'namedview']:
- pass
+ continue
elif node.tag in [inkex.addNS('eggbot', 'svg'), 'eggbot']:
- pass
+ continue
elif node.tag in [inkex.addNS('WCB', 'svg'), 'WCB']:
- pass
+ continue
+ elif node.tag in [inkex.addNS('image', 'svg'), 'image']:
+ continue
elif node.tag in [inkex.addNS('text', 'svg'), 'text']:
- inkex.errormsg('Warning: unable to draw text, please convert it to a path first.')
- pass
- elif not isinstance(node.tag, basestring):
- pass
+ inkex.errormsg('Warning: unable to hatch text, please convert it to a path first.')
+ continue
else:
- inkex.errormsg('Warning: unable to hatch object <{0}>, please convert it to a path first.'.format(node.tag))
- pass
+ continue # produce no error on other SVG elements.
def joinFillsWithNode(self, node, stroke_width, path):
@@ -1193,8 +1213,19 @@ class Eggbot_Hatch(inkex.Effect):
# Viewbox handling
self.handleViewBox()
- if self.options.hatchSpacing == 0:
- self.options.hatchSpacing = 0.1 # Hardcode minimum value
+ # Default spacing values for hatches and inset; handle px units case:
+ self.hatch_spacing_px = self.options.hatchSpacing
+ self.inset_dist_px = self.options.inset_dist
+
+ if self.options.units == 3: # Units in mm
+ self.hatch_spacing_px = (96.0 / 25.4) * self.options.hatchSpacing
+ self.inset_dist_px = (96.0 / 25.4) * self.options.inset_dist
+ if self.options.units == 4: # Units in inches
+ self.hatch_spacing_px = 96.0 * self.options.hatchSpacing
+ self.inset_dist_px = 96.0 * self.options.inset_dist
+
+ if self.hatch_spacing_px < 0.1:
+ self.hatch_spacing_px = 0.1 # Hardcode minimum value
ref_count = 0
pt_last_position_abs = [0, 0]
@@ -1256,14 +1287,14 @@ class Eggbot_Hatch(inkex.Effect):
stroke_width = 1.0
# The transform also applies to the hatch spacing we use when searching for end connections
- transformed_hatch_spacing = stroke_width * self.options.hatchSpacing
+ transformed_hatch_spacing = stroke_width * self.hatch_spacing_px
path = '' # regardless of whether or not we're reducing pen lifts
pt_last_position_abs = [0, 0]
pt_last_position_abs[0] = 0
pt_last_position_abs[1] = 0
f_distance_moved_with_pen_up = 0
- if not self.options.reducePenLifts:
+ if not self.options.connect_bool:
for segment in self.hatches[key]:
if len(segment) < 2:
continue