"""
Determine if XYZ is within the Spectral Locus
================================================================
Returns a logical true/false to indicate if a triplet of
tristimulus values falls strictly within the spectral locus.
Version 2020_01_24 Scott Allen Burns
"""
import numpy as np
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon
def in_spectral_locus(cmfs, XYZ):
"""
Checks to see if tristimulus values 'XYZ' are strictly
within the spectral locus, returning true or false. Note
that the illuminant does not come into play in this
determination. If 'XYZ' is on the boundary of the locus,
this function will return false (limited to floating point
precision).
Parameters
----------
cmfs: nx3 array of color matching functions.
XYZ: a three element vector of tristimulus values in the
0 <= Y <= 1 convention range.
Returns
-------
logical true or false.
Notes
-----
1. The function computes the projective transformation from
XYZ space to the x-y plane, so that the test for being within
the locus is reduced a dimension to being within a 2-D polygon.
2. While the shape of the object color solid depends on the
illuminant, the location of the spectral locus in the x-y plane
is independent of illuminant.
3. Be sure to pass an XYZ triplet that sums to a number far enough
from zero to prevent overflow when computing the chromaticity
coordinates (xy = XYZ[0:2]/np.sum(XYZ)).
4. The resolution of cmfs (number of rows) affects the shape of the
computed spectral locus. See Section 6 of Reference [1] for
further discussion.
5. This code was translated from the MATLAB code presented in Section
3 of Reference [2]
References
----------
[1] Burns SA. Numerical methods for smoothest reflectance
reconstruction. Color Research & Application, Vol 45,
No 1, 2020, pp 8-21.
[2] Supplementary Documentation: Numerical Methods for Smoothest
Reflectance Reconstruction, 2019
http://scottburns.us/suppl-docs-num-meth-smoothest-refl-recon/#Sec3
Acknowledgements
----------------
Thanks to Adam J. Burns and Mark (kram1032) for assistance
with translating from MATLAB to Python.
"""
# xy of spectral locus
locus = (cmfs[:, 0:2].T/np.sum(cmfs, 1)).T
poly = Polygon(locus)
# xy of XYZ
xy = XYZ[0:2]/np.sum(XYZ)
p = Point(xy)
# check if point is in polygon
return poly.contains(p)