kopia lustrzana https://github.com/EmbroidePy/pyembroidery
426 wiersze
17 KiB
Python
426 wiersze
17 KiB
Python
def build_unique_palette(thread_palette, threadlist):
|
|
"""Turns a threadlist into a unique index list with the thread palette"""
|
|
chart = [None] * len(thread_palette) # Create a lookup chart.
|
|
for thread in set(
|
|
threadlist
|
|
): # for each unique color, move closest remaining thread to lookup chart.
|
|
index = thread.find_nearest_color_index(thread_palette)
|
|
if index is None:
|
|
break # No more threads remain in palette
|
|
thread_palette[index] = None # entries may not be reused.
|
|
chart[index] = thread # assign the given index to the lookup.
|
|
|
|
palette = []
|
|
for thread in threadlist: # for each thread, return the index.
|
|
palette.append(thread.find_nearest_color_index(chart))
|
|
return palette
|
|
|
|
|
|
def build_palette(thread_palette, threadlist):
|
|
palette = []
|
|
for thread in threadlist: # for each thread, return the index.
|
|
palette.append(thread.find_nearest_color_index(thread_palette))
|
|
return palette
|
|
|
|
|
|
def build_nonrepeat_palette(thread_palette, threadlist):
|
|
last_index = None
|
|
last_thread = None
|
|
palette = []
|
|
for thread in threadlist: # for each thread, return the index.
|
|
index = thread.find_nearest_color_index(thread_palette)
|
|
if last_index == index and last_thread != thread:
|
|
repeated_thread = thread_palette[index]
|
|
repeated_index = index
|
|
thread_palette[index] = None
|
|
index = thread.find_nearest_color_index(thread_palette)
|
|
# index will no longer be repeated.
|
|
thread_palette[repeated_index] = repeated_thread
|
|
palette.append(index)
|
|
last_index = index
|
|
last_thread = thread
|
|
|
|
return palette
|
|
|
|
|
|
def find_nearest_color_index(find_color, values):
|
|
if isinstance(find_color, EmbThread):
|
|
find_color = find_color.color
|
|
red = (find_color >> 16) & 0xFF
|
|
green = (find_color >> 8) & 0xFF
|
|
blue = find_color & 0xFF
|
|
closest_index = None
|
|
current_closest_value = float("inf")
|
|
for current_index, t in enumerate(values):
|
|
if t is None:
|
|
continue
|
|
dist = color_distance_red_mean(
|
|
red, green, blue, t.get_red(), t.get_green(), t.get_blue()
|
|
)
|
|
if dist <= current_closest_value: # <= choose second if they tie.
|
|
current_closest_value = dist
|
|
closest_index = current_index
|
|
return closest_index
|
|
|
|
|
|
def color_rgb(r, g, b):
|
|
return int(((r & 255) << 16) | ((g & 255) << 8) | (b & 255))
|
|
|
|
|
|
def color_hex(hex_string):
|
|
h = hex_string.lstrip("#")
|
|
size = len(h)
|
|
if size == 6 or size == 8:
|
|
return int(h[:6], 16)
|
|
elif size == 4 or size == 3:
|
|
return int(h[0] + h[0] + h[1] + h[1] + h[2] + h[2], 16)
|
|
|
|
|
|
def color_distance_red_mean(r1, g1, b1, r2, g2, b2):
|
|
red_mean = int(round((r1 + r2) / 2))
|
|
r = int(r1 - r2)
|
|
g = int(g1 - g2)
|
|
b = int(b1 - b2)
|
|
return (
|
|
(((512 + red_mean) * r * r) >> 8)
|
|
+ 4 * g * g
|
|
+ (((767 - red_mean) * b * b) >> 8)
|
|
)
|
|
# See the very good color distance paper:
|
|
# https://www.compuphase.com/cmetric.htm
|
|
|
|
|
|
class EmbThread:
|
|
def __init__(
|
|
self,
|
|
thread=None,
|
|
description=None,
|
|
catalog_number=None,
|
|
details=None,
|
|
brand=None,
|
|
chart=None,
|
|
weight=None,
|
|
):
|
|
self.color = 0x000000
|
|
self.description = description # type: str
|
|
self.catalog_number = catalog_number # type: str
|
|
self.details = details # type: str
|
|
self.brand = brand # type: str
|
|
self.chart = chart # type: str
|
|
self.weight = weight # type: str
|
|
# description, catalog_number, details, brand, chart, weight
|
|
if thread is not None:
|
|
self.set(thread)
|
|
|
|
def __repr__(self):
|
|
parts = list()
|
|
parts.append("thread='%s'" % self.hex_color())
|
|
if self.description is not None:
|
|
parts.append("description='%s'" % self.description)
|
|
if self.catalog_number is not None:
|
|
parts.append("catalog_number='%s'" % self.catalog_number)
|
|
if self.details is not None:
|
|
parts.append("details='%s'" % self.details)
|
|
if self.brand is not None:
|
|
parts.append("brand='%s'" % self.brand)
|
|
if self.chart is not None:
|
|
parts.append("chart='%s'" % self.chart)
|
|
if self.weight is not None:
|
|
parts.append("weight='%s'" % self.weight)
|
|
return "EmbThread(%s)" % ", ".join(parts)
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def __eq__(self, other):
|
|
if other is None:
|
|
return False
|
|
if isinstance(other, int):
|
|
return self.color & 0xFFFFFF == other & 0xFFFFFF
|
|
try:
|
|
if isinstance(other, basestring):
|
|
return (
|
|
self.color & 0xFFFFFF
|
|
== EmbThread.parse_string_color(other) & 0xFFFFFF
|
|
)
|
|
except NameError:
|
|
if isinstance(other, str):
|
|
return (
|
|
self.color & 0xFFFFFF
|
|
== EmbThread.parse_string_color(other) & 0xFFFFFF
|
|
)
|
|
if not isinstance(other, EmbThread):
|
|
return False
|
|
if self.color & 0xFFFFFF != other.color & 0xFFFFFF:
|
|
return False
|
|
if self.description != other.description:
|
|
return False
|
|
if self.catalog_number != other.catalog_number:
|
|
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 __str__(self):
|
|
if self.description is None:
|
|
return "EmbThread %s" % self.hex_color()
|
|
else:
|
|
return "EmbThread %s %s" % (self.description, self.hex_color())
|
|
|
|
def set_color(self, r, g, b):
|
|
self.color = color_rgb(r, g, b)
|
|
|
|
def get_opaque_color(self):
|
|
return 0xFF000000 | self.color
|
|
|
|
def get_red(self):
|
|
red = self.color >> 16
|
|
return red & 0xFF
|
|
|
|
def get_green(self):
|
|
green = self.color >> 8
|
|
return green & 0xFF
|
|
|
|
def get_blue(self):
|
|
blue = self.color
|
|
return blue & 0xFF
|
|
|
|
def find_nearest_color_index(self, values):
|
|
return find_nearest_color_index(int(self.color), values)
|
|
|
|
def hex_color(self):
|
|
return "#%02x%02x%02x" % (self.get_red(), self.get_green(), self.get_blue())
|
|
|
|
def set_hex_color(self, hex_string):
|
|
self.color = color_hex(hex_string)
|
|
|
|
def set(self, thread):
|
|
if isinstance(thread, EmbThread):
|
|
self.color = thread.color
|
|
self.description = thread.description
|
|
self.catalog_number = thread.catalog_number
|
|
self.details = thread.details
|
|
self.brand = thread.brand
|
|
self.chart = thread.chart
|
|
self.weight = thread.weight
|
|
elif isinstance(thread, int):
|
|
self.color = thread
|
|
elif isinstance(thread, dict):
|
|
if "name" in thread:
|
|
self.description = thread["name"]
|
|
if "description" in thread:
|
|
self.description = thread["description"]
|
|
if "desc" in thread:
|
|
self.description = thread["desc"]
|
|
if "brand" in thread:
|
|
self.brand = thread["brand"]
|
|
if "manufacturer" in thread:
|
|
self.brand = thread["manufacturer"]
|
|
if "color" in thread or "rgb" in thread:
|
|
try:
|
|
color = thread["color"]
|
|
except KeyError:
|
|
color = thread["rgb"]
|
|
if isinstance(color, int):
|
|
self.color = color
|
|
elif isinstance(color, tuple) or isinstance(color, list):
|
|
self.color = (
|
|
(color[0] & 0xFF) << 16
|
|
| (color[1] & 0xFF) << 8
|
|
| (color[2] & 0xFF)
|
|
)
|
|
else:
|
|
try:
|
|
if isinstance(color, basestring):
|
|
self.color = self.parse_string_color(color)
|
|
except NameError:
|
|
if isinstance(color, str):
|
|
self.color = self.parse_string_color(color)
|
|
if "hex" in thread:
|
|
self.set_hex_color(thread["hex"])
|
|
if "id" in thread:
|
|
self.catalog_number = thread["id"]
|
|
if "catalog" in thread:
|
|
self.catalog_number = thread["catalog"]
|
|
else:
|
|
try:
|
|
if isinstance(thread, basestring):
|
|
self.color = self.parse_string_color(thread)
|
|
except NameError:
|
|
if isinstance(thread, str):
|
|
self.color = self.parse_string_color(thread)
|
|
try: # We might be using python 3.
|
|
if isinstance(thread, long):
|
|
self.color = int(thread)
|
|
except NameError:
|
|
pass
|
|
|
|
@staticmethod
|
|
def parse_string_color(color):
|
|
if color == "random":
|
|
import random
|
|
|
|
return random.randint(0, 0xFFFFFF)
|
|
if color[0:1] == "#":
|
|
return color_hex(color[1:])
|
|
color_dict = {
|
|
"aliceblue": color_rgb(240, 248, 255),
|
|
"antiquewhite": color_rgb(250, 235, 215),
|
|
"aqua": color_rgb(0, 255, 255),
|
|
"aquamarine": color_rgb(127, 255, 212),
|
|
"azure": color_rgb(240, 255, 255),
|
|
"beige": color_rgb(245, 245, 220),
|
|
"bisque": color_rgb(255, 228, 196),
|
|
"black": color_rgb(0, 0, 0),
|
|
"blanchedalmond": color_rgb(255, 235, 205),
|
|
"blue": color_rgb(0, 0, 255),
|
|
"blueviolet": color_rgb(138, 43, 226),
|
|
"brown": color_rgb(165, 42, 42),
|
|
"burlywood": color_rgb(222, 184, 135),
|
|
"cadetblue": color_rgb(95, 158, 160),
|
|
"chartreuse": color_rgb(127, 255, 0),
|
|
"chocolate": color_rgb(210, 105, 30),
|
|
"coral": color_rgb(255, 127, 80),
|
|
"cornflowerblue": color_rgb(100, 149, 237),
|
|
"cornsilk": color_rgb(255, 248, 220),
|
|
"crimson": color_rgb(220, 20, 60),
|
|
"cyan": color_rgb(0, 255, 255),
|
|
"darkblue": color_rgb(0, 0, 139),
|
|
"darkcyan": color_rgb(0, 139, 139),
|
|
"darkgoldenrod": color_rgb(184, 134, 11),
|
|
"darkgray": color_rgb(169, 169, 169),
|
|
"darkgreen": color_rgb(0, 100, 0),
|
|
"darkgrey": color_rgb(169, 169, 169),
|
|
"darkkhaki": color_rgb(189, 183, 107),
|
|
"darkmagenta": color_rgb(139, 0, 139),
|
|
"darkolivegreen": color_rgb(85, 107, 47),
|
|
"darkorange": color_rgb(255, 140, 0),
|
|
"darkorchid": color_rgb(153, 50, 204),
|
|
"darkred": color_rgb(139, 0, 0),
|
|
"darksalmon": color_rgb(233, 150, 122),
|
|
"darkseagreen": color_rgb(143, 188, 143),
|
|
"darkslateblue": color_rgb(72, 61, 139),
|
|
"darkslategray": color_rgb(47, 79, 79),
|
|
"darkslategrey": color_rgb(47, 79, 79),
|
|
"darkturquoise": color_rgb(0, 206, 209),
|
|
"darkviolet": color_rgb(148, 0, 211),
|
|
"deeppink": color_rgb(255, 20, 147),
|
|
"deepskyblue": color_rgb(0, 191, 255),
|
|
"dimgray": color_rgb(105, 105, 105),
|
|
"dimgrey": color_rgb(105, 105, 105),
|
|
"dodgerblue": color_rgb(30, 144, 255),
|
|
"firebrick": color_rgb(178, 34, 34),
|
|
"floralwhite": color_rgb(255, 250, 240),
|
|
"forestgreen": color_rgb(34, 139, 34),
|
|
"fuchsia": color_rgb(255, 0, 255),
|
|
"gainsboro": color_rgb(220, 220, 220),
|
|
"ghostwhite": color_rgb(248, 248, 255),
|
|
"gold": color_rgb(255, 215, 0),
|
|
"goldenrod": color_rgb(218, 165, 32),
|
|
"gray": color_rgb(128, 128, 128),
|
|
"grey": color_rgb(128, 128, 128),
|
|
"green": color_rgb(0, 128, 0),
|
|
"greenyellow": color_rgb(173, 255, 47),
|
|
"honeydew": color_rgb(240, 255, 240),
|
|
"hotpink": color_rgb(255, 105, 180),
|
|
"indianred": color_rgb(205, 92, 92),
|
|
"indigo": color_rgb(75, 0, 130),
|
|
"ivory": color_rgb(255, 255, 240),
|
|
"khaki": color_rgb(240, 230, 140),
|
|
"lavender": color_rgb(230, 230, 250),
|
|
"lavenderblush": color_rgb(255, 240, 245),
|
|
"lawngreen": color_rgb(124, 252, 0),
|
|
"lemonchiffon": color_rgb(255, 250, 205),
|
|
"lightblue": color_rgb(173, 216, 230),
|
|
"lightcoral": color_rgb(240, 128, 128),
|
|
"lightcyan": color_rgb(224, 255, 255),
|
|
"lightgoldenrodyellow": color_rgb(250, 250, 210),
|
|
"lightgray": color_rgb(211, 211, 211),
|
|
"lightgreen": color_rgb(144, 238, 144),
|
|
"lightgrey": color_rgb(211, 211, 211),
|
|
"lightpink": color_rgb(255, 182, 193),
|
|
"lightsalmon": color_rgb(255, 160, 122),
|
|
"lightseagreen": color_rgb(32, 178, 170),
|
|
"lightskyblue": color_rgb(135, 206, 250),
|
|
"lightslategray": color_rgb(119, 136, 153),
|
|
"lightslategrey": color_rgb(119, 136, 153),
|
|
"lightsteelblue": color_rgb(176, 196, 222),
|
|
"lightyellow": color_rgb(255, 255, 224),
|
|
"lime": color_rgb(0, 255, 0),
|
|
"limegreen": color_rgb(50, 205, 50),
|
|
"linen": color_rgb(250, 240, 230),
|
|
"magenta": color_rgb(255, 0, 255),
|
|
"maroon": color_rgb(128, 0, 0),
|
|
"mediumaquamarine": color_rgb(102, 205, 170),
|
|
"mediumblue": color_rgb(0, 0, 205),
|
|
"mediumorchid": color_rgb(186, 85, 211),
|
|
"mediumpurple": color_rgb(147, 112, 219),
|
|
"mediumseagreen": color_rgb(60, 179, 113),
|
|
"mediumslateblue": color_rgb(123, 104, 238),
|
|
"mediumspringgreen": color_rgb(0, 250, 154),
|
|
"mediumturquoise": color_rgb(72, 209, 204),
|
|
"mediumvioletred": color_rgb(199, 21, 133),
|
|
"midnightblue": color_rgb(25, 25, 112),
|
|
"mintcream": color_rgb(245, 255, 250),
|
|
"mistyrose": color_rgb(255, 228, 225),
|
|
"moccasin": color_rgb(255, 228, 181),
|
|
"navajowhite": color_rgb(255, 222, 173),
|
|
"navy": color_rgb(0, 0, 128),
|
|
"oldlace": color_rgb(253, 245, 230),
|
|
"olive": color_rgb(128, 128, 0),
|
|
"olivedrab": color_rgb(107, 142, 35),
|
|
"orange": color_rgb(255, 165, 0),
|
|
"orangered": color_rgb(255, 69, 0),
|
|
"orchid": color_rgb(218, 112, 214),
|
|
"palegoldenrod": color_rgb(238, 232, 170),
|
|
"palegreen": color_rgb(152, 251, 152),
|
|
"paleturquoise": color_rgb(175, 238, 238),
|
|
"palevioletred": color_rgb(219, 112, 147),
|
|
"papayawhip": color_rgb(255, 239, 213),
|
|
"peachpuff": color_rgb(255, 218, 185),
|
|
"peru": color_rgb(205, 133, 63),
|
|
"pink": color_rgb(255, 192, 203),
|
|
"plum": color_rgb(221, 160, 221),
|
|
"powderblue": color_rgb(176, 224, 230),
|
|
"purple": color_rgb(128, 0, 128),
|
|
"red": color_rgb(255, 0, 0),
|
|
"rosybrown": color_rgb(188, 143, 143),
|
|
"royalblue": color_rgb(65, 105, 225),
|
|
"saddlebrown": color_rgb(139, 69, 19),
|
|
"salmon": color_rgb(250, 128, 114),
|
|
"sandybrown": color_rgb(244, 164, 96),
|
|
"seagreen": color_rgb(46, 139, 87),
|
|
"seashell": color_rgb(255, 245, 238),
|
|
"sienna": color_rgb(160, 82, 45),
|
|
"silver": color_rgb(192, 192, 192),
|
|
"skyblue": color_rgb(135, 206, 235),
|
|
"slateblue": color_rgb(106, 90, 205),
|
|
"slategray": color_rgb(112, 128, 144),
|
|
"slategrey": color_rgb(112, 128, 144),
|
|
"snow": color_rgb(255, 250, 250),
|
|
"springgreen": color_rgb(0, 255, 127),
|
|
"steelblue": color_rgb(70, 130, 180),
|
|
"tan": color_rgb(210, 180, 140),
|
|
"teal": color_rgb(0, 128, 128),
|
|
"thistle": color_rgb(216, 191, 216),
|
|
"tomato": color_rgb(255, 99, 71),
|
|
"turquoise": color_rgb(64, 224, 208),
|
|
"violet": color_rgb(238, 130, 238),
|
|
"wheat": color_rgb(245, 222, 179),
|
|
"white": color_rgb(255, 255, 255),
|
|
"whitesmoke": color_rgb(245, 245, 245),
|
|
"yellow": color_rgb(255, 255, 0),
|
|
"yellowgreen": color_rgb(154, 205, 50),
|
|
}
|
|
return color_dict.get(color.lower(), 0x000000)
|
|
# return color or black.
|