2018-04-19 18:49:33 +00:00
import sys
2018-07-05 01:17:20 +00:00
import shapely . geometry
2018-05-02 01:21:07 +00:00
2018-03-31 00:37:11 +00:00
from . element import param , EmbroideryElement , Patch
2018-05-02 01:21:07 +00:00
from . . i18n import _
from . . utils import cache , Point
2018-07-28 00:26:18 +00:00
from . . stitches import running_stitch , bean_stitch
2018-06-21 19:41:06 +00:00
from . . svg import parse_length_with_units
2018-03-31 00:37:11 +00:00
2018-04-19 18:49:33 +00:00
warned_about_legacy_running_stitch = False
2018-03-31 00:37:11 +00:00
class Stroke ( EmbroideryElement ) :
element_name = " Stroke "
@property
@param ( ' satin_column ' , _ ( ' Satin stitch along paths ' ) , type = ' toggle ' , inverse = True )
def satin_column ( self ) :
return self . get_boolean_param ( " satin_column " )
@property
def color ( self ) :
return self . get_style ( " stroke " )
@property
def dashed ( self ) :
return self . get_style ( " stroke-dasharray " ) is not None
@property
2018-08-09 18:32:41 +00:00
@param ( ' running_stitch_length_mm ' ,
_ ( ' Running stitch length ' ) ,
tooltip = _ ( ' Length of stitches in running stitch mode. ' ) ,
unit = ' mm ' ,
type = ' float ' ,
default = 1.5 ,
sort_index = 3 )
2018-03-31 00:37:11 +00:00
def running_stitch_length ( self ) :
return max ( self . get_float_param ( " running_stitch_length_mm " , 1.5 ) , 0.01 )
@property
2018-07-28 00:26:18 +00:00
@param ( ' bean_stitch_repeats ' ,
_ ( ' Bean stitch number of repeats ' ) ,
tooltip = _ ( ' Backtrack each stitch this many times. A value of 1 would triple each stitch (forward, back, forward). A value of 2 would quintuple each stitch, etc. Only applies to running stitch. ' ) ,
type = ' int ' ,
default = 0 ,
sort_index = 2 )
def bean_stitch_repeats ( self ) :
return self . get_int_param ( " bean_stitch_repeats " , 0 )
@property
2018-08-09 18:32:41 +00:00
@param ( ' zigzag_spacing_mm ' ,
_ ( ' Zig-zag spacing (peak-to-peak) ' ) ,
tooltip = _ ( ' Length of stitches in zig-zag mode. ' ) ,
unit = ' mm ' ,
type = ' float ' ,
default = 0.4 ,
sort_index = 3 )
2018-03-31 00:37:11 +00:00
@cache
def zigzag_spacing ( self ) :
return max ( self . get_float_param ( " zigzag_spacing_mm " , 0.4 ) , 0.01 )
@property
2018-08-09 18:32:41 +00:00
@param ( ' repeats ' ,
_ ( ' Repeats ' ) ,
tooltip = _ ( ' Defines how many times to run down and back along the path. ' ) ,
type = ' int ' ,
default = " 1 " ,
sort_index = 1 )
2018-03-31 00:37:11 +00:00
def repeats ( self ) :
return self . get_int_param ( " repeats " , 1 )
@property
def paths ( self ) :
2018-04-03 02:11:57 +00:00
path = self . parse_path ( )
if self . manual_stitch_mode :
return [ self . strip_control_points ( subpath ) for subpath in path ]
else :
return self . flatten ( path )
2018-07-05 01:17:20 +00:00
@property
@cache
def shape ( self ) :
line_strings = [ shapely . geometry . LineString ( path ) for path in self . paths ]
return shapely . geometry . MultiLineString ( line_strings )
2018-04-03 02:11:57 +00:00
@property
2018-08-09 18:32:41 +00:00
@param ( ' manual_stitch ' ,
_ ( ' Manual stitch placement ' ) ,
tooltip = _ ( " Stitch every node in the path. Stitch length and zig-zag spacing are ignored. " ) ,
type = ' boolean ' ,
default = False ,
sort_index = 0 )
2018-04-03 02:11:57 +00:00
def manual_stitch_mode ( self ) :
return self . get_boolean_param ( ' manual_stitch ' )
2018-03-31 00:37:11 +00:00
def is_running_stitch ( self ) :
2018-04-19 18:49:33 +00:00
# using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines
2018-06-21 19:41:06 +00:00
stroke_width , units = parse_length_with_units ( self . get_style ( " stroke-width " , " 1 " ) )
2018-04-19 18:49:33 +00:00
if self . dashed :
return True
elif stroke_width < = 0.5 and self . get_float_param ( ' running_stitch_length_mm ' , None ) is not None :
# if they use a stroke width less than 0.5 AND they specifically set a running stitch
# length, then assume they intend to use the deprecated <= 0.5 method to set running
# stitch.
#
# Note that we use self.get_style("stroke_width") _not_ self.stroke_width above. We
# explicitly want the stroke width in "user units" ("document units") -- that is, what
# the user sees in inkscape's stroke settings.
#
# Also note that we don't use self.running_stitch_length_mm above. This is because we
# want to see if they set a running stitch length at all, and the property will apply
# a default value.
#
# Thsi is so tricky, and and intricate that's a major reason that we deprecated the
# 0.5 units rule.
# Warn them the first time.
global warned_about_legacy_running_stitch
if not warned_about_legacy_running_stitch :
warned_about_legacy_running_stitch = True
print >> sys . stderr , _ ( " Legacy running stitch setting detected! \n \n It looks like you ' re using a stroke " + \
" smaller than 0.5 units to indicate a running stitch, which is deprecated. Instead, please set " + \
" your stroke to be dashed to indicate running stitch. Any kind of dash will work. " )
# still allow the deprecated setting to work in order to support old files
return True
else :
return False
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
def simple_satin ( self , path , zigzag_spacing , stroke_width ) :
" zig-zag along the path at the specified spacing and wdith "
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
# `self.zigzag_spacing` is the length for a zig and a zag
# together (a V shape). Start with running stitch at half
# that length:
patch = self . running_stitch ( path , zigzag_spacing / 2.0 )
2018-06-05 00:19:37 +00:00
2018-06-05 00:43:56 +00:00
# Now move the points left and right. Consider each pair
# of points in turn, and move perpendicular to them,
# alternating left and right.
2018-06-05 00:19:37 +00:00
2018-06-05 00:43:56 +00:00
offset = stroke_width / 2.0
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
for i in xrange ( len ( patch ) - 1 ) :
start = patch . stitches [ i ]
end = patch . stitches [ i + 1 ]
segment_direction = ( end - start ) . unit ( )
zigzag_direction = segment_direction . rotate_left ( )
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
if i % 2 == 1 :
zigzag_direction * = - 1
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
patch . stitches [ i ] + = zigzag_direction * offset
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
return patch
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
def running_stitch ( self , path , stitch_length ) :
repeated_path = [ ]
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
# go back and forth along the path as specified by self.repeats
for i in xrange ( self . repeats ) :
if i % 2 == 1 :
# reverse every other pass
this_path = path [ : : - 1 ]
else :
this_path = path [ : ]
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
repeated_path . extend ( this_path )
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
stitches = running_stitch ( repeated_path , stitch_length )
2018-03-31 00:37:11 +00:00
2018-06-05 00:43:56 +00:00
return Patch ( self . color , stitches )
2018-03-31 00:37:11 +00:00
def to_patches ( self , last_patch ) :
patches = [ ]
for path in self . paths :
path = [ Point ( x , y ) for x , y in path ]
2018-04-03 02:11:57 +00:00
if self . manual_stitch_mode :
2018-04-07 02:09:26 +00:00
patch = Patch ( color = self . color , stitches = path , stitch_as_is = True )
2018-04-03 02:11:57 +00:00
elif self . is_running_stitch ( ) :
2018-06-05 00:43:56 +00:00
patch = self . running_stitch ( path , self . running_stitch_length )
2018-07-29 00:10:27 +00:00
if self . bean_stitch_repeats > 0 :
patch . stitches = bean_stitch ( patch . stitches , self . bean_stitch_repeats )
2018-03-31 00:37:11 +00:00
else :
2018-06-05 00:43:56 +00:00
patch = self . simple_satin ( path , self . zigzag_spacing , self . stroke_width )
2018-03-31 00:37:11 +00:00
2018-06-05 00:19:37 +00:00
if patch :
patches . append ( patch )
2018-03-31 00:37:11 +00:00
return patches