def endpoint(v1:float=43, prop1:props=props[0], v2:float=23, prop2:props=props[1], prop3:props=props[5]) -> PIL.Image.Image:
"""Looks up psychrometrics parameters. This has nothing to do with
psychology, even to my surprise. It's about thermo properties of water
in both liquid and vapor phases. You can specify 3 properties and 2
values, and this will calculate the other property.
All possible configs:
<table>
<tr><th>Property</th> <th>Description</th> <th>Units</th></tr>
<tr><td>relHum</td> <td>Relative humidity</td> <td>%</td></tr>
<tr><td>humid</td> <td>Absolute humidity</td> <td>kg water/kg dry air</td></tr>
<tr><td>wetBulb</td> <td>Wet bulb temperature</td> <td>Celcius</td></tr>
<tr><td>dryBulb</td> <td>Dry bulb temperature</td> <td>Celcius</td></tr>
<tr><td>enthalpy</td> <td>Enthalpy</td> <td>J/g dry air</td></tr>
<tr><td>specificV</td> <td>Specific volume</td> <td>m³/kg</td></tr>
<table>
:param v1: first value
:param prop1: first property
:param v2: second value
:param prop2: second property
:param prop3: third property"""
g = globals()
res = solveFwdFull(v1, v2, g[prop1], g[prop2])
[prop1, prop2, prop3] | apply(lambda prop: globals()[prop]["ps"] | repeat(3) | joinStreams()) | transpose() | apply(~aS(plt.plot, color="tab:blue") + ~aS(plt.plot, color="tab:orange") + ~aS(plt.plot, color="tab:green")) | deref()
if res is not None:
x, y = res; v3 = solveBkwd(x, y, g[prop3])
plt.plot([x], [y], "o", color="tab:red")
else: return ["Can't solve"] | toImg()
plt.legend([prop1, prop2, prop3]); plt.grid(True)
g[prop3]["ps"] | item().all(2) | insertColumn(g[prop3]["vs"], begin=False) | apply(round, 2, ndigits=4) | ~apply(plt.text, color="tab:red", weight="bold", fontsize=12) | ignore()
plt.title(f"{v1:.3f} {units[prop1]}, {v2:.3f} {units[prop2]}, {v3:.3f} {units[prop3]}")
return plt.gcf() | toImg()