From 241963471f33f14c342e12e2f6ec156e1f597d9c Mon Sep 17 00:00:00 2001 From: tatarize Date: Sat, 13 Jul 2019 00:06:26 -0700 Subject: [PATCH 1/3] Merge Patterns and Builtins --- pyembroidery/EmbMatrix.py | 25 ++++++- pyembroidery/EmbPattern.py | 133 ++++++++++++++++++++++++++++++++++++- pyembroidery/EmbThread.py | 35 +++++++++- pyembroidery/__init__.py | 3 +- 4 files changed, 189 insertions(+), 7 deletions(-) diff --git a/pyembroidery/EmbMatrix.py b/pyembroidery/EmbMatrix.py index c849ad6..e22145f 100644 --- a/pyembroidery/EmbMatrix.py +++ b/pyembroidery/EmbMatrix.py @@ -2,8 +2,29 @@ class EmbMatrix: - def __init__(self): - self.m = self.get_identity() + def __init__(self, m=None): + if m is None: + self.m = self.get_identity() + else: + self.m = m + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + return self.m == other.m + + def __matmul__(self, other): + return EmbMatrix(EmbMatrix.matrix_multiply(self.m, other.m)) + + def __rmatmul__(self, other): + return EmbMatrix(EmbMatrix.matrix_multiply(self.m, other.m)) + + def __imatmul__(self, other): + self.m = EmbMatrix.matrix_multiply(self.m, other.m) + + def __str__(self): + return "[%3f, %3f, %3f\n %3f, %3f, %3f\n %3f, %3f, %3f]" % self.m def get_matrix(self): return self.m diff --git a/pyembroidery/EmbPattern.py b/pyembroidery/EmbPattern.py index c12a89a..50d8d89 100644 --- a/pyembroidery/EmbPattern.py +++ b/pyembroidery/EmbPattern.py @@ -7,11 +7,103 @@ class EmbPattern: def __init__(self): self.stitches = [] # type: list self.threadlist = [] # type: list - self.extras = {} + self.extras = {} # type: dict # filename, name, category, author, keywords, comments, are typical self._previousX = 0 # type: float self._previousY = 0 # type: float + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + if not isinstance(other, EmbPattern): + return False + if self.stitches != other.stitches: + return False + if self.threadlist != other.threadlist: + return False + if self.extras != other.extras: + return False + return True + + def __str__(self): + if "name" in self.extras: + return "EmbPattern %s (commands: %3d, threads: %3d)" % \ + (self.extras["name"], len(self.stitches), len(self.threadlist)) + return "EmbPattern (commands: %3d, threads: %3d)" % (len(self.stitches), len(self.threadlist)) + + def __len__(self): + return len(self.stitches) + + def __getitem__(self, item): + if isinstance(item, str): + return self.extras[item] + return self.stitches[item] + + def __setitem__(self, key, value): + if isinstance(key, str): + self.extras[key] = value + else: + self.stitches[key] = value + + def __copy__(self): + return self.copy() + + def __deepcopy__(self): + return self.copy() + + def __iadd__(self, other): + if isinstance(other, EmbPattern): + self.add_pattern(other) + elif isinstance(other, EmbThread) or isinstance(other, str): + self.add_thread(other) + for i in range(0, len(self.stitches)): + data = self.stitches[i][2] & COMMAND_MASK + if data == STITCH or data == SEW_TO or data == NEEDLE_AT: + self.color_change() + break # Only add color change if stitching exists. + elif isinstance(other, int): + self.add_command(other) + elif isinstance(other, list) or isinstance(other, tuple): # tuple or list + if len(other) == 0: + return + v = other[0] + if isinstance(v, list) or isinstance(v, tuple): # tuple or list of tuple or lists + for v in other: + x = v[0] + y = v[1] + try: + cmd = v[2] + except IndexError: + cmd = STITCH + self.add_stitch_absolute(cmd, x, y) + elif isinstance(v, complex): # tuple or list of complex + for v in other: + x = v.real + y = v.imag + self.add_stitch_absolute(STITCH, x, y) + elif isinstance(v, int) or isinstance(v, float): # tuple or list of numbers. + i = 0 + ie = len(other) + while i < ie: + self.add_stitch_absolute(STITCH, other[i], other[i + 1]) + i += 2 + elif isinstance(v, str): + self.extras[v] = other[1] + else: + raise ValueError() + return self + + def __add__(self, other): + p = self.copy() + p.add_pattern(other) + return p + + def __radd__(self, other): + p = other.copy() + p.add_pattern(self) + return p + def copy(self): emb_pattern = EmbPattern() emb_pattern.stitches = self.stitches[:] @@ -238,7 +330,7 @@ def transform(self, matrix): matrix.apply(stitch) def fix_color_count(self): - """Ensure the there are threads for all color blocks.""" + """Ensure that there are threads for all color blocks.""" thread_index = 0 init_color = True for stitch in self.stitches: @@ -321,6 +413,41 @@ def add_stitchblock(self, stitchblock): except AttributeError: self.add_stitch_absolute(stitch[2], stitch[0], stitch[1]) + def add_pattern(self, pattern): + """ + add_pattern merges the given pattern with the current pattern. It accounts for some edge conditions but + not all of them. + + If there is an end command on the current pattern, that is removed. + If the color ending the current pattern is equal to the color starting the next those color blocks are merged. + Any prepended thread change command to the merging pattern is suppressed. + + :param pattern: pattern to add to current pattern + :return: + """ + if self.stitches[-1][2] == END: + self.stitches = self.stitches[:-1] # Remove END, if exists + + # Add the new thread only if it's different from the last one + self.fix_color_count() + + if len(pattern.threadlist) > 0: + if pattern.threadlist[0] == self.threadlist[-1]: + self.threadlist.extend(pattern.threadlist[1:]) + else: + self.threadlist.extend(pattern.threadlist) + self.color_change() + join_position = len(self.stitches) + self.stitches.extend(pattern.stitches) + + for i in range(join_position, len(self.stitches)): + data = self.stitches[i][2] & COMMAND_MASK + if data == STITCH or data == SEW_TO or data == NEEDLE_AT: + break + elif data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET: + self.stitches[i][2] = NO_COMMAND + self.extras.update(pattern.extras) + def get_pattern_interpolate_trim(self, jumps_to_require_trim): """Gets a processed pattern with untrimmed jumps merged and trims added if merged jumps are beyond the given value. @@ -337,7 +464,7 @@ def get_pattern_interpolate_trim(self, jumps_to_require_trim): command = stitch[2] & COMMAND_MASK if command == STITCH or command == SEQUIN_EJECT: trimmed = False - elif command == COLOR_CHANGE or command == TRIM: + elif command == COLOR_CHANGE or command == NEEDLE_SET or command == TRIM: trimmed = True if trimmed or stitch[2] != JUMP: new_pattern.add_stitch_absolute(stitch[2], diff --git a/pyembroidery/EmbThread.py b/pyembroidery/EmbThread.py index 4624085..4d900be 100644 --- a/pyembroidery/EmbThread.py +++ b/pyembroidery/EmbThread.py @@ -55,7 +55,7 @@ def color_distance_red_mean( class EmbThread: - def __init__(self): + def __init__(self, thread=None): self.color = 0xFF000000 self.description = None # type: str self.catalog_number = None # type: str @@ -64,6 +64,39 @@ def __init__(self): self.chart = None # type: str self.weight = None # type: str # description, catalog_number, details, brand, chart, weight + if thread is not None: + self.set(thread) + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + if other is None: + return False + if isinstance(other, int): + return (0xFF000000 | self.color) == (0xFF000000 | other) + if isinstance(other, str): + return (0xFF000000 | self.color) == (0xFF000000 | EmbThread.parse_string_color(other)) + if not isinstance(other, EmbThread): + return False + if (0xFF000000 | self.color) != (0xFF000000 | other.color): + return False + if self.description != other.description: + return False + if self.catalog_number != other.description: + return False + if self.details != other.details: + return False + if self.brand != other.brand: + return False + if self.chart != other.chart: + return False + if self.weight != other.weight: + return False + return True + + def __hash__(self): + return self.color & 0xFFFFFF def set_color(self, r, g, b): self.color = color_rgb(r, g, b) diff --git a/pyembroidery/__init__.py b/pyembroidery/__init__.py index bb63f58..13ff650 100644 --- a/pyembroidery/__init__.py +++ b/pyembroidery/__init__.py @@ -6,6 +6,7 @@ from .EmbFunctions import * from .EmbPattern import EmbPattern from .EmbMatrix import EmbMatrix +from .EmbThread import EmbThread # items available in a sub-heirarchy (e.g. pyembroidery.PecGraphics.get_graphic_as_string) -from .PecGraphics import get_graphic_as_string +from .PecGraphics import get_graphic_as_string \ No newline at end of file From 714306c27bd25d274fe79f1316c0c3b278797117 Mon Sep 17 00:00:00 2001 From: tatarize Date: Sat, 13 Jul 2019 00:08:58 -0700 Subject: [PATCH 2/3] Tests for the overloaded functions --- test/test_overloads.py | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test/test_overloads.py diff --git a/test/test_overloads.py b/test/test_overloads.py new file mode 100644 index 0000000..91bae62 --- /dev/null +++ b/test/test_overloads.py @@ -0,0 +1,90 @@ +from __future__ import print_function + +import unittest + +from pattern_for_tests import * + + +class TestOverloads(unittest.TestCase): + + def position_equals(self, stitches, j, k): + self.assertEqual(stitches[j][:1], stitches[k][:1]) + + def test_pattern_equal(self): + shift0 = get_shift_pattern() + shift1 = get_shift_pattern() + self.assertIsNot(shift0, shift1) + self.assertEqual(shift0, shift1) + + def test_pattern_merge(self): + shift0 = get_shift_pattern() + shift1 = get_shift_pattern() + shift0.add_command(END) + last_pos = len(shift0) - 1 + self.assertEqual(shift0[last_pos][2], END) + shift0 += shift1 + self.assertNotEqual(shift0[last_pos][2], END) + + shift2 = get_shift_pattern() + shift3 = get_shift_pattern() + shift2 += shift3 + + self.assertEqual(shift0, shift2) + + shift4 = get_shift_pattern() + + self.assertEqual(shift4, shift3) + self.assertEqual(shift4, shift1) + shift1["name"] = "shifty" + + self.assertNotEqual(shift4, shift1) + + def test_pattern_merge_color(self): + p0 = EmbPattern() + p0 += "blue" + p0 += ((0, 0), (1, 1), (2, 2)) + p0 += "red" + p0 += ((4, 4)) + + p1 = EmbPattern() + p1 += "red" + p1 += ((0, 0), (1, 1), (2, 2)) * 10 + p1 += "yellow" + p1 += ((4, 4)) + + p0 += p1 + self.assertEqual(p0.count_color_changes(), 2) + + def test_pattern_merge_color2(self): + p0 = EmbPattern() + p0 += COLOR_BREAK + p0 += "blue" + p0 += ((0, 0), (1, 1), (2, 2)) + p0 += "red" + p0 += ((4, 4)) + + p1 = EmbPattern() + p1 += COLOR_BREAK + p1 += "red" + p1 += ((0, 0), (1, 1), (2, 2)) + p1 += "yellow" + p1 += ((4, 4)) + + p0 += p1 + self.assertEqual(p0.count_color_changes(), 2) + + def test_thread_equal(self): + t0 = EmbThread("red") + t1 = EmbThread("#F00") + self.assertEqual(t0, t1) + + def test_matrix(self): + m0 = EmbMatrix() + m1 = EmbMatrix() + m0.post_scale(2) + m1.post_rotate(30) + catted = m0.__matmul__(m1) # might run in 2.7 + m2 = EmbMatrix() + m2.post_scale(2) + m2.post_rotate(30) + self.assertEqual(catted, m2) From 17a5f299b8a7fcea433bdd9b2566c923ff13dcd4 Mon Sep 17 00:00:00 2001 From: tatarize Date: Sat, 13 Jul 2019 00:09:52 -0700 Subject: [PATCH 3/3] Minor format adjustments --- pyembroidery/InbReader.py | 2 -- pyembroidery/MitReader.py | 1 - pyembroidery/PcdReader.py | 2 +- pyembroidery/PcqReader.py | 2 +- pyembroidery/PcsReader.py | 2 +- pyembroidery/PesWriter.py | 4 ++-- pyembroidery/PhbReader.py | 2 +- pyembroidery/PhcReader.py | 2 +- pyembroidery/ShvReader.py | 5 +++-- pyembroidery/StxReader.py | 2 +- pyembroidery/TbfReader.py | 2 +- pyembroidery/ZxyReader.py | 2 +- 12 files changed, 13 insertions(+), 15 deletions(-) diff --git a/pyembroidery/InbReader.py b/pyembroidery/InbReader.py index 769201a..e1b0acd 100644 --- a/pyembroidery/InbReader.py +++ b/pyembroidery/InbReader.py @@ -1,5 +1,3 @@ - - def read_inb_stitches(f, out): count = 0 while True: diff --git a/pyembroidery/MitReader.py b/pyembroidery/MitReader.py index 12d563a..a787e62 100644 --- a/pyembroidery/MitReader.py +++ b/pyembroidery/MitReader.py @@ -1,4 +1,3 @@ - MIT_SIZE_CONVERSION_RATIO = 2.0 / 1.0 diff --git a/pyembroidery/PcdReader.py b/pyembroidery/PcdReader.py index 6335437..e28a897 100644 --- a/pyembroidery/PcdReader.py +++ b/pyembroidery/PcdReader.py @@ -1,5 +1,5 @@ -from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 from .EmbThread import EmbThread +from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 PC_SIZE_CONVERSION_RATIO = 5.0 / 3.0 diff --git a/pyembroidery/PcqReader.py b/pyembroidery/PcqReader.py index 6335437..e28a897 100644 --- a/pyembroidery/PcqReader.py +++ b/pyembroidery/PcqReader.py @@ -1,5 +1,5 @@ -from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 from .EmbThread import EmbThread +from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 PC_SIZE_CONVERSION_RATIO = 5.0 / 3.0 diff --git a/pyembroidery/PcsReader.py b/pyembroidery/PcsReader.py index 6335437..e28a897 100644 --- a/pyembroidery/PcsReader.py +++ b/pyembroidery/PcsReader.py @@ -1,5 +1,5 @@ -from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 from .EmbThread import EmbThread +from .ReadHelper import read_int_8, read_int_24be, read_int_24le, read_int_16le, signed24 PC_SIZE_CONVERSION_RATIO = 5.0 / 3.0 diff --git a/pyembroidery/PesWriter.py b/pyembroidery/PesWriter.py index 8f02a33..c979486 100644 --- a/pyembroidery/PesWriter.py +++ b/pyembroidery/PesWriter.py @@ -1,8 +1,8 @@ -from .PecWriter import write_pec +from .EmbConstant import * from .EmbThreadPec import get_thread_set +from .PecWriter import write_pec from .WriteHelper import write_string_utf8, write_int_32le, write_int_24le, write_int_16le, write_int_8, \ write_float_32le -from .EmbConstant import * SEQUIN_CONTINGENCY = CONTINGENCY_SEQUIN_JUMP FULL_JUMP = True diff --git a/pyembroidery/PhbReader.py b/pyembroidery/PhbReader.py index 461fa52..f9fd69e 100644 --- a/pyembroidery/PhbReader.py +++ b/pyembroidery/PhbReader.py @@ -1,5 +1,5 @@ -from .PecReader import read_pec_stitches from .EmbThreadPec import get_thread_set +from .PecReader import read_pec_stitches from .ReadHelper import read_int_8, read_int_32le, read_int_16le diff --git a/pyembroidery/PhcReader.py b/pyembroidery/PhcReader.py index 48acf5a..537a8b5 100644 --- a/pyembroidery/PhcReader.py +++ b/pyembroidery/PhcReader.py @@ -33,5 +33,5 @@ def read(f, out, settings=None): bytes_in_section2 = read_int_32le(f) # Sectional bounds. f.seek(bytes_in_section2 + 10, 1) color_count2 = read_int_8(f) - f.seek(color_count2 + 0x1D, 1) #1D toto back + f.seek(color_count2 + 0x1D, 1) # 1D toto back read_pec_stitches(f, out) diff --git a/pyembroidery/ShvReader.py b/pyembroidery/ShvReader.py index 7fc1e15..a69b457 100644 --- a/pyembroidery/ShvReader.py +++ b/pyembroidery/ShvReader.py @@ -1,8 +1,9 @@ import math + +from .EmbConstant import * +from .EmbThreadShv import get_thread_set from .ReadHelper import read_int_16be, read_int_32be, \ read_int_8, read_string_8, signed16, signed8 -from .EmbThreadShv import get_thread_set -from .EmbConstant import * def read(f, out, settings=None): diff --git a/pyembroidery/StxReader.py b/pyembroidery/StxReader.py index 18d6c98..7828d7d 100644 --- a/pyembroidery/StxReader.py +++ b/pyembroidery/StxReader.py @@ -1,5 +1,5 @@ -from .ReadHelper import read_int_32le from .ExpReader import read_exp_stitches +from .ReadHelper import read_int_32le def read(f, out, settings=None): diff --git a/pyembroidery/TbfReader.py b/pyembroidery/TbfReader.py index 15b802d..fda524b 100644 --- a/pyembroidery/TbfReader.py +++ b/pyembroidery/TbfReader.py @@ -1,5 +1,5 @@ -from .ReadHelper import signed8, read_int_8, read_int_24be from .EmbThread import EmbThread +from .ReadHelper import signed8, read_int_8, read_int_24be def read(f, out, settings=None): diff --git a/pyembroidery/ZxyReader.py b/pyembroidery/ZxyReader.py index 59b42d9..1b645db 100644 --- a/pyembroidery/ZxyReader.py +++ b/pyembroidery/ZxyReader.py @@ -1,4 +1,4 @@ -from .ReadHelper import signed8, read_int_32le, read_int_16be +from .ReadHelper import read_int_16be def read_zxy_stitches(f, out):