From bc1ea4fa30f1a15f02ee0a782301e6605d2eb84f Mon Sep 17 00:00:00 2001 From: tatarize Date: Wed, 27 Mar 2024 09:33:20 -0700 Subject: [PATCH 1/6] Initial stub QLI reader --- pyembroidery/EmbPattern.py | 12 ++++++++++++ pyembroidery/QliReader.py | 5 +++++ pyembroidery/QliWriter.py | 7 +++++++ 3 files changed, 24 insertions(+) create mode 100644 pyembroidery/QliReader.py create mode 100644 pyembroidery/QliWriter.py diff --git a/pyembroidery/EmbPattern.py b/pyembroidery/EmbPattern.py index 7b4e7e0..72950fe 100644 --- a/pyembroidery/EmbPattern.py +++ b/pyembroidery/EmbPattern.py @@ -69,6 +69,7 @@ import pyembroidery.ZhsReader as ZhsReader import pyembroidery.ZxyReader as ZxyReader +from . import QliReader, QliWriter from .EmbEncoder import Transcoder as Normalizer from .EmbFunctions import * @@ -1397,6 +1398,17 @@ def supported_formats(): }, } ) + yield ( + { + "description": "Statler Stitcher", + "extension": "qli", + "extensions": ("qli",), + "mimetype": "text/plain", + "category": "quilting", + "reader": QliReader, + "writer": QliWriter, + } + ) yield ( { "description": "Husqvarna Embroidery Format", diff --git a/pyembroidery/QliReader.py b/pyembroidery/QliReader.py new file mode 100644 index 0000000..b8b3eb3 --- /dev/null +++ b/pyembroidery/QliReader.py @@ -0,0 +1,5 @@ +from .EmbFunctions import * + + +def read(f, out, settings=None): + pass diff --git a/pyembroidery/QliWriter.py b/pyembroidery/QliWriter.py new file mode 100644 index 0000000..e50604a --- /dev/null +++ b/pyembroidery/QliWriter.py @@ -0,0 +1,7 @@ +import math + +from .EmbFunctions import * + + +def write(pattern, f, settings=None): + pass From bc1b76d545bd8a3edb5c90705fe1b51628e95581 Mon Sep 17 00:00:00 2001 From: tatarize Date: Thu, 23 May 2024 13:28:27 -0700 Subject: [PATCH 2/6] Add stub for plt files --- pyembroidery/EmbPattern.py | 17 ++++++++++++++++- pyembroidery/PltReader.py | 5 +++++ pyembroidery/PltWriter.py | 7 +++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 pyembroidery/PltReader.py create mode 100644 pyembroidery/PltWriter.py diff --git a/pyembroidery/EmbPattern.py b/pyembroidery/EmbPattern.py index 72950fe..e76e327 100644 --- a/pyembroidery/EmbPattern.py +++ b/pyembroidery/EmbPattern.py @@ -50,6 +50,10 @@ import pyembroidery.PmvReader as PmvReader import pyembroidery.PmvWriter as PmvWriter import pyembroidery.PngWriter as PngWriter +import pyembroidery.PltReader as PltReader +import pyembroidery.PltWriter as PltWriter +import pyembroidery.QliReader as QliReader +import pyembroidery.QliWriter as QliWriter import pyembroidery.SewReader as SewReader import pyembroidery.ShvReader as ShvReader import pyembroidery.SpxReader as SpxReader @@ -69,7 +73,7 @@ import pyembroidery.ZhsReader as ZhsReader import pyembroidery.ZxyReader as ZxyReader -from . import QliReader, QliWriter + from .EmbEncoder import Transcoder as Normalizer from .EmbFunctions import * @@ -1409,6 +1413,17 @@ def supported_formats(): "writer": QliWriter, } ) + yield ( + { + "description": "HPGL2", + "extension": "plt", + "extensions": ("plt",), + "mimetype": "text/plain", + "category": "quilting", + "reader": PltReader, + "writer": PltWriter, + } + ) yield ( { "description": "Husqvarna Embroidery Format", diff --git a/pyembroidery/PltReader.py b/pyembroidery/PltReader.py new file mode 100644 index 0000000..b8b3eb3 --- /dev/null +++ b/pyembroidery/PltReader.py @@ -0,0 +1,5 @@ +from .EmbFunctions import * + + +def read(f, out, settings=None): + pass diff --git a/pyembroidery/PltWriter.py b/pyembroidery/PltWriter.py new file mode 100644 index 0000000..e50604a --- /dev/null +++ b/pyembroidery/PltWriter.py @@ -0,0 +1,7 @@ +import math + +from .EmbFunctions import * + + +def write(pattern, f, settings=None): + pass From f328137e3744c01b50c4e7bbccb620a656a33bfd Mon Sep 17 00:00:00 2001 From: tatarize Date: Thu, 23 May 2024 14:33:35 -0700 Subject: [PATCH 3/6] Early draft of PltWriter --- pyembroidery/PltWriter.py | 53 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/pyembroidery/PltWriter.py b/pyembroidery/PltWriter.py index e50604a..d310dc2 100644 --- a/pyembroidery/PltWriter.py +++ b/pyembroidery/PltWriter.py @@ -1,7 +1,56 @@ -import math +""" +HPGL2 Plot vector graphics are used commonly in pen plotters and vinyl cutters and have been a pretty mature language +since the 1970s. Here we are using a subset of the main language and commands to work with somewhat common form of +quilting machine. While this will process a lot of the more complex methods simple quilting stitches are expected to +work and are the typical goal product. + +The standard step size of 1 unit in HPGL is 1/40 mm. As opposed to 1/10 mm which is standard for embroidery. HPGL is +increasing Y is downwards, which is contrary to most embroidery. +""" + +from .WriteHelper import write_string_utf8 from .EmbFunctions import * def write(pattern, f, settings=None): - pass + write_string_utf8(f, "IN;") + write_string_utf8(f, "IP;") + + trimmed = True + + stitches = pattern.stitches + xx = 0 + yy = 0 + + pen_id = 1 + write_string_utf8(f, "SP%d;" % pen_id) + + for stitch in stitches: + # 4 to convert 1/10mm to 1/40mm. + x = stitch[0] * 4.0 + y = stitch[1] * 4.0 + data = stitch[2] & COMMAND_MASK + dx = int(round(x - xx)) + dy = int(round(y - yy)) + xx += dx + yy += dy + if data == STITCH: + write_string_utf8(f, "PD %d, %d;" % (int(xx), int(yy))) + trimmed = False + elif data == JUMP: + if trimmed: + write_string_utf8(f, "PU %d, %d;" % (int(xx), int(yy))) + else: + write_string_utf8(f, "PD %d, %d;" % (int(xx), int(yy))) + elif data == COLOR_CHANGE: + pen_id += 1 + write_string_utf8(f, "SP%d;" % pen_id) + trimmed = True + elif data == STOP: + trimmed = True + elif data == TRIM: + trimmed = True + elif data == END: + write_string_utf8(f, "EN;") + break From 3efe79e112e6fcbc3a930b03e16fe73cf872d045 Mon Sep 17 00:00:00 2001 From: tatarize Date: Thu, 23 May 2024 15:54:37 -0700 Subject: [PATCH 4/6] Add initial .plt test file --- test/test_convert_plt.py | 210 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 test/test_convert_plt.py diff --git a/test/test_convert_plt.py b/test/test_convert_plt.py new file mode 100644 index 0000000..609d7e3 --- /dev/null +++ b/test/test_convert_plt.py @@ -0,0 +1,210 @@ +from __future__ import print_function + +import unittest + +from test.pattern_for_tests import * + + +class TestConverts(unittest.TestCase): + + def position_equals(self, stitches, j, k): + self.assertEqual(stitches[j][:1], stitches[k][:1]) + + def test_convert_plt_to_u01(self): + file1 = "convert_u01.plt" + file2 = "converted_plt.u01" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 16) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->u01: ", t_pattern.stitches) + # self.addCleanup(os.remove, file1) + # self.addCleanup(os.remove, file2) + + def test_convert_plt_to_csv(self): + file1 = "convert_csv.plt" + file2 = "converted_plt.csv" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->csv: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_exp(self): + file1 = "convert_exp.plt" + file2 = "converted_plt.exp" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->exp: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_pes(self): + file1 = "convert_pes.plt" + file2 = "converted_plt.pes" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->pes: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_jef(self): + file1 = "convert_jef.plt" + file2 = "converted_plt.jef" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->jef: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_pec(self): + file1 = "convert_pec.plt" + file2 = "converted_plt.pec" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->pec: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_vp3(self): + file1 = "convert_vp3.plt" + file2 = "converted_plt.vp3" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->vp3: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_dst(self): + file1 = "convert_dst.plt" + file2 = "converted_plt.dst" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->dst: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_gcode(self): + file1 = "convert_gcode.plt" + file2 = "converted_plt.gcode" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->gcode: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_xxx(self): + file1 = "convert_xxx.plt" + file2 = "converted_plt.xxx" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->xxx: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_convert_plt_to_tbf(self): + file1 = "convert_tbf.plt" + file2 = "converted_plt.tbf" + get_big_pattern().write(file1) + f_pattern = EmbPattern(file1) + f_pattern.write(file2) + t_pattern = EmbPattern(file2) + + self.assertIsNotNone(t_pattern) + self.assertEqual(t_pattern.count_stitch_commands(NEEDLE_SET), 16) + self.assertEqual(t_pattern.count_stitch_commands(STITCH), 16 * 5) + self.position_equals(t_pattern.stitches, 0, -1) + print("plt->tbf: ", t_pattern.stitches) + self.addCleanup(os.remove, file1) + self.addCleanup(os.remove, file2) + + def test_plt_stop_write_large(self): + file = "stop2.plt" + pattern = get_shift_stop_pattern() + n_pattern = pattern.get_normalized_pattern() + self.assertEqual(n_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(n_pattern.count_stitch_commands(STITCH), 16 * 5) + self.assertEqual(n_pattern.count_stitch_commands(STOP), 5) + + pattern.write(file) + f_pattern = EmbPattern(file) + + self.assertIsNotNone(f_pattern) + + with open(file, "rb") as f: + f.seek(0x18) + colors = f.read(1) + self.assertEqual(ord(colors), f_pattern.count_color_changes() + f_pattern.count_stitch_commands(STOP) + 1) + + self.assertEqual(f_pattern.count_stitch_commands(COLOR_CHANGE), 15) + self.assertEqual(f_pattern.count_stitch_commands(STITCH), 16 * 5) + self.assertEqual(f_pattern.count_stitch_commands(STOP), 5) + self.addCleanup(os.remove, file) \ No newline at end of file From d0ae4a434d1215a09635478b4cb9a06dc7e74ad1 Mon Sep 17 00:00:00 2001 From: tatarize Date: Thu, 23 May 2024 15:54:58 -0700 Subject: [PATCH 5/6] Minor Plt writer fixes --- pyembroidery/PltWriter.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pyembroidery/PltWriter.py b/pyembroidery/PltWriter.py index d310dc2..1713fce 100644 --- a/pyembroidery/PltWriter.py +++ b/pyembroidery/PltWriter.py @@ -14,8 +14,8 @@ def write(pattern, f, settings=None): - write_string_utf8(f, "IN;") - write_string_utf8(f, "IP;") + write_string_utf8(f, "IN;\n") + write_string_utf8(f, "IP;\n") trimmed = True @@ -24,7 +24,7 @@ def write(pattern, f, settings=None): yy = 0 pen_id = 1 - write_string_utf8(f, "SP%d;" % pen_id) + write_string_utf8(f, "SP%d;\n" % pen_id) for stitch in stitches: # 4 to convert 1/10mm to 1/40mm. @@ -36,21 +36,24 @@ def write(pattern, f, settings=None): xx += dx yy += dy if data == STITCH: - write_string_utf8(f, "PD %d, %d;" % (int(xx), int(yy))) + if trimmed: + write_string_utf8(f, "PU %d, %d;\n" % (int(xx), int(yy))) + else: + write_string_utf8(f, "PD %d, %d;\n" % (int(xx), int(yy))) trimmed = False elif data == JUMP: if trimmed: - write_string_utf8(f, "PU %d, %d;" % (int(xx), int(yy))) + write_string_utf8(f, "PU %d, %d;\n" % (int(xx), int(yy))) else: - write_string_utf8(f, "PD %d, %d;" % (int(xx), int(yy))) + write_string_utf8(f, "PD %d, %d;\n" % (int(xx), int(yy))) elif data == COLOR_CHANGE: pen_id += 1 - write_string_utf8(f, "SP%d;" % pen_id) + write_string_utf8(f, "SP%d;\n" % pen_id) trimmed = True elif data == STOP: trimmed = True elif data == TRIM: trimmed = True elif data == END: - write_string_utf8(f, "EN;") + write_string_utf8(f, "EN;\n") break From 303a46112fc2b95d3b96078524eb79ab3b52e8f2 Mon Sep 17 00:00:00 2001 From: tatarize Date: Fri, 24 May 2024 07:53:04 -0700 Subject: [PATCH 6/6] Initial cursory reader attempt --- pyembroidery/PltReader.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pyembroidery/PltReader.py b/pyembroidery/PltReader.py index b8b3eb3..4a91fba 100644 --- a/pyembroidery/PltReader.py +++ b/pyembroidery/PltReader.py @@ -1,5 +1,36 @@ +import re + from .EmbFunctions import * +PU_COMMAND = re.compile(r"PU\s*([0-9]+)\s*,\s*([0-9]+)") +PD_COMMAND = re.compile(r"PD\s*([0-9]+)\s*,\s*([0-9]+)") +SP_COMMAND = re.compile(r"SP\s*([0-9]+)\s*") + def read(f, out, settings=None): - pass + data = f.read() + data = str(data, "utf8") + lines = data.split(";") + for line in lines: + line = line.strip() + match = PU_COMMAND.match(line) + if match: + x = int(match.group(1)) / 4.0 + y = int(match.group(2)) / 4.0 + out.move_abs(x, y) + out.stitch_abs(x, y) + + match = PD_COMMAND.match(line) + if match: + x = int(match.group(1)) / 4.0 + y = int(match.group(2)) / 4.0 + out.stitch_abs(x, y) + + match = SP_COMMAND.match(line) + if match: + pen = int(match.group(1)) + out.needle_change(pen) + + if line == "EN": + print("Done") + break