From 378f079e6d15757ed6cb0b2e04f50642b5adaaac Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 15:49:58 -0400 Subject: [PATCH] add ThreadCatalog and palette matching --- embroider_print.py | 2 + inkstitch/threads/__init__.py | 1 + inkstitch/threads/catalog.py | 72 +++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 inkstitch/threads/catalog.py diff --git a/embroider_print.py b/embroider_print.py index cbdaeb09d..7ab24876c 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -21,6 +21,7 @@ from inkstitch.extensions import InkstitchExtension from inkstitch.stitch_plan import patches_to_stitch_plan from inkstitch.svg import render_stitch_plan from inkstitch.utils import save_stderr, restore_stderr +from inkstitch.threads import ThreadCatalog from jinja2 import Environment, FileSystemLoader, select_autoescape from datetime import date @@ -290,6 +291,7 @@ class Print(InkstitchExtension): patches = self.elements_to_patches(self.elements) stitch_plan = patches_to_stitch_plan(patches) + ThreadCatalog().match_and_apply_palette(stitch_plan) render_stitch_plan(self.document.getroot(), stitch_plan) self.strip_namespaces() diff --git a/inkstitch/threads/__init__.py b/inkstitch/threads/__init__.py index fec82cede..03cd777b6 100644 --- a/inkstitch/threads/__init__.py +++ b/inkstitch/threads/__init__.py @@ -1,2 +1,3 @@ from color import ThreadColor from palette import ThreadPalette +from catalog import ThreadCatalog diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py new file mode 100644 index 000000000..0f959a522 --- /dev/null +++ b/inkstitch/threads/catalog.py @@ -0,0 +1,72 @@ +import os +from os.path import dirname, realpath +import sys +from glob import glob +from collections import Sequence +from .palette import ThreadPalette + +class _ThreadCatalog(Sequence): + """Holds a set of ThreadPalettes.""" + + def __init__(self): + self.palettes = [] + self.load_palettes(self.get_palettes_path()) + + def get_palettes_path(self): + if getattr(sys, 'frozen', None) is not None: + path = sys._MEIPASS + else: + path = dirname(dirname(dirname(realpath(__file__)))) + + return os.path.join(path, 'palettes') + + def load_palettes(self, path): + for palette_file in glob(os.path.join(path, '*.gpl')): + self.palettes.append(ThreadPalette(palette_file)) + + def __getitem__(self, item): + return self.palettes[item] + + def __len__(self): + return len(self.palettes) + + def match_and_apply_palette(self, stitch_plan): + """Figure out which color palette was used and set thread names. + + Scans the catalog of color palettes and chooses one that seems most + likely to be the one that the user used. A palette will only be + chosen if more tha 80% of the thread colors in the stitch plan are + exact matches for threads in the palette. All other threads will be + matched to the closest thread in the palette. + """ + + threads = [color_block.color for color_block in stitch_plan] + + for palette in self: + num_matched = sum(1 for thread in threads if thread in palette) + if num_matched > (0.8 * len(threads)): + # Pick this one. + break + else: + # This block will only get run if we ran off the end of the loop + # without breaking. No palette had enough colors that exactly + # matched, so just do nothing. + return + + for thread in threads: + nearest = palette.find_nearest(thread) + + thread.name = nearest.name + thread.number = nearest.number + thread.manufacturer = nearest.manufacturer + +_catalog = None + +def ThreadCatalog(): + """Singleton _ThreadCatalog factory""" + + global _catalog + if _catalog is None: + _catalog = _ThreadCatalog() + + return _catalog