Porównaj commity

...

12 Commity

Autor SHA1 Wiadomość Data
Kaalleen 8eca77d828 satisfy macos 2024-04-22 17:29:37 +02:00
Kaalleen bcfa5f6109 set pagecolor to background color by default 2024-04-21 22:53:19 +02:00
Kaalleen c0231276f6 experimental: background color picker 2024-04-21 11:14:32 +02:00
Kaalleen 21f577d69a rename simulator/realistic preview -> simulator 2024-04-21 10:46:49 +02:00
Lex Neva 80c7f5df7a focus entire simulator panel 2024-04-17 17:43:41 -04:00
Lex Neva 7e2695394d bugfix 2024-04-17 17:43:29 -04:00
Lex Neva 52998c8a4b remove unused API server 2024-04-17 17:36:21 -04:00
Lex Neva 4ed201ac52 adjust alignment 2024-04-17 17:29:56 -04:00
Lex Neva 0bf86324de don't maximize simulator 2024-04-17 17:23:45 -04:00
Lex Neva 73c4f3a72e avoid overlapping command symbols of different types 2024-04-17 17:08:46 -04:00
Lex Neva e1ef92c38f fix off-by-one error in color bar 2024-04-17 16:55:11 -04:00
Lex Neva a206ca4ff9 remove unused function 2024-04-17 16:51:23 -04:00
15 zmienionych plików z 84 dodań i 255 usunięć

Wyświetl plik

@ -1,6 +0,0 @@
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
from .server import APIServer

Wyświetl plik

@ -1,11 +0,0 @@
import os
from flask import Blueprint, jsonify
languages = Blueprint('languages', __name__)
@languages.route('')
def get_lang():
languages = dict(os.environ)
return jsonify(languages)

Wyświetl plik

@ -1,36 +0,0 @@
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
from flask import Blueprint, g, jsonify
page_specs = Blueprint('page_specs', __name__)
@page_specs.route('')
def get_page_specs():
svg = g.extension.document.getroot()
width = svg.get('width', 0)
height = svg.get('height', 0)
pagecolor = "white"
deskcolor = "white"
bordercolor = "black"
showpageshadow = True
namedview = svg.namedview
if namedview is not None:
pagecolor = namedview.get('pagecolor', pagecolor)
deskcolor = namedview.get('inkscape:deskcolor', deskcolor)
bordercolor = namedview.get('bordercolor', bordercolor)
showpageshadow = namedview.get('inkscape:showpageshadow', showpageshadow)
page_specs = {
"width": width,
"height": height,
"pagecolor": pagecolor,
"deskcolor": deskcolor,
"bordercolor": bordercolor,
"showpageshadow": showpageshadow
}
return jsonify(page_specs)

Wyświetl plik

