summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/extensions/params.py10
-rw-r--r--lib/simulator.py378
-rw-r--r--messages.po106
3 files changed, 328 insertions, 166 deletions
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index c464e044..6d1464a3 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -287,7 +287,6 @@ class ParamsTab(ScrolledPanel):
summary_box = wx.StaticBox(self, wx.ID_ANY, label=_("Inkscape objects"))
sizer = wx.StaticBoxSizer(summary_box, wx.HORIZONTAL)
-# sizer = wx.BoxSizer(wx.HORIZONTAL)
self.description = wx.StaticText(self)
self.update_description()
self.description.SetLabel(self.description_text)
@@ -423,6 +422,15 @@ class SettingsFrame(wx.Frame):
if self.simulate_window:
self.simulate_window.stop()
self.simulate_window.load(stitch_plan=stitch_plan)
+ self.simulate_window.calculate_dimensions()
+
+ children = self.simulate_window.GetChildren()
+ for child in children:
+ if isinstance(child, wx.Slider):
+ child.Destroy()
+ self.simulate_window.set_slider()
+
+ self.simulate_window.Layout()
else:
params_rect = self.GetScreenRect()
simulator_pos = params_rect.GetTopRight()
diff --git a/lib/simulator.py b/lib/simulator.py
index 3df72a08..530f6ebf 100644
--- a/lib/simulator.py
+++ b/lib/simulator.py
@@ -1,10 +1,9 @@
import sys
-import numpy
import wx
-import colorsys
+import time
from itertools import izip
-from .svg import PIXELS_PER_MM, color_block_to_point_lists
+from .svg import color_block_to_point_lists
from .i18n import _
class EmbroiderySimulator(wx.Frame):
@@ -27,21 +26,32 @@ class EmbroiderySimulator(wx.Frame):
if self.max_width < self.min_width:
self.max_width = self.min_width
+ self.load(stitch_plan)
+
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self, wx.ID_ANY)
+ self.panel.SetBackgroundStyle(wx.BG_STYLE_PAINT)
- self.panel.SetDoubleBuffered(True)
+ self.SetBackgroundColour('white')
- self.sizer = wx.BoxSizer(wx.VERTICAL)
- self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.slider_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.set_slider()
+ self.button_sizer = wx.StdDialogButtonSizer()
self.button_label = (
- [_("Speed up"), _('Press + or arrow up to speed up'), self.animation_speed_up],
- [_("Slow down"), _('Press - or arrow down to slow down'), self.animation_slow_down],
- [_("Pause"), _("Press P to pause the animation"), self.animation_pause],
- [_("Restart"), _("Press R to restart the animation"), self.animation_restart],
- [_("Quit"), _("Press Q to close the simulation window"), self.animation_quit])
+ # Switch direction button (currently not in use - would this be better?)
+ #[_('>>'), _('Switch direction | Play reverse (arrow left) | Play forward (arrow right)'), self.animation_switch_direction],
+ [_('<<'), _('Play reverse (arrow left)'), self.animation_reverse],
+ [_('-'), _('Play one frame backward (+)'), self.animation_one_frame_back],
+ [_('+'), _('Play one frame forward (+)'), self.animation_one_frame_forward],
+ [_('>>'), _('Play forward (arrow right)'), self.animation_forward],
+ [_('^'), _('Speed up (arrow up)'), self.animation_speed_up],
+ [_('v'), _('Slow down (arrow down)'), self.animation_slow_down],
+ [_('Pause'), _('Pause (P)'), self.animation_pause],
+ [_('Restart'), _('Restart (R)'), self.animation_restart],
+ [_('Quit'), _('Close (Q)'), self.animation_quit])
+
self.buttons = []
for i in range(0, len(self.button_label)):
self.buttons.append(wx.Button(self, -1, self.button_label[i][0]))
@@ -49,40 +59,53 @@ class EmbroiderySimulator(wx.Frame):
self.buttons[i].SetToolTip(self.button_label[i][1])
self.buttons[i].Bind(wx.EVT_BUTTON, self.button_label[i][2])
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+
self.sizer.Add(self.panel, 1, wx.EXPAND)
+ self.sizer.Add(self.slider_sizer, 0, wx.EXPAND)
self.sizer.Add(self.button_sizer, 0, wx.EXPAND)
self.SetSizer(self.sizer)
- self.load(stitch_plan)
+ self.calculate_dimensions()
if self.target_duration:
self.adjust_speed(self.target_duration)
- self.buffer = wx.Bitmap(self.width * self.scale + self.margin * 2, self.height * self.scale + self.margin * 2)
- self.dc = wx.MemoryDC()
+ self.buffer = wx.Bitmap(
+ self.width * self.scale + self.margin * 2,
+ self.height * self.scale + self.margin * 2)
+ self.dc = wx.BufferedDC()
self.dc.SelectObject(self.buffer)
self.canvas = wx.GraphicsContext.Create(self.dc)
self.clear()
- self.set_stitch_counter(1)
+ self.current_frame = 0
+ self.animation_direction = 1
+ self.set_stitch_counter(0)
shortcut_keys = [
- (wx.ACCEL_NORMAL, ord('+'), self.animation_speed_up),
- (wx.ACCEL_NORMAL, ord('='), self.animation_speed_up),
- (wx.ACCEL_SHIFT, ord('='), self.animation_speed_up),
- (wx.ACCEL_NORMAL, wx.WXK_ADD, self.animation_speed_up),
- (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_ADD, self.animation_speed_up),
- (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_UP, self.animation_speed_up),
+ (wx.ACCEL_NORMAL, wx.WXK_RIGHT, self.animation_forward),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_RIGHT, self.animation_forward),
+ (wx.ACCEL_NORMAL, wx.WXK_LEFT, self.animation_reverse),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_LEFT, self.animation_reverse),
(wx.ACCEL_NORMAL, wx.WXK_UP, self.animation_speed_up),
- (wx.ACCEL_NORMAL, ord('-'), self.animation_slow_down),
- (wx.ACCEL_NORMAL, ord('_'), self.animation_slow_down),
- (wx.ACCEL_NORMAL, wx.WXK_SUBTRACT, self.animation_slow_down),
- (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_SUBTRACT, self.animation_slow_down),
- (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_DOWN, self.animation_slow_down),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_UP, self.animation_speed_up),
(wx.ACCEL_NORMAL, wx.WXK_DOWN, self.animation_slow_down),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_DOWN, self.animation_slow_down),
+ (wx.ACCEL_NORMAL, ord('+'), self.animation_one_frame_forward),
+ (wx.ACCEL_NORMAL, ord('='), self.animation_one_frame_forward),
+ (wx.ACCEL_SHIFT, ord('='), self.animation_one_frame_forward),
+ (wx.ACCEL_NORMAL, wx.WXK_ADD, self.animation_one_frame_forward),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_ADD, self.animation_one_frame_forward),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_UP, self.animation_one_frame_forward),
+ (wx.ACCEL_NORMAL, ord('-'), self.animation_one_frame_back),
+ (wx.ACCEL_NORMAL, ord('_'), self.animation_one_frame_back),
+ (wx.ACCEL_NORMAL, wx.WXK_SUBTRACT, self.animation_one_frame_back),
+ (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_SUBTRACT, self.animation_one_frame_back),
(wx.ACCEL_NORMAL, ord('r'), self.animation_restart),
(wx.ACCEL_NORMAL, ord('p'), self.animation_pause),
+ (wx.ACCEL_NORMAL, wx.WXK_SPACE, self.animation_pause),
(wx.ACCEL_NORMAL, ord('q'), self.animation_quit)]
accel_entries = []
@@ -97,6 +120,7 @@ class EmbroiderySimulator(wx.Frame):
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_CLOSE, self.on_close)
+ self.Bind(wx.EVT_SLIDER, self.on_slider)
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.panel.SetFocus()
@@ -114,36 +138,73 @@ class EmbroiderySimulator(wx.Frame):
def load(self, stitch_plan=None):
if stitch_plan:
self.mirror = False
- self.segments = self._stitch_plan_to_segments(stitch_plan)
- else:
+ self.stitch_plan_to_lines(stitch_plan)
+ self.move_to_top_left()
return
- self.trim_margins()
- self.calculate_dimensions()
-
def adjust_speed(self, duration):
- self.frame_period = 1000 * float(duration) / len(self.segments)
+ self.frame_period = 1000 * float(duration) / len(self.lines)
self.stitches_per_frame = 1
while self.frame_period < 1.0:
self.frame_period *= 2
self.stitches_per_frame *= 2
- def animation_speed_up(self, event):
- if self.frame_period == 1:
- self.stitches_per_frame *= 2
+ # Switch direction button (currently not in use - would this be better?)
+ def animation_switch_direction(self, event):
+ direction_button = event.GetEventObject()
+ lbl = direction_button.GetLabel()
+ if self.animation_direction == 1:
+ self.animation_reverse('backward')
+ direction_button.SetLabel('<<')
else:
- self.frame_period = self.frame_period / 2
+ self.animation_forward('forward')
+ direction_button.SetLabel('>>')
+
+ def animation_forward(self, event):
+ self.animation_direction = 1
+ if not self.timer.IsRunning():
+ self.timer.StartOnce(self.frame_period)
+
+ def animation_reverse(self, event):
+ self.animation_direction = -1
+ if not self.timer.IsRunning():
+ self.timer.StartOnce(self.frame_period)
+
+ def animation_one_frame_forward(self, event):
+ if self.current_frame < len(self.lines):
+ self.timer.Stop()
+ self.current_frame = self.current_frame + 1
+ self.draw_one_frame()
+ self.set_stitch_counter(self.current_frame)
+ self.set_stitch_slider(self.current_frame)
+
+ def animation_one_frame_back(self, event):
+ if self.current_frame > 1:
+ self.timer.Stop()
+ self.current_frame = self.current_frame - 1
+ self.draw_one_frame()
+ self.set_stitch_counter(self.current_frame)
+ self.set_stitch_slider(self.current_frame)
+
+ def animation_speed_up(self, event):
+ if self.stitches_per_frame <= 1280:
+ if self.frame_period == 1:
+ self.stitches_per_frame *= 2
+ else:
+ self.frame_period = self.frame_period / 2
self.animation_update_timer()
def animation_slow_down(self, event):
- if self.stitches_per_frame == 1:
- self.frame_period *= 2
- else:
- self.stitches_per_frame /= 2
+ if self.frame_period <= 1280:
+ if self.stitches_per_frame == 1:
+ self.frame_period *= 2
+ else:
+ self.stitches_per_frame /= 2
self.animation_update_timer()
def animation_restart(self, event):
+ self.current_frame = 1
self.stop()
self.clear()
self.go()
@@ -152,7 +213,7 @@ class EmbroiderySimulator(wx.Frame):
if self.timer.IsRunning():
self.timer.Stop()
else:
- self.timer.Start(self.frame_period)
+ self.timer.StartOnce(self.frame_period)
def animation_quit(self, event):
self.Close()
@@ -160,19 +221,32 @@ class EmbroiderySimulator(wx.Frame):
def animation_update_timer(self):
self.frame_period = max(1, self.frame_period)
self.stitches_per_frame = max(self.stitches_per_frame, 1)
+ self.set_stitch_counter(self.current_frame)
if self.timer.IsRunning():
self.timer.Stop()
- self.timer.Start(self.frame_period)
+ self.timer.StartOnce(self.frame_period)
- def set_stitch_counter(self, current_stitch):
- if hasattr(self.panel, 'stitch_counter'):
- self.panel.stitch_counter.SetLabel(_("Stitch # ") + str(current_stitch) + ' / ' + str(len(self.segments) + 1))
- else:
- self.font = wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
- self.panel.stitch_counter = wx.StaticText(self, label=_("Stitch #") + '1 / ' + str(len(self.segments)), pos=(30, 10))
- self.panel.stitch_counter.SetFont(self.font)
- self.panel.stitch_counter.SetForegroundColour('red')
- self.panel.stitch_counter.SetBackgroundColour('white')
+ def set_stitch_counter(self, current_frame):
+ self.dc.SetTextForeground('red')
+ stitch_counter_text = _("Stitch # ") + \
+ str(current_frame) + ' / ' + str(len(self.lines))
+ self.dc.DrawText(stitch_counter_text, 30, 5)
+
+ def on_slider(self, event):
+ self.panel.SetFocus()
+ self.draw_one_frame()
+ obj = event.GetEventObject()
+ self.current_frame = obj.GetValue()
+ self.animation_update_timer()
+
+ def set_slider(self):
+ self.stitch_slider = wx.Slider(
+ self, value=1, minValue=1, maxValue=len(
+ self.lines), style=wx.SL_HORIZONTAL | wx.SL_LABELS)
+ self.slider_sizer.Add(self.stitch_slider, 1, wx.EXPAND)
+
+ def set_stitch_slider(self, val):
+ self.stitch_slider.SetValue(val)
def _strip_quotes(self, string):
if string.startswith('"') and string.endswith('"'):
@@ -183,55 +257,51 @@ class EmbroiderySimulator(wx.Frame):
def color_to_pen(self, color):
return wx.Pen(color.visible_on_white.rgb)
- def _stitch_plan_to_segments(self, stitch_plan):
- segments = []
+ def stitch_plan_to_lines(self, stitch_plan):
+ self.pens = []
+ self.lines = []
for color_block in stitch_plan:
pen = self.color_to_pen(color_block.color)
- for point_list in color_block_to_point_lists(color_block):
+ for i, point_list in enumerate(
+ color_block_to_point_lists(color_block)):
+ if i == 0:
+ # add the first stitch
+ first_x, first_y = point_list[0]
+ self.lines.append((first_x, first_y, first_x, first_y))
+ self.pens.append(pen)
+
# if there's only one point, there's nothing to do, so skip
if len(point_list) < 2:
continue
for start, end in izip(point_list[:-1], point_list[1:]):
- segments.append(((start, end), pen))
-
- return segments
-
- def all_coordinates(self):
- for segment in self.segments:
- start, end = segment[0]
+ line = (start[0], start[1], end[0], end[1])
+ self.lines.append(line)
+ self.pens.append(pen)
- yield start
- yield end
-
- def trim_margins(self):
+ def move_to_top_left(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 = []
+ min_x = sys.maxsize
+ min_y = sys.maxsize
- for segment in self.segments:
- (start, end), color = segment
+ for x1, y1, x2, y2 in self.lines:
+ min_x = min(min_x, x2)
+ min_y = min(min_y, y2)
- new_segment = (
- (
- (start[0] - min_x, start[1] - min_y),
- (end[0] - min_x, end[1] - min_y),
- ),
- color
- )
+ new_lines = []
- new_segments.append(new_segment)
+ for line in self.lines:
+ (start, end, start1, end1) = line
+ new_lines.append(
+ (start - min_x,
+ end - min_y,
+ start1 - min_x,
+ end1 - min_y))
- self.segments = new_segments
+ self.lines = new_lines
def calculate_dimensions(self):
# 0.01 avoids a division by zero below for designs with no width or
@@ -239,26 +309,51 @@ class EmbroiderySimulator(wx.Frame):
width = 0.01
height = 0.01
- for x, y in self.all_coordinates():
- width = max(width, x)
- height = max(height, y)
+ for x1, y1, x2, y2 in self.lines:
+ width = max(width, x2)
+ height = max(height, y2)
self.width = width
self.height = height
- self.scale = min(float(self.max_width - self.margin * 2) / width, float(self.max_height - self.margin * 2 - 40) / height)
+
+ button_width, button_height = self.buttons[0].GetSize()
+ slider_width, slider_height = self.stitch_slider.GetSize()
+ self.controls_height = button_height + slider_height
+
+ self.scale = min(
+ float(
+ self.max_width -
+ self.margin *
+ 2) /
+ width,
+ float(
+ self.max_height -
+ self.margin *
+ 2 -
+ self.controls_height) /
+ height)
# make room for decorations and the margin
self.scale *= 0.95
+ for i, point in enumerate(self.lines):
+ x1, x2, y1, y2 = point
+ x1 = x1 * self.scale + self.margin
+ y1 = y1 * self.scale + self.margin
+ x2 = x2 * self.scale + self.margin
+ y2 = y2 * self.scale + self.margin
+
+ self.lines[i] = (x1, x2, y1, y2)
+
def go(self):
self.clear()
- self.current_stitch = 0
+ self.current_frame = 0
if not self.timer:
- self.timer = wx.PyTimer(self.draw_one_frame)
+ self.timer = wx.PyTimer(self.iterate_frames)
- self.timer.Start(self.frame_period)
+ self.timer.StartOnce(self.frame_period)
def on_close(self, event):
self.stop()
@@ -287,57 +382,88 @@ class EmbroiderySimulator(wx.Frame):
client_width, client_height = self.GetClientSize()
decorations_width = window_width - client_width
- decorations_height = window_height - client_height + 40
+ decorations_height = window_height - client_height
- setsize_window_width = self.width * self.scale + decorations_width + self.margin * 2
- setsize_window_height = (self.height) * self.scale + decorations_height + self.margin * 2
+ setsize_window_width = self.width * self.scale + \
+ decorations_width + self.margin * 2
+ setsize_window_height = self.height * self.scale + \
+ decorations_height + self.controls_height + self.margin * 2
# set minimum width (force space for control buttons)
if setsize_window_width < self.min_width:
setsize_window_width = self.min_width
- self.SetSize(( setsize_window_width, setsize_window_height))
+ self.SetSize((setsize_window_width, setsize_window_height))
# center the simulation on screen if not called by params
# else center vertically
- if self.x_position == None:
+ if self.x_position is None:
self.Centre()
else:
display_rect = self.get_current_screen_rect()
- self.SetPosition((self.x_position, display_rect[3] / 2 - setsize_window_height / 2))
+ self.SetPosition(
+ (self.x_position,
+ display_rect[3] /
+ 2 -
+ setsize_window_height /
+ 2))
e.Skip()
def on_paint(self, e):
- dc = wx.PaintDC(self.panel)
- dc.Blit(0, 0, self.buffer.GetWidth(), self.buffer.GetHeight(), self.dc, 0, 0)
+ dc = wx.AutoBufferedPaintDC(self.panel)
+ dc.Blit(
+ 0,
+ 0,
+ self.buffer.GetWidth(),
+ self.buffer.GetHeight(),
+ self.dc,
+ 0,
+ 0)
+
+ self.last_pos_x, self.last_pos_y, self.last_pos_x1, self.last_pos_y1 = self.lines[0]
+
+ if hasattr(self, 'visible_lines'):
+ if len(self.visible_lines) > 0:
+ self.last_pos_x1, self.last_pos_y1, self.last_pos_x, self.last_pos_y = self.visible_lines[-1]
+
+ dc.DrawLine(
+ self.last_pos_x - 10,
+ self.last_pos_y,
+ self.last_pos_x + 10,
+ self.last_pos_y)
+ dc.DrawLine(
+ self.last_pos_x,
+ self.last_pos_y - 10,
+ self.last_pos_x,
+ self.last_pos_y + 10)
+
+ def iterate_frames(self):
+ self.current_frame += self.stitches_per_frame * self.animation_direction
+
+ if self.current_frame <= len(self.lines) and self.current_frame >= 1:
+ # calculate time_to_next_frame
+ start = time.time()
+ self.draw_one_frame()
+ duration = time.time() - start
+ duration_ms = int(duration * 1000)
+ time_to_next_frame = self.frame_period - duration_ms
+ time_to_next_frame = max(1, time_to_next_frame)
+ self.timer.StartOnce(time_to_next_frame)
+ elif self.current_frame > len(self.lines):
+ self.current_frame = len(self.lines)
+ self.draw_one_frame()
+ elif self.current_frame < 1:
+ self.current_frame = 1
+ self.draw_one_frame()
+ else:
+ self.timer.Stop()
- if self.last_pos:
- dc.DrawLine(self.last_pos[0] - 10, self.last_pos[1], self.last_pos[0] + 10, self.last_pos[1])
- dc.DrawLine(self.last_pos[0], self.last_pos[1] - 10, self.last_pos[0], self.last_pos[1] + 10)
+ self.set_stitch_counter(self.current_frame)
+ self.set_stitch_slider(self.current_frame)
def draw_one_frame(self):
- for i in xrange(self.stitches_per_frame):
- try:
- ((x1, y1), (x2, y2)), color = self.segments[self.current_stitch]
-
- if self.mirror:
- y1 = self.height - y1
- y2 = self.height - y2
-
- x1 = x1 * self.scale + self.margin
- y1 = y1 * self.scale + self.margin
- x2 = x2 * self.scale + self.margin
- y2 = y2 * self.scale + self.margin
-
- self.canvas.SetPen(color)
- self.canvas.DrawLines(((x1, y1), (x2, y2)))
- self.Refresh()
-
- self.current_stitch += 1
- self.last_pos = (x2, y2)
-
- self.set_stitch_counter(self.current_stitch + 1)
-
- except IndexError:
- self.timer.Stop()
+ self.clear()
+ self.visible_lines = self.lines[:self.current_frame]
+ self.dc.DrawLineList(self.visible_lines,
+ self.pens[:self.current_frame])
diff --git a/messages.po b/messages.po
index 9105da77..e3f14902 100644
--- a/messages.po
+++ b/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2018-08-24 20:56-0400\n"
+"POT-Creation-Date: 2018-08-25 11:47-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -487,7 +487,7 @@ msgstr ""
msgid "Install"
msgstr ""
-#: lib/extensions/install.py:40 lib/extensions/params.py:380
+#: lib/extensions/install.py:40 lib/extensions/params.py:379
msgid "Cancel"
msgstr ""
@@ -576,61 +576,61 @@ msgstr ""
msgid "Inkscape objects"
msgstr ""
-#: lib/extensions/params.py:346
+#: lib/extensions/params.py:345
msgid "Embroidery Params"
msgstr ""
-#: lib/extensions/params.py:363
+#: lib/extensions/params.py:362
msgid "Presets"
msgstr ""
-#: lib/extensions/params.py:368
+#: lib/extensions/params.py:367
msgid "Load"
msgstr ""
-#: lib/extensions/params.py:371
+#: lib/extensions/params.py:370
msgid "Add"
msgstr ""
-#: lib/extensions/params.py:374
+#: lib/extensions/params.py:373
msgid "Overwrite"
msgstr ""
-#: lib/extensions/params.py:377
+#: lib/extensions/params.py:376
msgid "Delete"
msgstr ""
-#: lib/extensions/params.py:384
+#: lib/extensions/params.py:383
msgid "Use Last Settings"
msgstr ""
-#: lib/extensions/params.py:387
+#: lib/extensions/params.py:386
msgid "Apply and Quit"
msgstr ""
-#: lib/extensions/params.py:439
+#: lib/extensions/params.py:447
msgid "Preview"
msgstr ""
-#: lib/extensions/params.py:458
+#: lib/extensions/params.py:466
msgid "Internal Error"
msgstr ""
-#: lib/extensions/params.py:511
+#: lib/extensions/params.py:519
msgid "Please enter or select a preset name first."
msgstr ""
-#: lib/extensions/params.py:511 lib/extensions/params.py:517
-#: lib/extensions/params.py:545
+#: lib/extensions/params.py:519 lib/extensions/params.py:525
+#: lib/extensions/params.py:553
msgid "Preset"
msgstr ""
-#: lib/extensions/params.py:517
+#: lib/extensions/params.py:525
#, python-format
msgid "Preset \"%s\" not found."
msgstr ""
-#: lib/extensions/params.py:545
+#: lib/extensions/params.py:553
#, python-format
msgid ""
"Preset \"%s\" already exists. Please use another name or press "
@@ -674,54 +674,82 @@ msgstr ""
msgid "Generate INX files"
msgstr ""
-#: lib/simulator.py:40
-msgid "Speed up"
+#: lib/simulator.py:45
+msgid "<<"
msgstr ""
-#: lib/simulator.py:40
-msgid "Press + or arrow up to speed up"
+#: lib/simulator.py:45
+msgid "Play reverse (arrow left)"
msgstr ""
-#: lib/simulator.py:41
-msgid "Slow down"
+#: lib/simulator.py:46
+msgid "-"
msgstr ""
-#: lib/simulator.py:41
-msgid "Press - or arrow down to slow down"
+#: lib/simulator.py:46
+msgid "Play one frame backward (+)"
msgstr ""
-#: lib/simulator.py:42
+#: lib/simulator.py:47
+msgid "+"
+msgstr ""
+
+#: lib/simulator.py:47
+msgid "Play one frame forward (+)"
+msgstr ""
+
+#: lib/simulator.py:48
+msgid ">>"
+msgstr ""
+
+#: lib/simulator.py:48
+msgid "Play forward (arrow right)"
+msgstr ""
+
+#: lib/simulator.py:49
+msgid "^"
+msgstr ""
+
+#: lib/simulator.py:49
+msgid "Speed up (arrow up)"
+msgstr ""
+
+#: lib/simulator.py:50
+msgid "v"
+msgstr ""
+
+#: lib/simulator.py:50
+msgid "Slow down (arrow down)"
+msgstr ""
+
+#: lib/simulator.py:51
msgid "Pause"
msgstr ""
-#: lib/simulator.py:42
-msgid "Press P to pause the animation"
+#: lib/simulator.py:51
+msgid "Pause (P)"
msgstr ""
-#: lib/simulator.py:43
+#: lib/simulator.py:52
msgid "Restart"
msgstr ""
-#: lib/simulator.py:43
-msgid "Press R to restart the animation"
+#: lib/simulator.py:52
+msgid "Restart (R)"
msgstr ""
-#: lib/simulator.py:44
+#: lib/simulator.py:53
msgid "Quit"
msgstr ""
-#: lib/simulator.py:44
-msgid "Press Q to close the simulation window"
+#: lib/simulator.py:53
+msgid "Close (Q)"
msgstr ""
-#: lib/simulator.py:169
+#: lib/simulator.py:231
msgid "Stitch # "
msgstr ""
-#: lib/simulator.py:172
-msgid "Stitch #"
-msgstr ""
-
#: lib/stitches/auto_fill.py:167
msgid ""
"Unable to autofill. This most often happens because your shape is made "