diff --git a/pyembroidery/EmbPattern.py b/pyembroidery/EmbPattern.py index 7b4e7e0..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 @@ -70,6 +74,7 @@ import pyembroidery.ZhsReader as ZhsReader import pyembroidery.ZxyReader as ZxyReader + from .EmbEncoder import Transcoder as Normalizer from .EmbFunctions import * from .EmbThread import EmbThread @@ -1397,6 +1402,28 @@ def supported_formats(): }, } ) + yield ( + { + "description": "Statler Stitcher", + "extension": "qli", + "extensions": ("qli",), + "mimetype": "text/plain", + "category": "quilting", + "reader": QliReader, + "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..4a91fba --- /dev/null +++ b/pyembroidery/PltReader.py @@ -0,0 +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): + 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 diff --git a/pyembroidery/PltWriter.py b/pyembroidery/PltWriter.py new file mode 100644 index 0000000..1713fce --- /dev/null +++ b/pyembroidery/PltWriter.py @@ -0,0 +1,59 @@ +""" +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): + write_string_utf8(f, "IN;\n") + write_string_utf8(f, "IP;\n") + + trimmed = True + + stitches = pattern.stitches + xx = 0 + yy = 0 + + pen_id = 1 + write_string_utf8(f, "SP%d;\n" % 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: + 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;\n" % (int(xx), int(yy))) + else: + 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;\n" % pen_id) + trimmed = True + elif data == STOP: + trimmed = True + elif data == TRIM: + trimmed = True + elif data == END: + write_string_utf8(f, "EN;\n") + break 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 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