@ -1,121 +0,0 @@
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import errno
import logging
import socket
import sys
import time
from threading import Thread
from contextlib import closing
import requests
from flask import Flask, g
from werkzeug.serving import make_server
from ..utils.json import InkStitchJSONProvider
from .simulator import simulator
from .stitch_plan import stitch_plan
from .page_specs import page_specs
from .lang import languages
# this for electron axios
from flask_cors import CORS
class APIServer(Thread):
def __init__(self, *args, **kwargs):
self.extension = args[0]
Thread.__init__(self, *args[1:], **kwargs)
self.daemon = True
self.app = None
self.host = None
self.port = None
self.ready = False
self.__setup_app()
self.flask_server = None
self.server_thread = None
def __setup_app(self): # noqa: C901
# Disable warning about using a development server in a production environment
cli = sys.modules['flask.cli']
cli.show_server_banner = lambda *x: None
self.app = Flask(__name__)
CORS(self.app)
self.app.json = InkStitchJSONProvider(self.app)
self.app.register_blueprint(simulator, url_prefix="/simulator")
self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan")
self.app.register_blueprint(page_specs, url_prefix="/page_specs")
self.app.register_blueprint(languages, url_prefix="/languages")
@self.app.before_request
def store_extension():
# make the InkstitchExtension object available to the view handling
# this request
g.extension = self.extension
@self.app.route('/ping')
def ping():
return "pong"
def stop(self):
self.flask_server.shutdown()
self.server_thread.join()
def disable_logging(self):
logging.getLogger('werkzeug').setLevel(logging.ERROR)
# https://github.com/aluo-x/Learning_Neural_Acoustic_Fields/blob/master/train.py
# https://github.com/pytorch/pytorch/issues/71029
def find_free_port(self):
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('localhost', 0))
return s.getsockname()[1]
def run(self):
self.disable_logging()
self.host = "127.0.0.1"
self.port = self.find_free_port()
self.flask_server = make_server(self.host, self.port, self.app)
self.server_thread = Thread(target=self.flask_server.serve_forever)
self.server_thread.start()
def ready_checker(self):
"""Wait until the server is started.
Annoyingly, there's no way to get a callback to be run when the Flask
server starts. Instead, we'll have to poll.
"""
while True:
if self.port:
try:
response = requests.get("http://%s:%s/ping" % (self.host, self.port))
if response.status_code == 200:
break
except socket.error as e:
if e.errno == errno.ECONNREFUSED:
pass
else:
raise
time.sleep(0.1)
def start_server(self):
"""Start the API server.
returns: port (int) -- the port that the server is listening on
(on localhost)
"""
checker = Thread(target=self.ready_checker)
checker.start()
self.start()
checker.join()
return self.port

Wyświetl plik

@ -1,8 +0,0 @@
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
from flask import Blueprint
simulator = Blueprint('simulator', __name__)

Wyświetl plik

