def assert_round_trip(self, color, space): """Cycle through all the other colors and convert to them and back and check the results.""" c1 = Color(color).convert(space) for space in SPACES: # Print the color space to easily identify which color space broke. c2 = c1.convert(space) c2.convert(c1.space(), in_place=True) # Catch cases where we are really close to 360 which should wrap to 0 for c in (c1, c2): if isinstance(c._space, Cylindrical): if util.round_half_up(util.no_nan(c.hue), c.PRECISION) == 360: c.hue = 0 # Run rounded string back through parsing in case we hit something like a hue that needs normalization. str1 = Color(c1.to_string(color=True, fit=False)).to_string(color=True, fit=False) str2 = Color(c2.to_string(color=True, fit=False)).to_string(color=True, fit=False) # Print failing results for debug purposes if str1 != str2: print('----- Convert: {} <=> {} -----'.format( c1.space(), space)) print('Original: ', color.to_string(color=True, fit=False)) print(c1.space() + ': ', str1, c1.coords()) print(space + ': ', str2, c2.coords()) assert str1 == str2
def cie_diagram(mode="1931", observer="2deg", colorize=True, opacity=1, rgb_spaces=None, white_points=None, theme='light', title='', show_labels=True, axis=True, show_legend=True, black_body=False): """CIE diagram.""" opt = DiagramOptions(theme=theme, mode=mode, observer=observer, title=title) figure = plt.figure() ax = plt.axes(xlabel=opt.axis_labels[0], ylabel=opt.axis_labels[1]) ax.set_aspect('equal') if axis is False: plt.axis('off') figure.add_axes(ax) plt.title(opt.title) if show_labels: plt.margins(0.15) xs = [] ys = [] annotations = [] # Get points for the spectral locus for k, v in opt.observer.items(): # Get the XYZ values in the correct format if opt.mode == "1931": x, y = util.xyz_to_xyY(v, (0.31270, 0.32900))[:2] xs.append(x) ys.append(y) elif opt.mode == "1976": x, y = util.xyz_to_uv(v) xs.append(x) ys.append(y) else: x, y = util.xy_to_uv_1960( util.xyz_to_xyY(v, (0.31270, 0.32900))[:2]) xs.append(x) ys.append(y) # Prepare annotation labels for all points on the spectral locus. if k in labels: annotations.append((k, (x, y))) xs, ys = get_spline(xs, ys, len(xs) * 3) # Draw the bottom purple line xs.append(xs[0]) ys.append(ys[0]) # Generate fill colors for inside the spectral locus if colorize: px = [] py = [] c = [] path = mpltpath.Path(list(zip(xs, ys))) for r in itertools.product( (x / RESOLUTION for x in range(0, RESOLUTION + 1)), (x / RESOLUTION for x in range(0, RESOLUTION + 1))): srgb = Color('srgb', []) if path.contains_point(r): if opt.mode == "1931": xyz = util.xy_to_xyz(r) elif opt.mode == "1976": xyz = util.xy_to_xyz(util.uv_to_xy(r)) else: xyz = util.xy_to_xyz(util.uv_1960_to_xy(r)) px.append(r[0]) py.append(r[1]) srgb.update('xyz-d65', xyz, opacity) m = max(srgb.coords()) srgb.update('srgb', [(i / m if m != 0 else 0) for i in srgb.coords()], srgb.alpha) c.append(srgb.to_string(hex=True, fit="clip")) plt.scatter(px, py, edgecolors=None, c=c, s=1) # Plot spectral locus and label it plt.plot(xs, ys, color=opt.locus_line_color if colorize else opt.default_color, marker="", linewidth=1.5, markersize=2, antialiased=True) if show_labels: # Label points ax = [] ay = [] for annotate in annotations: offset = opt.locus_labels(annotate[0]) ax.append(annotate[1][0]) ay.append(annotate[1][1]) plt.annotate('{:d}'.format(annotate[0]), annotate[1], size=8, color=opt.locus_label_color, textcoords="offset points", xytext=offset, ha='center') plt.scatter( ax, ay, marker=".", color=opt.locus_point_color if not colorize else opt.default_color, zorder=100) # Add any specified white points. if white_points: wx = [] wy = [] annot = [] for wp in white_points: w = WHITES[wp] annot.append(wp) if opt.mode == '1931': wx.append(w[0]) wy.append(w[1]) elif opt.mode == '1976': uv = util.xyz_to_uv(util.xy_to_xyz(w)) wx.append(uv[0]) wy.append(uv[1]) else: uv = util.xy_to_uv_1960(w) wx.append(uv[0]) wy.append(uv[1]) plt.scatter(wx, wy, marker=".", color=opt.default_colorized_color if colorize else opt.default_color) for pt, a in zip(zip(wx, wy), annot): plt.annotate(a, pt, size=8, color=opt.default_colorized_color if colorize else opt.default_color, textcoords="offset points", xytext=(15, -3), ha='center') # Show the black body (Planckian locus) curve # Work in progress. if black_body and opt.mode != '1976': uaxis = [] vaxis = [] bres = 20 boffset = 1000 brange = 24000 for cct in range(0, bres + 1): t = cct / bres * brange + boffset bu, bv = black_body_curve(t, opt.mode) uaxis.append(bu) vaxis.append(bv) uaxis, vaxis = get_spline(uaxis, vaxis, len(uaxis) * 4) plt.plot(uaxis, vaxis, color=opt.default_colorized_color if colorize else opt.default_color, marker="", linewidth=1, markersize=0, antialiased=True) # `plt.scatter(uaxis, vaxis, c=opt.default_color)` # Draw RGB triangles if one is specified if rgb_spaces: temp = Color('srgb', []) for space, color in rgb_spaces: if opt.mode == '1931': red = temp.mutate(space, [1, 0, 0]).xy() green = temp.mutate(space, [0, 1, 0]).xy() blue = temp.mutate(space, [0, 0, 1]).xy() elif opt.mode == '1976': red = temp.mutate(space, [1, 0, 0]).uv() green = temp.mutate(space, [0, 1, 0]).uv() blue = temp.mutate(space, [0, 0, 1]).uv() else: red = temp.mutate(space, [1, 0, 0]).uv('1960') green = temp.mutate(space, [0, 1, 0]).uv('1960') blue = temp.mutate(space, [0, 0, 1]).uv('1960') plt.plot([red[0], green[0], blue[0], red[0]], [red[1], green[1], blue[1], red[1]], marker='o', color=color, label=space, linewidth=2, markersize=0, path_effects=[ path_effects.SimpleLineShadow(alpha=0.2, offset=(1, -1)), path_effects.Normal() ]) # We current only add labels when drawing RGB triangles if rgb_spaces and show_legend: ax.legend()