I'm trying to determine the intersection of the sRGB "cube" in the xyY colorspace with a plane at a specific $Y$-value.
When transformed to the xyY colorspace the RGB cube changes its shape:
import colour
from colour import xyY_to_XYZ, xy_to_xyY
from colour.adaptation import matrix_chromatic_adaptation_VonKries
from colour.plotting import plot_RGB_colourspace_section
from colour.plotting import plot_RGB_colourspaces_gamuts
from IPython.display import display, IFrame, HTML, Latex, Markdown
import matplotlib.pyplot as plt
import sympy
from sympy import Matrix, Eq
from sympy import Max, Min, cancel, together, solve
from sympy import Piecewise, oo
from sympy import MatrixSymbol
from sympy.printing import latex as sympy_latex
from sympy.codegen.ast import Assignment
with colour.utilities.suppress_warnings(colour_usage_warnings=True):
plot_RGB_colourspaces_gamuts('sRGB', 'CIE xyY',segments=16)
Better Visualization on Wikipedia: https://en.wikipedia.org/wiki/File:SRGB_gamut_within_CIExyY_color_space_mesh.webm
I want to analytically calculate the shape of the section for a specific $Y$ value. The colour
module can do it, but it uses the trimesh
library to calculate the shape from a mesh. I want to calculate the points using math.
with colour.utilities.suppress_warnings(colour_usage_warnings=True):
plot_RGB_colourspace_section('sRGB', 'CIE xyY', Y = 0.3)
display(IFrame('./tool.html', "100%", "900px"))
for my fresh Miniconda3 Install:
conda create -n sRGB_Y_section_in_xyY_space python=3.9 numpy sympy networkx trimesh
activate sRGB_Y_section_in_xyY_space
conda install -c conda-forge colour-science trimesh
conda install -n sRGB_Y_section_in_xyY_space ipykernel --update-deps
We determine the $rgb$ to $xyY$ transformation using sympy
First we transform $rgb$ to $XYZ$
sRGB = colour.models.rgb.RGB_COLOURSPACE_sRGB
r, g, b = sympy.symbols('r g b', real=True, positive=True)
x, y, Y = sympy.symbols('x y Y', real=True, positive=True)
v_rgb = Matrix([[r], [g], [b]])
# From colour.sRGB_to_XYZ
matrix_RGB_to_XYZ = Matrix(sRGB.matrix_RGB_to_XYZ)
M_CAT = matrix_chromatic_adaptation_VonKries(
xyY_to_XYZ(xy_to_xyY(sRGB.whitepoint)),
xyY_to_XYZ(xy_to_xyY(sRGB.whitepoint)),
transform="CAT02",
)
M_CAT = Matrix(M_CAT)
v_xyz = matrix_RGB_to_XYZ * v_rgb
v_xyz = M_CAT * v_xyz
display(Latex(r"""$$
\begin{align*}
\begin{pmatrix}X\\Y\\Z\end{pmatrix} &= M_{CAT} \cdot M_{RGB\rightarrow XYZ} \cdot \begin{pmatrix}r\\g\\b\end{pmatrix}\\""" +
f"&= {sympy_latex(M_CAT)} \cdot {sympy_latex(matrix_RGB_to_XYZ)}" + r"\cdot \begin{pmatrix}r\\g\\b\end{pmatrix} \\"
f"&= {sympy_latex(M_CAT * matrix_RGB_to_XYZ)}" + r"\cdot \begin{pmatrix}r\\g\\b\end{pmatrix} \\"
f"&= {sympy_latex(v_xyz)}" +
r"""\end{align*}$$
"""))
Then we transform $XYZ$ to $xy$ for fixed $Y$
$$ \begin{pmatrix}x\\y\\Y\end{pmatrix} = \begin{pmatrix}\frac{X}{X + Y +Z}\\\frac{Y}{X + Y + Z}\\Y\end{pmatrix} $$v_xyY = Matrix([
[v_xyz[0] / (v_xyz[0] + v_xyz[1] + v_xyz[2])],
[Y / (v_xyz[0] + v_xyz[1] + v_xyz[2])],
[Y]
])
#v_xyY = v_xyY.subs(v_xyz[1], Y) #Maybe replace the Y value?
#We only care about xy, because Y will be given
v_xy = v_xyY[0:2,0]
Eq(Matrix([[x],[y]]),v_xy)
Eq(Y,v_xyz[1])
We calculate the intersections between all 6 sides of the $RGB$-Cube ($r=0,r=1,g=0,g=1,b=0,b=1$). Then we use the previous $Y$ equation to replace one of the remaining variables.
Because I don't know any better I will try all combinations of sides and variables:
variations = [
((b,0), g, r), #Read as: set b=0, solve for g, remaining variable is r
((b,0), r, g),
((b,1), g, r),
((b,1), r, g),
((r,0), g, b),
((r,0), b, g),
((r,1), g, b),
((r,1), b, g),
((g,0), r, b),
((g,0), b, r),
((g,1), r, b),
((g,1), b, r),
]
def show_step(doshow, step):
if doshow:
display(step())
results = {}
for i, (side_sub, solve_for, unsolved_for) in enumerate(variations):
show_steps = i == 0
show_step(show_steps, lambda: Latex(r'Starting with $$\begin{pmatrix}x\\y\end{pmatrix} = ' + f'{sympy_latex(v_xy)} \\text{{ with }} {sympy_latex(Eq(v_xyz[1],Y))}$$'))
v_xy_side = v_xy.subs([side_sub])
solve_eq = Eq(v_xyz[1],Y).subs([side_sub])
show_step(show_steps, lambda: Latex(f'Setting ${side_sub[0]}={side_sub[1]}$: $${sympy_latex(v_xy_side)} \\text{{ with }} {sympy_latex(solve_eq)}$$'))
solved_for_var = solve(solve_eq, solve_for)
if (solved_for_var):
show_step(show_steps, lambda: Latex(f'Solving ${sympy_latex(solve_eq)}$ for ${solve_for}$: $${sympy_latex(Eq(solve_for,solved_for_var[0]))} \\text{{ with }} g,r\\in [0,1]$$'))
f = v_xy_side.subs(solve_for, solved_for_var[0])
show_step(show_steps, lambda: Latex(f"""Inserting ${solve_for}$ into the $(x,y)$ yields:
$$\\begin{{pmatrix}}x\\\\y\\end{{pmatrix}} = {sympy_latex(f)} $$
"""))
f0 = solve(Eq(solved_for_var[0], 0), unsolved_for)[0]
f1 = solve(Eq(solved_for_var[0], 1), unsolved_for)[0]
min_other_var = together(cancel(Min(f0, f1)))
max_other_var = together(cancel(Max(f0, f1)))
show_step(show_steps, lambda: Latex(f"""To find the start and end of the intersection line, we solve for ${solve_for}=0$ and ${solve_for}=1$:
$$\\begin{{align*}}
{solve_for}=0 &\implies {unsolved_for} = {f0} \\\\
{solve_for}=1 &\implies {unsolved_for} = {f1} \\\\
&\implies {unsolved_for} \in C = [{sympy_latex(min_other_var)},{sympy_latex(max_other_var)}]
\\end{{align*}}$$
"""))
# Solution is only valid if other var is between 0 and 1
condition_big = (min_other_var <= 1) & (max_other_var >= 0)
condition = sympy.simplify(condition_big)
show_step(show_steps, lambda: Latex(f"""If $C$ does not overlap with $[0,1]$ there is no intersection and no point can be calculated.
$C$ overlaps with $[0,1]$ and an intersection can be calculted if
$$\\begin{{align*}}
&({sympy_latex(min_other_var)} <= 1) \\land ({sympy_latex(max_other_var)} >= 0) \\\\
\implies&{sympy_latex(condition_big)} \\\\
\implies& {sympy_latex(condition)} \\\\
\\end{{align*}}$$
"""))
# Alternatively one could use a Piecewise here and fold it later
clamp0 = Max(0, min_other_var)
clamp1 = Min(1, max_other_var)
show_step(show_steps, lambda: Latex(f"""The start and end of the intersection line are
$$ r={sympy_latex(clamp0)} \\text{{ and }} r={sympy_latex(clamp1)} $$
"""))
p1 = together(f.subs(unsolved_for, clamp0))
p2 = together(f.subs(unsolved_for, clamp1))
show_step(show_steps, lambda: Latex(f"""This yields the start and end point of the intersection line:
$$\\begin{{align*}}
p_1 &= {sympy_latex(p1)} \\text{{ and }} \\\\
p_2 &= {sympy_latex(p2)} \\\\
&\\text{{ if }} {sympy_latex(condition)}
\\end{{align*}}$$
"""))
results[(side_sub, solve_for)] = [
p1, p2, condition
]
We repeat these steps for all sides and variables, which gives us 8 pairs of points that are corner points of the section - if their condition is valid.
Shown alongside the plot_RGB_colourspace_section
result from the colour
module, which uses a mesh-intersection algorithm to get the same result
Ys = [0.3, 0.5, 0.75, 0.9]
fig, axs = plt.subplots(2,2)
markers = {0: '+', 1:'x'}
colors = {b: 'blue', g: 'green', r:'yellow'}
linestyles = {r: 'solid', g: 'dashed', b: 'dotted'}
for Yset, ax in zip(Ys, axs.flat):
print(f"Y = {Yset}:")
plot_RGB_colourspace_section('sRGB', 'CIE xyY', origin=Yset, normalise=False, standalone=False, axes=ax)
for (side_sub, solve_for),(p1, p2, condition) in results.items():
print(f"{side_sub}, {solve_for}: ", end="")
if not condition.subs(Y, Yset):
print("Skipping! Outside interval")
continue
x1, y1 = p1.subs(Y, Yset)
x2, y2 = p2.subs(Y, Yset)
print(f"({x1}, {y1}) - ({x2}, {y2})")
ax.plot([x1,x2],[y1,y2], label=f"{side_sub}, {solve_for}",
marker = markers[side_sub[1]], color=colors[side_sub[0]],
linestyle=linestyles[solve_for]
)
plt.show()
Y = 0.3: (b, 0), g: (0.300000000000000, 0.600000000000000) - (0.577366133592135, 0.379762859192371) (b, 0), r: (0.577366133592134, 0.379762859192371) - (0.299999999999999, 0.600000000000001) (b, 1), g: (0.185991660699996, 0.189529545550267) - (0.320637180742191, 0.160185102785441) (b, 1), r: (0.320637180742191, 0.160185102785441) - (0.185991660699995, 0.189529545550267) (r, 0), g: (0.300000000000000, 0.600000000000000) - (0.185991660699996, 0.189529545550267) (r, 0), b: (0.185991660699996, 0.189529545550268) - (0.300000000000003, 0.600000000000009) (r, 1), g: (0.577366133592135, 0.379762859192371) - (0.320637180742191, 0.160185102785441) (r, 1), b: (0.320637180742192, 0.160185102785442) - (0.577366133592139, 0.379762859192375) (g, 0), r: Skipping! Outside interval (g, 0), b: Skipping! Outside interval (g, 1), r: Skipping! Outside interval (g, 1), b: Skipping! Outside interval Y = 0.5: (b, 0), g: (0.300000000000000, 0.600000000000000) - (0.495059200569750, 0.445117065788302) (b, 0), r: (0.495059200569750, 0.445117065788303) - (0.299999999999999, 0.600000000000000) (b, 1), g: (0.205824026719549, 0.260933096753992) - (0.317519075319181, 0.226637455616832) (b, 1), r: (0.317519075319181, 0.226637455616832) - (0.205824026719549, 0.260933096753992) (r, 0), g: (0.300000000000000, 0.600000000000000) - (0.205824026719549, 0.260933096753992) (r, 0), b: (0.205824026719549, 0.260933096753994) - (0.300000000000002, 0.600000000000007) (r, 1), g: (0.495059200569750, 0.445117065788302) - (0.317519075319181, 0.226637455616832) (r, 1), b: (0.317519075319182, 0.226637455616833) - (0.495059200569754, 0.445117065788307) (g, 0), r: Skipping! Outside interval (g, 0), b: Skipping! Outside interval (g, 1), r: Skipping! Outside interval (g, 1), b: Skipping! Outside interval Y = 0.75: (b, 0), g: (0.327642853755811, 0.578050733894227) - (0.442282300482694, 0.487023528647806) (b, 0), r: (0.442282300482694, 0.487023528647807) - (0.327642853755811, 0.578050733894227) (b, 1), g: (0.222644528905781, 0.321492870002572) - (0.314735972548770, 0.285950308190888) (b, 1), r: (0.314735972548770, 0.285950308190888) - (0.222644528905781, 0.321492870002572) (r, 0), g: (0.250912034477843, 0.423265915111071) - (0.222644528905781, 0.321492870002572) (r, 0), b: (0.222644528905781, 0.321492870002574) - (0.250912034477842, 0.423265915111070) (r, 1), g: (0.442282300482694, 0.487023528647806) - (0.314735972548770, 0.285950308190888) (r, 1), b: (0.314735972548771, 0.285950308190889) - (0.442282300482697, 0.487023528647811) (g, 0), r: Skipping! Outside interval (g, 0), b: Skipping! Outside interval (g, 1), r: (0.327642853755811, 0.578050733894227) - (0.250912034477841, 0.423265915111068) (g, 1), b: (0.250912034477842, 0.423265915111070) - (0.327642853755814, 0.578050733894233) Y = 0.9: (b, 0), g: (0.408706220886141, 0.513683956415632) - (0.422410100746755, 0.502802659267398) (b, 0), r: (0.422410100746755, 0.502802659267398) - (0.408706220886142, 0.513683956415632) (b, 1), g: (0.276461377969248, 0.328894161707499) - (0.313453617218774, 0.313279573011545) (b, 1), r: (0.313453617218773, 0.313279573011545) - (0.276461377969248, 0.328894161707499) (r, 0), g: Skipping! Outside interval (r, 0), b: Skipping! Outside interval (r, 1), g: (0.422410100746755, 0.502802659267398) - (0.313453617218774, 0.313279573011545) (r, 1), b: (0.313453617218775, 0.313279573011547) - (0.422410100746758, 0.502802659267404) (g, 0), r: Skipping! Outside interval (g, 0), b: Skipping! Outside interval (g, 1), r: (0.408706220886142, 0.513683956415632) - (0.276461377969248, 0.328894161707499) (g, 1), b: (0.276461377969249, 0.328894161707502) - (0.408706220886145, 0.513683956415637)
From the text output it can be seen, that there are a lot of overlapping results.
For example the result for $f_{b=0,g}(0)$ is equal to $f_{b=0,r}(1)$.
These are the sets that grant equal results. Not all functions are valid for the whole $Y$-range, so the marked functions were manually selected to create a good coverage.
axes = {"x": 0, "y": 1}
allsets = [
[
((b,0),g,0), ((b,0),r,1),
((r,0),g,0), ((r,0),b,1),
((g,1),r,0), ((g,1),b,1),
],
[
((b,0),r,0), ((b,0),g,1),
((g,0),r,0), ((g,0),b,1),
((r,1),g,0), ((r,1),b,1),
],
[
((b,1),r,0), ((b,1),g,1),
((g,0),b,0), ((g,0),r,1),
((r,1),b,0), ((r,1),g,1),
],
[
((b,1),g,0), ((b,1),r,1),
((r,0),b,0), ((r,0),g,1),
((g,1),b,0), ((g,1),r,1),
],
]
selectedsets = [
{
((b,0),g,0), ((g,1),r,0),
},
{
((b,0),r,0), ((r,1),b,1),
},
{
((b,1),g,1), ((g,0),b,0),
},
{
((b,1),g,0), ((r,0),b,0),
},
]
Ys = [0, 0.01, 0.1, 0.2, 0.3, 0.7, 0.8, 0.9, 0.99,1] # 0.4, 0.5, 0.6 left out, because not close to any
table_output = ["<table><tbody>"]
for i, (equalsets, selectedset) in enumerate(zip(allsets,selectedsets), 1):
if i > 1:
table_output.append(f'</tbody></table><table style="margin-top: 3em"><tbody>')
table_output.append(f'<tr><th>Set {i}</th>')
table_output.extend(f'<th>{Yset}</th>' for Yset in Ys)
table_output.append('</tr>')
for axisname, axis in axes.items():
for s in equalsets:
side_sub, solve_for, pnum = s
if s in selectedset:
table_output.append('<tr style="background-color: yellow;color: black">\n')
else:
table_output.append('<tr>\n')
table_output.append(f'<th>\n$$f_{{{side_sub[0]}={side_sub[1]},{solve_for}}}({pnum}, {{{axisname}}})$$\n</th>')
condition = results[(side_sub, solve_for)][2]
for Yset in Ys:
f = results[(side_sub, solve_for)][pnum][axis]
f = Piecewise((f, condition), (oo, True))
value = float(f.subs(Y, Yset))
table_output.append(f'<td>{value:12.9f}</td>')
table_output.append('</tr>\n')
table_output.append('</tbody></table>')
display(Markdown(''.join(table_output)))
Set 1 | 0 | 0.01 | 0.1 | 0.2 | 0.3 | 0.7 | 0.8 | 0.9 | 0.99 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
$$f_{b=0,g}(0, {x})$$ | nan | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.360315454 | 0.408706221 | inf | inf |
$$f_{b=0,r}(1, {x})$$ | nan | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.360315454 | 0.408706221 | inf | inf |
$$f_{r=0,g}(0, {x})$$ | nan | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | inf | inf | inf | inf |
$$f_{r=0,b}(1, {x})$$ | nan | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | 0.300000000 | inf | inf | inf | inf |
$$f_{g=1,r}(0, {x})$$ | inf | inf | inf | inf | inf | inf | 0.360315454 | 0.408706221 | 0.322153757 | inf |
$$f_{g=1,b}(1, {x})$$ | inf | inf | inf | inf | inf | inf | 0.360315454 | 0.408706221 | 0.322153757 | inf |
$$f_{b=0,g}(0, {y})$$ | nan | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.552107696 | 0.513683956 | inf | inf |
$$f_{b=0,r}(1, {y})$$ | nan | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.552107696 | 0.513683956 | inf | inf |
$$f_{r=0,g}(0, {y})$$ | nan | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | inf | inf | inf | inf |
$$f_{r=0,b}(1, {y})$$ | nan | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | 0.600000000 | inf | inf | inf | inf |
$$f_{g=1,r}(0, {y})$$ | inf | inf | inf | inf | inf | inf | 0.552107696 | 0.513683956 | 0.344605315 | inf |
$$f_{g=1,b}(1, {y})$$ | inf | inf | inf | inf | inf | inf | 0.552107696 | 0.513683956 | 0.344605315 | inf |
Set 2 | 0 | 0.01 | 0.1 | 0.2 | 0.3 | 0.7 | 0.8 | 0.9 | 0.99 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
$$f_{b=0,r}(0, {x})$$ | nan | 0.640074499 | 0.640074499 | 0.640074499 | 0.577366134 | 0.450422206 | 0.434978131 | 0.422410101 | inf | inf |
$$f_{b=0,g}(1, {x})$$ | nan | 0.640074499 | 0.640074499 | 0.640074499 | 0.577366134 | 0.450422206 | 0.434978131 | 0.422410101 | inf | inf |
$$f_{g=0,r}(0, {x})$$ | nan | 0.640074499 | 0.640074499 | 0.640074499 | inf | inf | inf | inf | inf | inf |
$$f_{g=0,b}(1, {x})$$ | nan | 0.640074499 | 0.640074499 | 0.640074499 | inf | inf | inf | inf | inf | inf |
$$f_{r=1,g}(0, {x})$$ | inf | inf | inf | inf | 0.577366134 | 0.450422206 | 0.434978131 | 0.422410101 | 0.322153757 | 0.312715907 |
$$f_{r=1,b}(1, {x})$$ | inf | inf | inf | inf | 0.577366134 | 0.450422206 | 0.434978131 | 0.422410101 | 0.322153757 | inf |
$$f_{b=0,r}(0, {y})$$ | nan | 0.329970511 | 0.329970511 | 0.329970511 | 0.379762859 | 0.480560196 | 0.492823261 | 0.502802659 | inf | inf |
$$f_{b=0,g}(1, {y})$$ | nan | 0.329970511 | 0.329970511 | 0.329970511 | 0.379762859 | 0.480560196 | 0.492823261 | 0.502802659 | inf | inf |
$$f_{g=0,r}(0, {y})$$ | nan | 0.329970511 | 0.329970511 | 0.329970511 | inf | inf | inf | inf | inf | inf |
$$f_{g=0,b}(1, {y})$$ | nan | 0.329970511 | 0.329970511 | 0.329970511 | inf | inf | inf | inf | inf | inf |
$$f_{r=1,g}(0, {y})$$ | inf | inf | inf | inf | 0.379762859 | 0.480560196 | 0.492823261 | 0.502802659 | 0.344605315 | 0.329001481 |
$$f_{r=1,b}(1, {y})$$ | inf | inf | inf | inf | 0.379762859 | 0.480560196 | 0.492823261 | 0.502802659 | 0.344605315 | inf |
Set 3 | 0 | 0.01 | 0.1 | 0.2 | 0.3 | 0.7 | 0.8 | 0.9 | 0.99 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
$$f_{b=1,r}(0, {x})$$ | inf | inf | 0.182085716 | 0.269351508 | 0.320637181 | 0.315219531 | 0.314282195 | 0.313453617 | 0.312786018 | inf |
$$f_{b=1,g}(1, {x})$$ | inf | inf | 0.182085716 | 0.269351508 | 0.320637181 | 0.315219531 | 0.314282195 | 0.313453617 | 0.312786018 | 0.312715907 |
$$f_{g=0,b}(0, {x})$$ | nan | 0.150016622 | 0.182085716 | 0.269351508 | inf | inf | inf | inf | inf | inf |
$$f_{g=0,r}(1, {x})$$ | nan | 0.150016622 | 0.182085716 | 0.269351508 | inf | inf | inf | inf | inf | inf |
$$f_{r=1,b}(0, {x})$$ | inf | inf | inf | inf | 0.320637181 | 0.315219531 | 0.314282195 | 0.313453617 | 0.312786018 | inf |
$$f_{r=1,g}(1, {x})$$ | inf | inf | inf | inf | 0.320637181 | 0.315219531 | 0.314282195 | 0.313453617 | 0.312786018 | 0.312715907 |
$$f_{b=1,r}(0, {y})$$ | inf | inf | 0.077672922 | 0.125746040 | 0.160185103 | 0.275644812 | 0.295621112 | 0.313279573 | 0.327507306 | inf |
$$f_{b=1,g}(1, {y})$$ | inf | inf | 0.077672922 | 0.125746040 | 0.160185103 | 0.275644812 | 0.295621112 | 0.313279573 | 0.327507306 | 0.329001481 |
$$f_{g=0,b}(0, {y})$$ | nan | 0.060006649 | 0.077672922 | 0.125746040 | inf | inf | inf | inf | inf | inf |
$$f_{g=0,r}(1, {y})$$ | nan | 0.060006649 | 0.077672922 | 0.125746040 | inf | inf | inf | inf | inf | inf |
$$f_{r=1,b}(0, {y})$$ | inf | inf | inf | inf | 0.160185103 | 0.275644812 | 0.295621112 | 0.313279573 | 0.327507306 | inf |
$$f_{r=1,g}(1, {y})$$ | inf | inf | inf | inf | 0.160185103 | 0.275644812 | 0.295621112 | 0.313279573 | 0.327507306 | 0.329001481 |
Set 4 | 0 | 0.01 | 0.1 | 0.2 | 0.3 | 0.7 | 0.8 | 0.9 | 0.99 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
$$f_{b=1,g}(0, {x})$$ | inf | inf | 0.155578082 | 0.172574495 | 0.185991661 | 0.219778917 | 0.231176464 | 0.276461378 | 0.309419063 | 0.312715907 |
$$f_{b=1,r}(1, {x})$$ | inf | inf | 0.155578082 | 0.172574495 | 0.185991661 | 0.219778917 | 0.231176464 | 0.276461378 | 0.309419063 | inf |
$$f_{r=0,b}(0, {x})$$ | nan | 0.150016622 | 0.155578082 | 0.172574495 | 0.185991661 | 0.219778917 | inf | inf | inf | inf |
$$f_{r=0,g}(1, {x})$$ | nan | 0.150016622 | 0.155578082 | 0.172574495 | 0.185991661 | 0.219778917 | inf | inf | inf | inf |
$$f_{g=1,b}(0, {x})$$ | inf | inf | inf | inf | inf | inf | 0.231176464 | 0.276461378 | 0.309419063 | inf |
$$f_{g=1,r}(1, {x})$$ | inf | inf | inf | inf | inf | inf | 0.231176464 | 0.276461378 | 0.309419063 | inf |
$$f_{b=1,g}(0, {y})$$ | inf | inf | 0.080029878 | 0.141222991 | 0.189529546 | 0.311175651 | 0.328760112 | 0.328894162 | 0.328991721 | 0.329001481 |
$$f_{b=1,r}(1, {y})$$ | inf | inf | 0.080029878 | 0.141222991 | 0.189529546 | 0.311175651 | 0.328760112 | 0.328894162 | 0.328991721 | inf |
$$f_{r=0,b}(0, {y})$$ | nan | 0.060006649 | 0.080029878 | 0.141222991 | 0.189529546 | 0.311175651 | inf | inf | inf | inf |
$$f_{r=0,g}(1, {y})$$ | nan | 0.060006649 | 0.080029878 | 0.141222991 | 0.189529546 | 0.311175651 | inf | inf | inf | inf |
$$f_{g=1,b}(0, {y})$$ | inf | inf | inf | inf | inf | inf | 0.328760112 | 0.328894162 | 0.328991721 | inf |
$$f_{g=1,r}(1, {y})$$ | inf | inf | inf | inf | inf | inf | 0.328760112 | 0.328894162 | 0.328991721 | inf |
Sadly I don't know of a better way to do this instead of the manual sorting and guessing. If you know: Let me know.
We combine the selected results into a function for each point. Showing $p_0$ as an example.
def generate_functions_for_set(s):
axes = {"x", "y"}
result = []
inf_matrix = Matrix([[oo], [oo]])
for i, equalset in enumerate(s):
fs = sympy.piecewise_exclusive(Piecewise(*[
(
results[(side_sub, solve_for)][pnum],
results[(side_sub, solve_for)][2],
)
for side_sub, solve_for, pnum in equalset
], (inf_matrix, True)))
#Readd the "otherwise" case that was removed by piecewise_exclusive
fs = Piecewise(*[(arg[0], sympy.simplify(arg[1])) for arg in fs.args],(inf_matrix, True))
result.append(fs)
return result
point_functions = generate_functions_for_set(selectedsets)
display(Latex(f'$$p_{0} = {sympy.printing.latex(point_functions[0])}$$'))
for pf, p_mat_sym in zip(point_functions, (MatrixSymbol(f'p{i}', 2, 1) for i in range(4))):
refactored_function = Piecewise(*[(Assignment(p_mat_sym, arg[0]), arg[1]) for arg in pf.args])
code = sympy.jscode(refactored_function)
display(Markdown(f'```javascript\n{code}\n```'))
if (Y >= 0.715199999999999 && Y <= 0.999999999999999) {
p0[0] = (1.93979303857008*Y + 0.0404469426152398*Math.max(0, 13.8504155124654*Y - 12.8504155124654) - 1.02973998118532)/(3.03057384760113*Y + 0.984392568203199*Math.max(0, 13.8504155124654*Y - 12.8504155124654) - 0.975466415804328);
p0[1] = Y/(3.03057384760113*Y + 0.984392568203199*Math.max(0, 13.8504155124654*Y - 12.8504155124654) - 0.975466415804328);
}
else if (Y <= 0.927800000000001 && (Y > 0.999999999999999 || Y < 0.715199999999999)) {
p0[0] = (0.5*Y + 0.3061*Math.max(0, 4.70366886171213*Y - 3.36406396989651))/(1.66666666666667*Y + 0.289966666666667*Math.max(0, 4.70366886171213*Y - 3.36406396989651));
p0[1] = Y/(1.66666666666667*Y + 0.289966666666667*Math.max(0, 4.70366886171213*Y - 3.36406396989651));
}
else {
p0[0] = Number.POSITIVE_INFINITY;
p0[1] = Number.POSITIVE_INFINITY;
}
if (Y >= 0.2126 && Y <= 0.999999999999998) {
p1[0] = (2.5*Y - 1.4304*Math.min(1, 1.39821029082774*Y - 0.297259507829978) - 0.1191)/(16.6648199445983*Y - 10.7266792243767*Math.min(1, 1.39821029082774*Y - 0.297259507829978) - 2.89864072022161);
p1[1] = Y/(16.6648199445983*Y - 10.7266792243767*Math.min(1, 1.39821029082774*Y - 0.297259507829978) - 2.89864072022161);
}
else if (Y <= 0.927799999999999 && (Y > 0.999999999999998 || Y < 0.2126)) {
p1[0] = (1.93979303857008*Y - 1.02973998118532*Math.max(0, 1.39821029082774*Y - 0.297259507829978))/(3.03057384760113*Y - 0.975466415804328*Math.max(0, 1.39821029082774*Y - 0.297259507829978));
p1[1] = Y/(3.03057384760113*Y - 0.975466415804328*Math.max(0, 1.39821029082774*Y - 0.297259507829978));
}
else {
p1[0] = Number.POSITIVE_INFINITY;
p1[1] = Number.POSITIVE_INFINITY;
}
if (Y <= 0.2848) {
p2[0] = (2.5*Y - 0.1191*Math.max(0, 4.70366886171214*Y - 0.339604891815616))/(16.6648199445983*Y - 2.89864072022161*Math.max(0, 4.70366886171214*Y - 0.339604891815616));
p2[1] = Y/(16.6648199445983*Y - 2.89864072022161*Math.max(0, 4.70366886171214*Y - 0.339604891815616));
}
else if (Y <= 1.0) {
p2[0] = (0.5*Y + 0.3061*Math.min(1, 4.70366886171213*Y - 0.339604891815616) + 0.1444)/(1.66666666666667*Y + 0.289966666666667*Math.min(1, 4.70366886171213*Y - 0.339604891815616) + 1.08286666666667);
p2[1] = Y/(1.66666666666667*Y + 0.289966666666667*Math.min(1, 4.70366886171213*Y - 0.339604891815616) + 1.08286666666667);
}
else {
p2[0] = Number.POSITIVE_INFINITY;
p2[1] = Number.POSITIVE_INFINITY;
}
if (Y >= 0.0722000000000001 && Y <= 1.0) {
p3[0] = (0.5*Y + 0.3061*Math.max(0, 4.70366886171213*Y - 3.70366886171213) + 0.1444)/(1.66666666666667*Y + 0.289966666666667*Math.max(0, 4.70366886171213*Y - 3.70366886171213) + 1.08286666666667);
p3[1] = Y/(1.66666666666667*Y + 0.289966666666667*Math.max(0, 4.70366886171213*Y - 3.70366886171213) + 1.08286666666667);
}
else if (Y <= 0.787399999999999 && (Y > 1.0 || Y < 0.0722000000000001)) {
p3[0] = (2.5*Y - 1.4304*Math.max(0, 1.39821029082774*Y - 0.100950782997763))/(16.6648199445983*Y - 10.7266792243767*Math.max(0, 1.39821029082774*Y - 0.100950782997763));
p3[1] = Y/(16.6648199445983*Y - 10.7266792243767*Math.max(0, 1.39821029082774*Y - 0.100950782997763));
}
else {
p3[0] = Number.POSITIVE_INFINITY;
p3[1] = Number.POSITIVE_INFINITY;
}
As an alternative to the manual selection one could also just use all the functions and let sympy.piecewise_exclusive
do the job. That does not get rid of manually grouping the functions into groups, though, and the generated functions are quite elaborate:
point_functions_unfiltered = generate_functions_for_set(allsets)
display(Latex(f'$$p_{0} = {sympy.printing.latex(point_functions_unfiltered[0])}$$'))
for pf, p_mat_sym in zip(point_functions_unfiltered, (MatrixSymbol(f'p{i}', 2, 1) for i in range(4))):
refactored_function = Piecewise(*[(Assignment(p_mat_sym, arg[0]), arg[1]) for arg in pf.args])
code = sympy.jscode(refactored_function)
display(Markdown(f'```javascript\n{code}\n```'))