@ -1,29 +0,0 @@
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
from flask import Blueprint, g, jsonify
from ..exceptions import InkstitchException, format_uncaught_exception
from ..stitch_plan import stitch_groups_to_stitch_plan
stitch_plan = Blueprint('stitch_plan', __name__)
@stitch_plan.route('')
def get_stitch_plan():
if not g.extension.get_elements():
return dict(colors=[], stitch_blocks=[], commands=[])
try:
metadata = g.extension.get_inkstitch_metadata()
collapse_len = metadata['collapse_len_mm']
min_stitch_len = metadata['min_stitch_len_mm']
stitch_groups = g.extension.elements_to_stitch_groups(g.extension.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
return jsonify(stitch_plan)
except InkstitchException as exc:
return jsonify({"error_message": str(exc)}), 500
except Exception:
return jsonify({"error_message": format_uncaught_exception()}), 500

Wyświetl plik

@ -14,6 +14,7 @@ from ..gui.simulator import SplitSimulatorWindow
from ..i18n import _
from ..svg import get_correction_transform
from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_LETTERING, SVG_GROUP_TAG
from ..utils.svg_data import get_pagecolor
from .commands import CommandsExtension
@ -59,6 +60,7 @@ class Lettering(CommandsExtension):
def effect(self):
metadata = self.get_inkstitch_metadata()
background_color = get_pagecolor(self.svg.namedview)
app = wx.App()
frame = SplitSimulatorWindow(
title=_("Ink/Stitch Lettering"),
@ -66,6 +68,7 @@ class Lettering(CommandsExtension):
group=self.get_or_create_group(),
on_cancel=self.cancel,
metadata=metadata,
background_color=background_color,
target_duration=1
)

Wyświetl plik

@ -15,7 +15,6 @@ from secrets import randbelow
import wx
from wx.lib.scrolledpanel import ScrolledPanel
from .base import InkstitchExtension
from ..commands import is_command, is_command_symbol
from ..elements import (Clone, EmbroideryElement, FillStitch, Polyline,
SatinColumn, Stroke)
@ -28,7 +27,9 @@ from ..stitch_plan import stitch_groups_to_stitch_plan
from ..svg.tags import SVG_POLYLINE_TAG
from ..utils import get_resource_dir
from ..utils.param import ParamOption
from ..utils.svg_data import get_pagecolor
from ..utils.threading import ExitThread, check_stop_flag
from .base import InkstitchExtension
def grouper(iterable_obj, count, fillvalue=None):
@ -473,10 +474,11 @@ class ParamsTab(ScrolledPanel):
class SettingsPanel(wx.Panel):
def __init__(self, parent, tabs_factory=None, on_cancel=None, metadata=None, simulator=None):
def __init__(self, parent, tabs_factory=None, on_cancel=None, metadata=None, background_color='white', simulator=None):
self.tabs_factory = tabs_factory
self.cancel_hook = on_cancel
self.metadata = metadata
self.background_color = background_color
self.simulator = simulator
self.parent = parent
@ -782,12 +784,14 @@ class Params(InkstitchExtension):
try:
app = wx.App()
metadata = self.get_inkstitch_metadata()
background_color = get_pagecolor(self.svg.namedview)
frame = SplitSimulatorWindow(
title=_("Embroidery Params"),
panel_class=SettingsPanel,
tabs_factory=self.create_tabs,
on_cancel=self.cancel,
metadata=metadata,
background_color=background_color,
target_duration=5
)

Wyświetl plik

@ -5,9 +5,10 @@
import wx
from .base import InkstitchExtension
from ..gui.simulator import SimulatorWindow
from ..stitch_plan import stitch_groups_to_stitch_plan
from ..utils.svg_data import get_pagecolor
from .base import InkstitchExtension
class Simulator(InkstitchExtension):
@ -23,19 +24,15 @@ class Simulator(InkstitchExtension):
min_stitch_len = metadata['min_stitch_len_mm']
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
background_color = get_pagecolor(self.svg.namedview)
app = wx.App()
current_screen = wx.Display.GetFromPoint(wx.GetMousePosition())
display = wx.Display(current_screen)
screen_rect = display.GetClientArea()
simulator_pos = (screen_rect[0], screen_rect[1])
# subtract 1 because otherwise the window becomes maximized on Linux
width = screen_rect[2] - 1
height = screen_rect[3] - 1
simulator = SimulatorWindow(pos=simulator_pos, size=(width, height))
height = int(screen_rect[3] * 0.8)
simulator = SimulatorWindow(size=(0, height), background_color=background_color)
wx.CallLater(100, simulator.Centre)
app.SetTopWindow(simulator)
simulator.Show()
simulator.load(stitch_plan)

Wyświetl plik

@ -13,6 +13,7 @@ from ..gui.simulator import SplitSimulatorWindow
from ..gui.tartan import TartanMainPanel
from ..i18n import _
from ..svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_TARTAN, SVG_GROUP_TAG
from ..utils.svg_data import get_pagecolor
from .base import InkstitchExtension
@ -58,6 +59,7 @@ class Tartan(InkstitchExtension):
errormsg(_("To create a tartan pattern please select at least one element with a fill color."))
return
metadata = self.get_inkstitch_metadata()
background_color = get_pagecolor(self.svg.namedview)
app = wx.App()
frame = SplitSimulatorWindow(
@ -66,6 +68,7 @@ class Tartan(InkstitchExtension):
elements=list(self.elements),
on_cancel=self.cancel,
metadata=metadata,
background_color=background_color,
target_duration=1
)

Wyświetl plik

@ -28,12 +28,13 @@ from . import PresetsPanel, PreviewRenderer, info_dialog
class LetteringPanel(wx.Panel):
DEFAULT_FONT = "small_font"
def __init__(self, parent, simulator, group, on_cancel=None, metadata=None):
def __init__(self, parent, simulator, group, on_cancel=None, metadata=None, background_color='white'):
self.parent = parent
self.simulator = simulator
self.group = group
self.cancel_hook = on_cancel
self.metadata = metadata or dict()
self.background_color = background_color
super().__init__(parent, wx.ID_ANY)

Wyświetl plik

@ -122,6 +122,9 @@ class ControlPanel(wx.Panel):
self.btnColorChange.SetToolTip(_('Show color changes'))
self.btnColorChange.SetBitmap(self.load_icon('color_change'))
self.btnColorChange.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.on_marker_button('color_change', event))
self.btnBackgroundColor = wx.ColourPickerCtrl(self, -1, colour='white', size=self.btnJump.GetSize())
self.btnBackgroundColor.SetToolTip(_("Change background color"))
self.btnBackgroundColor.Bind(wx.EVT_COLOURPICKER_CHANGED, self.on_update_background_color)
if self.detach_callback:
self.btnDetachSimulator = wx.BitmapButton(self, -1, style=self.button_style)
self.btnDetachSimulator.SetToolTip(_('Detach/attach simulator window'))
@ -131,8 +134,10 @@ class ControlPanel(wx.Panel):
# Layout
self.hbSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.hbSizer1.Add(self.slider, 1, wx.EXPAND | wx.RIGHT, 10)
self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_CENTER | wx.Right, 10)
self.hbSizer1.Add(self.totalstitchText, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_TOP | wx.TOP, 25)
self.hbSizer1.Add((1, 1), 0, wx.RIGHT, 10)
self.hbSizer1.Add(self.totalstitchText, 0, wx.ALIGN_TOP | wx.TOP, 25)
self.hbSizer1.Add((1, 1), 0, wx.RIGHT, 10)
self.controls_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Controls")), wx.HORIZONTAL)
self.controls_inner_sizer = wx.BoxSizer(wx.HORIZONTAL)
@ -149,11 +154,12 @@ class ControlPanel(wx.Panel):
self.show_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Show")), wx.HORIZONTAL)
self.show_inner_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.show_inner_sizer.Add(self.btnNpp, 0, wx.EXPAND | wx.ALL, 2)
self.show_inner_sizer.Add(self.btnNpp, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnJump, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnTrim, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnStop, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnColorChange, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnBackgroundColor, 0, wx.EXPAND | wx.ALL, 2)
if self.detach_callback:
self.show_inner_sizer.Add(self.btnDetachSimulator, 0, wx.ALL, 2)
self.show_sizer.Add((1, 1), 1)
@ -228,7 +234,6 @@ class ControlPanel(wx.Panel):
self.accel_table = wx.AcceleratorTable(self.accel_entries)
self.SetAcceleratorTable(self.accel_table)
self.SetFocus()
# wait for layouts so that panel size is set
if self.stitch_plan:
@ -249,11 +254,6 @@ class ControlPanel(wx.Panel):
self.totalstitchText.SetLabel(f"/ { num_stitches }")
self.choose_speed()
def add_color(self, color, num_stitches):
start = self._last_color_block_end + 1
self.slider.add_color_section(ColorSection(color.rgb, start, start + num_stitches - 1))
self._last_color_block_end = self._last_color_block_end + num_stitches
def clear(self):
self.stitches = []
self._set_num_stitches(0)
@ -266,12 +266,14 @@ class ControlPanel(wx.Panel):
self._set_num_stitches(stitch_plan.num_stitches)
stitch_num = 0
last_block_end = 1
for color_block in stitch_plan.color_blocks:
self.stitches.extend(color_block.stitches)
start = stitch_num + 1
end = start + color_block.num_stitches
self.slider.add_color_section(color_block.color.rgb, start, end)
end = start + color_block.num_stitches - 1
self.slider.add_color_section(color_block.color.rgb, last_block_end, end)
last_block_end = end
for stitch_num, stitch in enumerate(color_block.stitches, start):
if stitch.trim:
@ -291,6 +293,13 @@ class ControlPanel(wx.Panel):
def on_marker_button(self, marker_type, event):
self.slider.enable_marker_list(marker_type, event.GetEventObject().GetValue())
def on_update_background_color(self, event):
self.set_background_color(event.Colour)
def set_background_color(self, color):
self.btnBackgroundColor.SetColour(color)
self.drawing_panel.SetBackgroundColour(color)
def choose_speed(self):
if self.target_duration:
self.set_speed(int(self.num_stitches / float(self.target_duration)))
@ -815,11 +824,12 @@ class DrawingPanel(wx.Panel):
class MarkerList(list):
def __init__(self, icon_name, stitch_numbers=()):
def __init__(self, icon_name, offset=0, stitch_numbers=()):
super().__init__(self)
icons_dir = get_resource_dir("icons")
self.icon_name = icon_name
self.icon = wx.Image(os.path.join(icons_dir, f"{icon_name}.png")).ConvertToBitmap()
self.offset = offset
self.enabled = False
self.extend(stitch_numbers)
@ -843,27 +853,27 @@ class SimulatorSlider(wx.Panel):
kwargs['style'] = wx.SL_HORIZONTAL | wx.SL_VALUE_LABEL | wx.SL_TOP | wx.ALIGN_TOP
self._height = self.GetTextExtent("M").y * 4
self._height = self.GetTextExtent("M").y * 6
self.SetMinSize((self._height, self._height))
self.marker_lists = {
"trim": MarkerList("trim"),
"stop": MarkerList("stop"),
"jump": MarkerList("jump"),
"color_change": MarkerList("color_change"),
"jump": MarkerList("jump", 0.17),
"stop": MarkerList("stop", 0.34),
"color_change": MarkerList("color_change", 0.34),
}
self.marker_pen = wx.Pen(wx.Colour(0, 0, 0))
self.color_sections = []
self.margin = 15
self.tab_start = 0
self.tab_width = 0.2
self.tab_height = 0.2
self.color_bar_start = 0.3
self.color_bar_thickness = 0.25
self.tab_width = 0.15
self.tab_height = 0.15
self.color_bar_start = 0.22
self.color_bar_thickness = 0.17
self.marker_start = self.color_bar_start
self.marker_end = 0.75
self.marker_icon_start = 0.75
self.marker_icon_size = self._height // 4
self.marker_end = 0.5
self.marker_icon_start = 0.5
self.marker_icon_size = self._height // 6
self._min = minValue
self._max = maxValue
@ -901,7 +911,7 @@ class SimulatorSlider(wx.Panel):
self._value = 0
self._tab_rect = None
for marker_list in self.marker_lists:
for marker_list in self.marker_lists.values():
marker_list.clear()
def add_color_section(self, color, start, end):
@ -975,11 +985,11 @@ class SimulatorSlider(wx.Panel):
x = _value_to_x(value)
gc.StrokeLine(
x, height * self.marker_start,
x, height * self.marker_end
x, height * (self.marker_end + marker_list.offset)
)
gc.DrawBitmap(
marker_list.icon,
x - self.marker_icon_size / 2, height * self.marker_icon_start,
x - self.marker_icon_size / 2, height * (self.marker_icon_start + marker_list.offset),
self.marker_icon_size, self.marker_icon_size
)
@ -1034,7 +1044,7 @@ class SimulatorSlider(wx.Panel):
class SimulatorPanel(wx.Panel):
""""""
def __init__(self, parent, stitch_plan=None, target_duration=5, stitches_per_second=16, detach_callback=None):
def __init__(self, parent, stitch_plan=None, background_color='white', target_duration=5, stitches_per_second=16, detach_callback=None):
""""""
super().__init__(parent, style=wx.BORDER_SUNKEN)
@ -1045,6 +1055,7 @@ class SimulatorPanel(wx.Panel):
detach_callback=detach_callback)
self.dp = DrawingPanel(self, stitch_plan=stitch_plan, control_panel=self.cp)
self.cp.set_drawing_panel(self.dp)
self.cp.set_background_color(wx.Colour(background_color))
vbSizer = wx.BoxSizer(wx.VERTICAL)
vbSizer.Add(self.dp, 1, wx.EXPAND | wx.ALL, 2)
@ -1068,6 +1079,7 @@ class SimulatorPanel(wx.Panel):
class SimulatorWindow(wx.Frame):
def __init__(self, panel=None, parent=None, **kwargs):
background_color = kwargs.pop('background_color', 'white')
super().__init__(None, title=_("Embroidery Simulation"), **kwargs)
self.SetWindowStyle(wx.FRAME_FLOAT_ON_PARENT | wx.DEFAULT_FRAME_STYLE)
@ -1086,7 +1098,7 @@ class SimulatorWindow(wx.Frame):
self.panel.Show()
else:
self.is_child = False
self.panel = SimulatorPanel(self)
self.panel = SimulatorPanel(self, background_color=background_color)
self.sizer.Add(self.panel, 1, wx.EXPAND)
self.SetSizer(self.sizer)
@ -1096,6 +1108,8 @@ class SimulatorWindow(wx.Frame):
if self.is_child:
self.Bind(wx.EVT_CLOSE, self.on_close)
else:
self.Maximize()
def detach_simulator_panel(self):
self.sizer.Detach(self.panel)
@ -1118,7 +1132,13 @@ class SplitSimulatorWindow(wx.Frame):
self.detached_simulator_frame = None
self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
self.simulator_panel = SimulatorPanel(self.splitter, target_duration=target_duration, detach_callback=self.toggle_detach_simulator)
background_color = kwargs.pop('background_color', 'white')
self.simulator_panel = SimulatorPanel(
self.splitter,
background_color=background_color,
target_duration=target_duration,
detach_callback=self.toggle_detach_simulator
)
self.settings_panel = panel_class(self.splitter, simulator=self.simulator_panel, **kwargs)
self.splitter.SplitVertically(self.settings_panel, self.simulator_panel)
@ -1135,6 +1155,7 @@ class SplitSimulatorWindow(wx.Frame):
self.SetMinSize(self.sizer.CalcMin())
self.simulator_panel.SetFocus()
self.Maximize()
self.Show()
wx.CallLater(100, self.set_sash_position)
@ -1176,7 +1197,7 @@ class SplitSimulatorWindow(wx.Frame):
self.detached_simulator_frame = None
self.Maximize()
self.splitter.UpdateSize()
self.SetFocus()
self.simulator_panel.SetFocus()
self.Raise()
wx.CallLater(100, self.set_sash_position)
global_settings['pop_out_simulator'] = False

Wyświetl plik

@ -26,13 +26,14 @@ from . import CodePanel, CustomizePanel, EmbroideryPanel, HelpPanel
class TartanMainPanel(wx.Panel):
def __init__(self, parent, simulator, elements, on_cancel=None, metadata=None, output_groups=inkex.Group()):
def __init__(self, parent, simulator, elements, on_cancel=None, metadata=None, background_color='white', output_groups=inkex.Group()):
self.parent = parent
self.simulator = simulator
self.elements = elements
self.cancel_hook = on_cancel
self.palette = Palette()
self.metadata = metadata or dict()
self.background_color = background_color
self.output_groups = output_groups
super().__init__(parent, wx.ID_ANY)

Wyświetl plik

@ -0,0 +1,10 @@
# Authors: see git history
#
# Copyright (c) 2024 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
def get_pagecolor(namedview, default_color='white'):
pagecolor = default_color
if namedview is not None:
pagecolor = namedview.get('pagecolor', pagecolor)
return pagecolor

Wyświetl plik

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Simulator / Realistic Preview</name>
<name>Simulator</name>
<id>org.{{ id_inkstitch }}.simulator</id>
<param name="extension" type="string" gui-hidden="true">simulator</param>
<effect implements-custom-gui="true">