From a736f7293ff1df73cf3ac653d966aa8c7390e2b8 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 28 Dec 2017 15:39:56 -0500 Subject: rename draw_one_stitch to be more accurate --- embroider_simulate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 2629c812..13a84e8a 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -118,7 +118,7 @@ class EmbroiderySimulator(wx.Frame): def go(self): self.current_stitch = 0 - self.timer = wx.PyTimer(self.draw_one_stitch) + self.timer = wx.PyTimer(self.draw_one_frame) self.timer.Start(self.frame_period) def clear(self): @@ -149,7 +149,7 @@ class EmbroiderySimulator(wx.Frame): dc = wx.ClientDC(self) dc.DrawBitmap(self.buffer, 0, 0) - def draw_one_stitch(self): + def draw_one_frame(self): for i in xrange(self.stitches_per_frame): try: ((x1, y1), (x2, y2)), color = self.stitches[self.current_stitch] -- cgit v1.2.3 From 69590df70cdacb35eae7bdd6ca647b904b52d777 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 28 Dec 2017 15:55:43 -0500 Subject: generalize EmbroiderySimulator * add ability to use patches instead of a stitch file * add stop() and load() methods to use a new stitch file / patches list --- embroider_simulate.py | 55 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 10 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 13a84e8a..7b096a2d 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -4,9 +4,12 @@ import numpy import wx import inkex +from embroider import patches_to_stitches, stitches_to_polylines + class EmbroiderySimulator(wx.Frame): def __init__(self, *args, **kwargs): - stitch_file = kwargs.pop('stitch_file') + stitch_file = kwargs.pop('stitch_file', None) + patches = kwargs.pop('patches', None) self.frame_period = kwargs.pop('frame_period', 80) self.stitches_per_frame = kwargs.pop('stitches_per_frame', 1) @@ -14,10 +17,9 @@ class EmbroiderySimulator(wx.Frame): self.panel = wx.Panel(self, wx.ID_ANY) self.panel.SetFocus() - self.stitches = self._parse_stitches(stitch_file) - self.width, self.height = self.get_dimensions() + self.load(stitch_file, patches) - self.buffer = wx.EmptyBitmap(self.width, self.height) + self.buffer = wx.Bitmap(self.width, self.height) self.dc = wx.MemoryDC() self.dc.SelectObject(self.buffer) self.canvas = wx.GraphicsContext.Create(self.dc) @@ -30,6 +32,16 @@ class EmbroiderySimulator(wx.Frame): self.last_pos = None + def load(self, stitch_file=None, patches=None): + if stitch_file: + self.segments = self._parse_stitch_file(stitch_file) + elif patches: + self.segments = self._patches_to_stitches(patches) + else: + raise TypeError("EmbroiderySimulator requires a stitch_file or list of patches") + + self.width, self.height = self.get_dimensions() + def on_key_down(self, event): keycode = event.GetKeyCode() @@ -64,12 +76,30 @@ class EmbroiderySimulator(wx.Frame): return string - def _parse_stitches(self, stitch_file_path): + def _patches_to_segments(patches): + stitches = patches_to_stitches(patches) + + segments = [] + + last_pos = None + last_color = None + + for stitch in stitches: + if stitch.color == last_color: + pos = (stitch.x, stitch.y) + segments.append(((last_pos, pos), stitch.color)) + + last_pos = pos + last_color = stitch.color + + return segments + + def _parse_stitch_file(self, stitch_file_path): # "$","1","229","229","229","(null)","(null)" # "*","JUMP","1.595898","48.731899" # "*","STITCH","1.595898","48.731899" - stitches = [] + segments = [] pos = (0, 0) color = wx.Brush('black') @@ -97,18 +127,18 @@ class EmbroiderySimulator(wx.Frame): new_pos = (int(float(x) * 10), int(float(y) * 10)) if not cut: - stitches.append(((pos, new_pos), color)) + segments.append(((pos, new_pos), color)) cut = False pos = new_pos - return stitches + return segments def get_dimensions(self): width = 0 height = 0 - for stitch in self.stitches: + for stitch in self.segments: (start_x, start_y), (end_x, end_y) = stitch[0] width = max(width, start_x, end_x) @@ -117,10 +147,15 @@ class EmbroiderySimulator(wx.Frame): return width, height def go(self): + self.clear() + self.current_stitch = 0 self.timer = wx.PyTimer(self.draw_one_frame) self.timer.Start(self.frame_period) + def stop(self): + self.timer.Stop() + def clear(self): self.dc.SetBackground(wx.Brush('white')) self.dc.Clear() @@ -152,7 +187,7 @@ class EmbroiderySimulator(wx.Frame): def draw_one_frame(self): for i in xrange(self.stitches_per_frame): try: - ((x1, y1), (x2, y2)), color = self.stitches[self.current_stitch] + ((x1, y1), (x2, y2)), color = self.segments[self.current_stitch] y1 = self.height - y1 y2 = self.height - y2 -- cgit v1.2.3 From 61ed1da1cfb39f6350156bf56ce969efa8899891 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Dec 2017 16:05:21 -0500 Subject: params simulate window when you change params, a simulate window opens to preview the results right away --- embroider_simulate.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 7b096a2d..d80dbe6e 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -3,6 +3,7 @@ import os import numpy import wx import inkex +import simplestyle from embroider import patches_to_stitches, stitches_to_polylines @@ -10,10 +11,12 @@ class EmbroiderySimulator(wx.Frame): def __init__(self, *args, **kwargs): stitch_file = kwargs.pop('stitch_file', None) patches = kwargs.pop('patches', None) + self.on_close_hook = kwargs.pop('on_close', None) self.frame_period = kwargs.pop('frame_period', 80) self.stitches_per_frame = kwargs.pop('stitches_per_frame', 1) wx.Frame.__init__(self, *args, **kwargs) + self.panel = wx.Panel(self, wx.ID_ANY) self.panel.SetFocus() @@ -30,15 +33,19 @@ class EmbroiderySimulator(wx.Frame): self.panel.Bind(wx.EVT_PAINT, self.on_paint) self.panel.Bind(wx.EVT_KEY_DOWN, self.on_key_down) + self.timer = None + self.last_pos = None + self.Bind(wx.EVT_CLOSE, self.on_close) + def load(self, stitch_file=None, patches=None): if stitch_file: self.segments = self._parse_stitch_file(stitch_file) elif patches: - self.segments = self._patches_to_stitches(patches) + self.segments = self._patches_to_segments(patches) else: - raise TypeError("EmbroiderySimulator requires a stitch_file or list of patches") + return self.width, self.height = self.get_dimensions() @@ -76,18 +83,22 @@ class EmbroiderySimulator(wx.Frame): return string - def _patches_to_segments(patches): + def _patches_to_segments(self, patches): stitches = patches_to_stitches(patches) segments = [] last_pos = None last_color = None + pen = None for stitch in stitches: + pos = (stitch.x, stitch.y) + if stitch.color == last_color: - pos = (stitch.x, stitch.y) - segments.append(((last_pos, pos), stitch.color)) + segments.append(((last_pos, pos), pen)) + else: + pen = wx.Pen(simplestyle.parseColor(stitch.color)) last_pos = pos last_color = stitch.color @@ -138,8 +149,8 @@ class EmbroiderySimulator(wx.Frame): width = 0 height = 0 - for stitch in self.segments: - (start_x, start_y), (end_x, end_y) = stitch[0] + for segment in self.segments: + (start_x, start_y), (end_x, end_y) = segment[0] width = max(width, start_x, end_x) height = max(height, start_y, end_y) @@ -153,8 +164,17 @@ class EmbroiderySimulator(wx.Frame): self.timer = wx.PyTimer(self.draw_one_frame) self.timer.Start(self.frame_period) + def on_close(self, event): + self.stop() + + if self.on_close_hook: + self.on_close_hook() + + self.Destroy() + def stop(self): - self.timer.Stop() + if self.timer: + self.timer.Stop() def clear(self): self.dc.SetBackground(wx.Brush('white')) -- cgit v1.2.3 From 7152caa14d9c0ec912b5326fde58c0c410e45391 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Dec 2017 20:54:35 -0500 Subject: render in a background thread The problem with this approach is that sometimes the SystemExit happens down inside shapely and it complains bitterly (on stderr). May have to rethink. --- embroider_simulate.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index d80dbe6e..6db203ad 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -179,6 +179,8 @@ class EmbroiderySimulator(wx.Frame): def clear(self): self.dc.SetBackground(wx.Brush('white')) self.dc.Clear() + self.last_pos = None + self.Refresh() def on_size(self, e): # ensure that the whole canvas is visible -- cgit v1.2.3 From 824dd3c4e6a42dbdb032f93ccfc050e5edb9b242 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 31 Dec 2017 22:05:05 -0500 Subject: don't mirror Y axis for patches --- embroider_simulate.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 6db203ad..2d913bbb 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -41,8 +41,10 @@ class EmbroiderySimulator(wx.Frame): def load(self, stitch_file=None, patches=None): if stitch_file: + self.mirror = True self.segments = self._parse_stitch_file(stitch_file) elif patches: + self.mirror = False self.segments = self._patches_to_segments(patches) else: return @@ -210,8 +212,10 @@ class EmbroiderySimulator(wx.Frame): for i in xrange(self.stitches_per_frame): try: ((x1, y1), (x2, y2)), color = self.segments[self.current_stitch] - y1 = self.height - y1 - y2 = self.height - y2 + + if self.mirror: + y1 = self.height - y1 + y2 = self.height - y2 self.canvas.SetPen(color) self.canvas.DrawLines(((x1, y1), (x2, y2))) -- cgit v1.2.3 From c2388a53a66be19199f84d332cfb9f613512db4a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 31 Dec 2017 22:24:18 -0500 Subject: adjust default simulation speed to always take ~5 seconds --- embroider_simulate.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 2d913bbb..6fdff57c 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -14,6 +14,7 @@ class EmbroiderySimulator(wx.Frame): self.on_close_hook = kwargs.pop('on_close', None) self.frame_period = kwargs.pop('frame_period', 80) self.stitches_per_frame = kwargs.pop('stitches_per_frame', 1) + self.target_duration = kwargs.pop('target_duration', None) wx.Frame.__init__(self, *args, **kwargs) @@ -22,6 +23,9 @@ class EmbroiderySimulator(wx.Frame): self.load(stitch_file, patches) + if self.target_duration: + self.adjust_speed(self.target_duration) + self.buffer = wx.Bitmap(self.width, self.height) self.dc = wx.MemoryDC() self.dc.SelectObject(self.buffer) @@ -51,6 +55,14 @@ class EmbroiderySimulator(wx.Frame): self.width, self.height = self.get_dimensions() + def adjust_speed(self, duration): + self.frame_period = 1000 * float(duration) / len(self.segments) + self.stitches_per_frame = 1 + + while self.frame_period < 1.0: + self.frame_period *= 2 + self.stitches_per_frame *= 2 + def on_key_down(self, event): keycode = event.GetKeyCode() -- cgit v1.2.3 From 75241f5f9ddb1e48744878e987136224f9526845 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 31 Dec 2017 22:38:15 -0500 Subject: trim unnecessary whitespace --- embroider_simulate.py | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 6fdff57c..0bee6327 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -53,6 +53,7 @@ class EmbroiderySimulator(wx.Frame): else: return + self.trim_margins() self.width, self.height = self.get_dimensions() def adjust_speed(self, duration): @@ -159,15 +160,48 @@ class EmbroiderySimulator(wx.Frame): return segments + def all_coordinates(self): + for segment in self.segments: + start, end = segment[0] + + yield start + yield end + + def trim_margins(self): + """remove any unnecessary whitespace around the design""" + + min_x = sys.maxint + min_y = sys.maxint + + for x, y in self.all_coordinates(): + min_x = min(min_x, x) + min_y = min(min_y, y) + + + new_segments = [] + + for segment in self.segments: + (start, end), color = segment + + new_segment = ( + ( + (start[0] - min_x, start[1] - min_y), + (end[0] - min_x, end[1] - min_y), + ), + color + ) + + new_segments.append(new_segment) + + self.segments = new_segments + def get_dimensions(self): width = 0 height = 0 - for segment in self.segments: - (start_x, start_y), (end_x, end_y) = segment[0] - - width = max(width, start_x, end_x) - height = max(height, start_y, end_y) + for x, y in self.all_coordinates(): + width = max(width, x) + height = max(height, y) return width, height -- cgit v1.2.3 From e9bddedf36e91f33b13a22997872812e849b2157 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 1 Jan 2018 14:56:27 -0500 Subject: render objects in the correct order --- embroider_simulate.py | 1 - 1 file changed, 1 deletion(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 0bee6327..5654b9c3 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -177,7 +177,6 @@ class EmbroiderySimulator(wx.Frame): min_x = min(min_x, x) min_y = min(min_y, y) - new_segments = [] for segment in self.segments: -- cgit v1.2.3 From 084dc783d2f2a94e77dfb6afe9b5a2ca14d7fc64 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 1 Jan 2018 15:00:58 -0500 Subject: add restart option for simulator --- embroider_simulate.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 5654b9c3..31d3cd75 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -84,6 +84,10 @@ class EmbroiderySimulator(wx.Frame): self.timer.Stop() else: self.timer.Start(self.frame_period) + elif keycode == ord("R"): + self.stop() + self.clear() + self.go() self.frame_period = max(1, self.frame_period) self.stitches_per_frame = max(self.stitches_per_frame, 1) @@ -208,7 +212,10 @@ class EmbroiderySimulator(wx.Frame): self.clear() self.current_stitch = 0 - self.timer = wx.PyTimer(self.draw_one_frame) + + if not self.timer: + self.timer = wx.PyTimer(self.draw_one_frame) + self.timer.Start(self.frame_period) def on_close(self, event): -- cgit v1.2.3 From 09b9dd94e6591e0e74b8896c8cb8fdf173e32f5b Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 1 Jan 2018 15:26:18 -0500 Subject: adjust colors in the simulator to make them visible Colors too close to white are darkened just a bit to make them stand out against the white background. --- embroider_simulate.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'embroider_simulate.py') diff --git a/embroider_simulate.py b/embroider_simulate.py index 31d3cd75..f3f9e5e2 100644 --- a/embroider_simulate.py +++ b/embroider_simulate.py @@ -4,6 +4,7 @@ import numpy import wx import inkex import simplestyle +import colorsys from embroider import patches_to_stitches, stitches_to_polylines @@ -102,6 +103,24 @@ class EmbroiderySimulator(wx.Frame): return string + def color_to_pen(self, color): + # python colorsys module uses floats from 0 to 1.0 + color = [value / 255.0 for value in color] + + hls = list(colorsys.rgb_to_hls(*color)) + + # Our background is white. If the color is too close to white, then + # it won't be visible. Capping lightness should make colors visible + # without changing them too much. + hls[1] = min(hls[1], 0.85) + + color = colorsys.hls_to_rgb(*hls) + + # convert back to values in the range of 0-255 + color = [value * 255 for value in color] + + return wx.Pen(color) + def _patches_to_segments(self, patches): stitches = patches_to_stitches(patches) @@ -117,7 +136,7 @@ class EmbroiderySimulator(wx.Frame): if stitch.color == last_color: segments.append(((last_pos, pos), pen)) else: - pen = wx.Pen(simplestyle.parseColor(stitch.color)) + pen = self.color_to_pen(simplestyle.parseColor(stitch.color)) last_pos = pos last_color = stitch.color @@ -132,7 +151,7 @@ class EmbroiderySimulator(wx.Frame): segments = [] pos = (0, 0) - color = wx.Brush('black') + pen = wx.Brush('black') cut = True with open(stitch_file_path) as stitch_file: @@ -144,7 +163,7 @@ class EmbroiderySimulator(wx.Frame): if symbol == "$": red, green, blue = fields[2:5] - color = wx.Pen((int(red), int(green), int(blue))) + color = color_to_pen((int(red), int(green), int(blue))) elif symbol == "*": if command == "COLOR": # change color @@ -157,7 +176,7 @@ class EmbroiderySimulator(wx.Frame): new_pos = (int(float(x) * 10), int(float(y) * 10)) if not cut: - segments.append(((pos, new_pos), color)) + segments.append(((pos, new_pos), pen)) cut = False pos = new_pos -- cgit v1.2.3