diff --git a/colour/adaptation/cmccat2000.py b/colour/adaptation/cmccat2000.py index a249c3839..6fa4e8859 100644 --- a/colour/adaptation/cmccat2000.py +++ b/colour/adaptation/cmccat2000.py @@ -22,7 +22,7 @@ from __future__ import annotations -from typing import NamedTuple +from dataclasses import dataclass import numpy as np @@ -31,6 +31,7 @@ from colour.hints import ArrayLike, Literal, NDArrayFloat from colour.utilities import ( CanonicalMapping, + MixinDataclassIterable, as_float_array, from_range_100, to_domain_100, @@ -61,7 +62,8 @@ """ -class InductionFactors_CMCCAT2000(NamedTuple): +@dataclass(frozen=True) +class InductionFactors_CMCCAT2000(MixinDataclassIterable): """ *CMCCAT2000* chromatic adaptation model induction factors. diff --git a/colour/adaptation/fairchild2020.py b/colour/adaptation/fairchild2020.py index a23cbe6e7..b1d2c978c 100644 --- a/colour/adaptation/fairchild2020.py +++ b/colour/adaptation/fairchild2020.py @@ -17,7 +17,7 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass import numpy as np @@ -26,6 +26,7 @@ from colour.hints import ArrayLike, Literal, NDArrayFloat from colour.utilities import ( CanonicalMapping, + MixinDataclassIterable, as_float_array, from_range_1, row_as_diagonal, @@ -49,9 +50,8 @@ ] -class Coefficients_DegreeOfAdaptation_vK20( - namedtuple("Coefficients_DegreeOfAdaptation_vK20", ("D_n", "D_r", "D_p")) -): +@dataclass(frozen=True) +class Coefficients_DegreeOfAdaptation_vK20(MixinDataclassIterable): """ *Von Kries 2020* (*vK20*) degree of adaptation coefficients. @@ -69,6 +69,10 @@ class Coefficients_DegreeOfAdaptation_vK20( :cite:`Fairchild2020` """ + D_n: float + D_r: float + D_p: float + CONDITIONS_DEGREE_OF_ADAPTATION_VK20: CanonicalMapping = CanonicalMapping( { @@ -200,7 +204,7 @@ def matrix_chromatic_adaptation_vk20( M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] - D_n, D_r, D_p = coefficients + D_n, D_r, D_p = coefficients.values LMS_n = vecmul(M, XYZ_n) LMS_r = vecmul(M, XYZ_r) diff --git a/colour/algebra/common.py b/colour/algebra/common.py index 333753128..fa80d4204 100644 --- a/colour/algebra/common.py +++ b/colour/algebra/common.py @@ -18,6 +18,7 @@ Callable, Literal, NDArrayFloat, + Self, Tuple, cast, ) @@ -202,7 +203,7 @@ def __init__( self._mode = optional(mode, get_sdiv_mode()) self._previous_mode = get_sdiv_mode() - def __enter__(self) -> sdiv_mode: + def __enter__(self) -> Self: """ Set the *Colour* safe division function mode upon entering the context manager. @@ -411,7 +412,7 @@ def __init__(self, enable: bool) -> None: self._enable = enable self._previous_state = is_spow_enabled() - def __enter__(self) -> spow_enable: + def __enter__(self) -> Self: """ Set the *Colour* safe / symmetrical power function enabled state upon entering the context manager. diff --git a/colour/appearance/cam16.py b/colour/appearance/cam16.py index 7c159570d..31fb8901c 100644 --- a/colour/appearance/cam16.py +++ b/colour/appearance/cam16.py @@ -20,7 +20,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -54,6 +53,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_100, @@ -89,7 +89,8 @@ """Inverse adaptation matrix :math:`M^{-1}_{16}`.""" -class InductionFactors_CAM16(namedtuple("InductionFactors_CAM16", ("F", "c", "N_c"))): +@dataclass(frozen=True) +class InductionFactors_CAM16(MixinDataclassIterable): """ *CAM16* colour appearance model induction factors. @@ -112,6 +113,10 @@ class InductionFactors_CAM16(namedtuple("InductionFactors_CAM16", ("F", "c", "N_ :cite:`Li2017` """ + F: float + c: float + N_c: float + VIEWING_CONDITIONS_CAM16: CanonicalMapping = CanonicalMapping( VIEWING_CONDITIONS_CIECAM02 diff --git a/colour/appearance/ciecam02.py b/colour/appearance/ciecam02.py index d81e4ead0..60e1bf3b2 100644 --- a/colour/appearance/ciecam02.py +++ b/colour/appearance/ciecam02.py @@ -32,7 +32,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -51,6 +50,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, as_int_array, @@ -117,9 +117,8 @@ """Inverse CAT02 chromatic adaptation transform.""" -class InductionFactors_CIECAM02( - namedtuple("InductionFactors_CIECAM02", ("F", "c", "N_c")) -): +@dataclass(frozen=True) +class InductionFactors_CIECAM02(MixinDataclassIterable): """ *CIECAM02* colour appearance model induction factors. @@ -138,6 +137,10 @@ class InductionFactors_CIECAM02( :cite:`Wikipedia2007a` """ + F: float + c: float + N_c: float + VIEWING_CONDITIONS_CIECAM02: CanonicalMapping = CanonicalMapping( { diff --git a/colour/appearance/ciecam16.py b/colour/appearance/ciecam16.py index 91a0ba5c3..4e7ae3035 100644 --- a/colour/appearance/ciecam16.py +++ b/colour/appearance/ciecam16.py @@ -20,7 +20,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -53,6 +52,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_100, @@ -84,9 +84,8 @@ ] -class InductionFactors_CIECAM16( - namedtuple("InductionFactors_CIECAM16", ("F", "c", "N_c")) -): +@dataclass(frozen=True) +class InductionFactors_CIECAM16(MixinDataclassIterable): """ *CIECAM16* colour appearance model induction factors. @@ -109,6 +108,10 @@ class InductionFactors_CIECAM16( :cite:`CIEDivision12022` """ + F: float + c: float + N_c: float + VIEWING_CONDITIONS_CIECAM16: CanonicalMapping = CanonicalMapping( VIEWING_CONDITIONS_CIECAM02 diff --git a/colour/appearance/hellwig2022.py b/colour/appearance/hellwig2022.py index 3b265df7d..519162a1b 100644 --- a/colour/appearance/hellwig2022.py +++ b/colour/appearance/hellwig2022.py @@ -24,7 +24,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -53,6 +52,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_100, @@ -91,9 +91,8 @@ ] -class InductionFactors_Hellwig2022( - namedtuple("InductionFactors_Hellwig2022", ("F", "c", "N_c")) -): +@dataclass(frozen=True) +class InductionFactors_Hellwig2022(MixinDataclassIterable): """ *Hellwig and Fairchild (2022)* colour appearance model induction factors. @@ -116,6 +115,10 @@ class InductionFactors_Hellwig2022( :cite:`Fairchild2022`, :cite:`Hellwig2022` """ + F: float + c: float + N_c: float + VIEWING_CONDITIONS_HELLWIG2022: CanonicalMapping = CanonicalMapping( VIEWING_CONDITIONS_CIECAM02 diff --git a/colour/appearance/hunt.py b/colour/appearance/hunt.py index 52354b583..336d9a0cb 100644 --- a/colour/appearance/hunt.py +++ b/colour/appearance/hunt.py @@ -19,7 +19,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import dataclass, field import numpy as np @@ -29,6 +28,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_degrees, @@ -79,9 +79,8 @@ ] -class InductionFactors_Hunt( - namedtuple("InductionFactors_Hunt", ("N_c", "N_b", "N_cb", "N_bb")) -): +@dataclass(frozen=True) +class InductionFactors_Hunt(MixinDataclassIterable): """ *Hunt* colour appearance model induction factors. @@ -105,19 +104,10 @@ class InductionFactors_Hunt( :cite:`Fairchild2013u`, :cite:`Hunt2004b` """ - def __new__( - cls, - N_c: float, - N_b: float, - N_cb: float | None = None, - N_bb: float | None = None, - ) -> InductionFactors_Hunt: - """ - Return a new instance of the - :class:`colour.appearance.InductionFactors_Hunt` class. - """ - - return super().__new__(cls, N_c, N_b, N_cb, N_bb) + N_c: float + N_b: float + N_cb: float | None = field(default_factory=lambda: None) + N_bb: float | None = field(default_factory=lambda: None) VIEWING_CONDITIONS_HUNT: CanonicalMapping = CanonicalMapping( diff --git a/colour/appearance/kim2009.py b/colour/appearance/kim2009.py index e8347c18a..ca5ab4341 100644 --- a/colour/appearance/kim2009.py +++ b/colour/appearance/kim2009.py @@ -21,7 +21,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -42,6 +41,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_100, @@ -72,9 +72,8 @@ ] -class InductionFactors_Kim2009( - namedtuple("InductionFactors_Kim2009", ("F", "c", "N_c")) -): +@dataclass(frozen=True) +class InductionFactors_Kim2009(MixinDataclassIterable): """ *Kim, Weyrich and Kautz (2009)* colour appearance model induction factors. @@ -102,6 +101,10 @@ class InductionFactors_Kim2009( :cite:`Kim2009` """ + F: float + c: float + N_c: float + VIEWING_CONDITIONS_KIM2009: CanonicalMapping = CanonicalMapping( VIEWING_CONDITIONS_CIECAM02 @@ -116,7 +119,8 @@ class InductionFactors_Kim2009( """ -class MediaParameters_Kim2009(namedtuple("MediaParameters_Kim2009", ("E",))): +@dataclass(frozen=True) +class MediaParameters_Kim2009: """ *Kim, Weyrich and Kautz (2009)* colour appearance model media parameters. @@ -130,13 +134,7 @@ class MediaParameters_Kim2009(namedtuple("MediaParameters_Kim2009", ("E",))): :cite:`Kim2009` """ - def __new__(cls, E: float) -> MediaParameters_Kim2009: - """ - Return a new instance of the - :class:`colour.appearance.MediaParameters_Kim2009` class. - """ - - return super().__new__(cls, E) + E: float MEDIA_PARAMETERS_KIM2009: CanonicalMapping = CanonicalMapping( diff --git a/colour/appearance/llab.py b/colour/appearance/llab.py index 33e150eea..c70aa8a2f 100644 --- a/colour/appearance/llab.py +++ b/colour/appearance/llab.py @@ -27,7 +27,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import dataclass, field import numpy as np @@ -43,6 +42,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, from_range_degrees, @@ -78,9 +78,8 @@ ] -class InductionFactors_LLAB( - namedtuple("InductionFactors_LLAB", ("D", "F_S", "F_L", "F_C")) -): +@dataclass(frozen=True) +class InductionFactors_LLAB(MixinDataclassIterable): """ *:math:`LLAB(l:c)`* colour appearance model induction factors. @@ -100,6 +99,11 @@ class InductionFactors_LLAB( :cite:`Fairchild2013x`, :cite:`Luo1996b`, :cite:`Luo1996c` """ + D: float + F_S: float + F_L: float + F_C: float + VIEWING_CONDITIONS_LLAB: CanonicalMapping = CanonicalMapping( { diff --git a/colour/appearance/zcam.py b/colour/appearance/zcam.py index 278c67a67..739679938 100644 --- a/colour/appearance/zcam.py +++ b/colour/appearance/zcam.py @@ -26,7 +26,6 @@ from __future__ import annotations -from collections import namedtuple from dataclasses import astuple, dataclass, field import numpy as np @@ -44,6 +43,7 @@ from colour.utilities import ( CanonicalMapping, MixinDataclassArithmetic, + MixinDataclassIterable, as_float, as_float_array, as_int_array, @@ -74,9 +74,8 @@ ] -class InductionFactors_ZCAM( - namedtuple("InductionFactors_ZCAM", ("F_s", "F", "c", "N_c")) -): +@dataclass(frozen=True) +class InductionFactors_ZCAM(MixinDataclassIterable): """ *ZCAM* colour appearance model induction factors. @@ -101,12 +100,21 @@ class InductionFactors_ZCAM( :cite:`Safdar2021` """ + F_s: float + F: float + c: float + N_c: float + VIEWING_CONDITIONS_ZCAM: CanonicalMapping = CanonicalMapping( { - "Average": InductionFactors_ZCAM(0.69, *VIEWING_CONDITIONS_CIECAM02["Average"]), - "Dim": InductionFactors_ZCAM(0.59, *VIEWING_CONDITIONS_CIECAM02["Dim"]), - "Dark": InductionFactors_ZCAM(0.525, *VIEWING_CONDITIONS_CIECAM02["Dark"]), + "Average": InductionFactors_ZCAM( + 0.69, *VIEWING_CONDITIONS_CIECAM02["Average"].values + ), + "Dim": InductionFactors_ZCAM(0.59, *VIEWING_CONDITIONS_CIECAM02["Dim"].values), + "Dark": InductionFactors_ZCAM( + 0.525, *VIEWING_CONDITIONS_CIECAM02["Dark"].values + ), } ) VIEWING_CONDITIONS_ZCAM.__doc__ = """ @@ -424,7 +432,7 @@ def XYZ_to_ZCAM( L_A = as_float_array(L_A) Y_b = as_float_array(Y_b) - F_s, F, _c, _N_c = surround + F_s, F, _c, _N_c = surround.values # Step 0 (Forward) - Chromatic adaptation from reference illuminant to # "CIE Standard Illuminant D65" illuminant using "CAT02". @@ -641,7 +649,7 @@ def ZCAM_to_XYZ( L_A = as_float_array(L_A) Y_b = as_float_array(Y_b) - F_s, F, c, N_c = surround + F_s, F, c, N_c = surround.values # Step 0 (Forward) - Chromatic adaptation from reference illuminant to # "CIE Standard Illuminant D65" illuminant using "CAT02". diff --git a/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py b/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py index 614940ecd..693e8198f 100644 --- a/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py +++ b/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py @@ -56,7 +56,7 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass import numpy as np @@ -114,9 +114,8 @@ ] -class ColourChecker( - namedtuple("ColourChecker", ("name", "data", "illuminant", "rows", "columns")) -): +@dataclass(frozen=True) +class ColourChecker: """ *Colour Checker* data. @@ -134,6 +133,12 @@ class ColourChecker( *Colour Checker* column count. """ + name: str + data: dict[str, NDArrayFloat] + illuminant: NDArrayFloat + rows: int + columns: int + SAMPLE_LABELS_COLORCHECKER_CLASSIC: tuple = ( "dark skin", diff --git a/colour/colorimetry/datasets/illuminants/hunterlab.py b/colour/colorimetry/datasets/illuminants/hunterlab.py index fde9e1359..a2e2d753f 100644 --- a/colour/colorimetry/datasets/illuminants/hunterlab.py +++ b/colour/colorimetry/datasets/illuminants/hunterlab.py @@ -22,10 +22,11 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass import numpy as np +from colour.hints import NDArrayFloat from colour.utilities import CanonicalMapping __author__ = "Colour Developers" @@ -44,9 +45,26 @@ "TVS_ILLUMINANTS_HUNTERLAB", ] -Illuminant_Specification_HunterLab = namedtuple( - "Illuminant_Specification_HunterLab", ("name", "XYZ_n", "K_ab") -) + +@dataclass(frozen=True) +class Illuminant_Specification_HunterLab: + """ + Define the specification for an *HunterLab* illuminant. + + Parameters + ---------- + name + Illuminant name + XYZ_n + Illuminant *CIE XYZ* tristimulus values + K_ab + Illuminant :math:`K_{a}` and :math:`K_{b}` chromaticity coefficients. + """ + + name: str + XYZ_n: NDArrayFloat + K_ab: NDArrayFloat + DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931: tuple = ( ("A", np.array([109.83, 100.00, 35.55]), np.array([185.20, 38.40])), diff --git a/colour/colorimetry/spectrum.py b/colour/colorimetry/spectrum.py index 1e1acdb8d..5dbd446fc 100644 --- a/colour/colorimetry/spectrum.py +++ b/colour/colorimetry/spectrum.py @@ -426,7 +426,7 @@ def __len__(self) -> int: return len(self.wavelengths) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the spectral shape is equal to given other object. @@ -452,7 +452,7 @@ def __eq__(self, other: Any) -> bool: return np.array_equal(self.wavelengths, other.wavelengths) return False - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the spectral shape is not equal to given other object. diff --git a/colour/continuous/abstract.py b/colour/continuous/abstract.py index 962564bfe..451d3cef7 100644 --- a/colour/continuous/abstract.py +++ b/colour/continuous/abstract.py @@ -16,7 +16,6 @@ import numpy as np from colour.hints import ( - Any, ArrayLike, Callable, DTypeFloat, @@ -514,7 +513,7 @@ def __len__(self) -> int: return len(self.domain) @abstractmethod - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the abstract continuous function is equal to given other object, must be reimplemented by sub-classes. @@ -534,7 +533,7 @@ def __eq__(self, other: Any) -> bool: ... # pragma: no cover @abstractmethod - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the abstract continuous function is not equal to given other object, must be reimplemented by sub-classes. diff --git a/colour/continuous/multi_signals.py b/colour/continuous/multi_signals.py index bca659b4a..7f3bd3e5e 100644 --- a/colour/continuous/multi_signals.py +++ b/colour/continuous/multi_signals.py @@ -968,7 +968,7 @@ def __contains__(self, x: ArrayLike | slice) -> bool: return x in first_item(self._signals.values()) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the multi-continuous signals is equal to given other object. @@ -1018,7 +1018,7 @@ def __eq__(self, other: Any) -> bool: ) return False - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the multi-continuous signals is not equal to given other object. diff --git a/colour/continuous/signal.py b/colour/continuous/signal.py index 24de4ad3d..f68e6cf02 100644 --- a/colour/continuous/signal.py +++ b/colour/continuous/signal.py @@ -849,7 +849,7 @@ def __contains__(self, x: ArrayLike | slice) -> bool: ) @ndarray_copy_enable(False) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the continuous signal is equal to given other object. @@ -897,7 +897,7 @@ def __eq__(self, other: Any) -> bool: ) return False - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the continuous signal is not equal to given other object. diff --git a/colour/corresponding/datasets/breneman1987.py b/colour/corresponding/datasets/breneman1987.py index 5bfa7fd2f..b1fb62429 100644 --- a/colour/corresponding/datasets/breneman1987.py +++ b/colour/corresponding/datasets/breneman1987.py @@ -15,11 +15,12 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass, field import numpy as np -from colour.hints import ArrayLike, Literal +from colour.hints import NDArrayFloat, Tuple +from colour.utilities import MixinDataclassIterable, as_float_array from colour.utilities.documentation import ( DocstringDict, is_documentation_building, @@ -52,12 +53,8 @@ ] -class BrenemanExperimentResult( - namedtuple( - "BrenemanExperimentResult", - ("name", "uv_t", "uv_m", "s_uv", "d_uv_i", "d_uv_g"), - ) -): +@dataclass(frozen=True) +class BrenemanExperimentResult(MixinDataclassIterable): """ Experiment result. @@ -79,38 +76,31 @@ class BrenemanExperimentResult( :math:`\\delta_uv_g^p`. """ - def __new__( - cls, - name: str, - uv_t: ArrayLike, - uv_m: ArrayLike, - s_uv: ArrayLike | None = None, - d_uv_i: ArrayLike | None = None, - d_uv_g: ArrayLike | None = None, - ) -> BrenemanExperimentResult: - """ - Return a new instance of the - :class:`colour.corresponding.datasets.corresponding_chromaticities.\ -BrenemanExperimentResult` class. - """ - - return super().__new__( - cls, - name, - np.array(uv_t), - np.array(uv_m), - np.array(s_uv), - np.array(d_uv_i), - np.array(d_uv_g), - ) - - -class PrimariesChromaticityCoordinates( - namedtuple( - "PrimariesChromaticityCoordinates", - ("experiment", "illuminants", "Y", "P_uvp", "D_uvp", "T_uvp"), - ) -): + name: str + uv_t: NDArrayFloat + uv_m: NDArrayFloat + s_uv: NDArrayFloat | None = field(default_factory=lambda: None) + d_uv_i: NDArrayFloat | None = field(default_factory=lambda: None) + d_uv_g: NDArrayFloat | None = field(default_factory=lambda: None) + + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "uv_t", as_float_array(self.uv_t)) + object.__setattr__(self, "uv_m", as_float_array(self.uv_m)) + + if self.s_uv is not None: + object.__setattr__(self, "s_uv", as_float_array(self.s_uv)) + + if self.d_uv_i is not None: + object.__setattr__(self, "d_uv_i", as_float_array(self.d_uv_i)) + + if self.d_uv_g is not None: + object.__setattr__(self, "d_uv_g", as_float_array(self.d_uv_g)) + + +@dataclass(frozen=True) +class PrimariesChromaticityCoordinates(MixinDataclassIterable): """ Chromaticity coordinates of the primaries. @@ -130,69 +120,127 @@ class PrimariesChromaticityCoordinates( Chromaticity coordinates :math:`uv^p` of primary :math:`T`. """ - def __new__( - cls, - experiment: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], - illuminants: ArrayLike, - Y: ArrayLike, - P_uvp: ArrayLike | None = None, - D_uvp: ArrayLike | None = None, - T_uvp: ArrayLike | None = None, - ) -> PrimariesChromaticityCoordinates: - """ - Return a new instance of the - :class:`colour.corresponding.datasets.corresponding_chromaticities.\ -PrimariesChromaticityCoordinates` class. - """ - - return super().__new__( - cls, - experiment, - np.array(illuminants), - np.array(Y), - np.array(P_uvp), - np.array(D_uvp), - np.array(T_uvp), - ) + experiment: int + illuminants: Tuple[str, str] + Y: float + P_uvp: NDArrayFloat | None = field(default_factory=lambda: None) + D_uvp: NDArrayFloat | None = field(default_factory=lambda: None) + T_uvp: NDArrayFloat | None = field(default_factory=lambda: None) + + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "Y", as_float_array(self.Y)) + + if self.P_uvp is not None: + object.__setattr__(self, "P_uvp", as_float_array(self.P_uvp)) + + if self.D_uvp is not None: + object.__setattr__(self, "D_uvp", as_float_array(self.D_uvp)) + + if self.T_uvp is not None: + object.__setattr__(self, "T_uvp", as_float_array(self.T_uvp)) BRENEMAN_EXPERIMENT_1_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.259, 0.526), (0.200, 0.475)), BrenemanExperimentResult( - "Gray", (0.259, 0.524), (0.199, 0.487), (4, 4), (2, 3), (0, 0) + "Illuminant", np.array([0.259, 0.526]), np.array([0.200, 0.475]) + ), + BrenemanExperimentResult( + "Gray", + np.array([0.259, 0.524]), + np.array([0.199, 0.487]), + np.array([4, 4]), + np.array([2, 3]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Red", (0.459, 0.522), (0.420, 0.509), (19, 4), (-10, -7), (-19, -3) + "Red", + np.array([0.459, 0.522]), + np.array([0.420, 0.509]), + np.array([19, 4]), + np.array([-10, -7]), + np.array([-19, -3]), ), BrenemanExperimentResult( - "Skin", (0.307, 0.526), (0.249, 0.497), (7, 4), (-1, 1), (-6, -1) + "Skin", + np.array([0.307, 0.526]), + np.array([0.249, 0.497]), + np.array([7, 4]), + np.array([-1, 1]), + np.array([-6, -1]), ), BrenemanExperimentResult( - "Orange", (0.360, 0.544), (0.302, 0.548), (12, 1), (1, -2), (-7, -6) + "Orange", + np.array([0.360, 0.544]), + np.array([0.302, 0.548]), + np.array([12, 1]), + np.array([1, -2]), + np.array([-7, -6]), ), BrenemanExperimentResult( - "Brown", (0.350, 0.541), (0.290, 0.537), (11, 4), (3, 0), (-5, -3) + "Brown", + np.array([0.350, 0.541]), + np.array([0.290, 0.537]), + np.array([11, 4]), + np.array([3, 0]), + np.array([-5, -3]), ), BrenemanExperimentResult( - "Yellow", (0.318, 0.550), (0.257, 0.554), (8, 2), (0, 2), (-5, -5) + "Yellow", + np.array([0.318, 0.550]), + np.array([0.257, 0.554]), + np.array([8, 2]), + np.array([0, 2]), + np.array([-5, -5]), ), BrenemanExperimentResult( - "Foliage", (0.258, 0.542), (0.192, 0.529), (4, 6), (3, 2), (3, -6) + "Foliage", + np.array([0.258, 0.542]), + np.array([0.192, 0.529]), + np.array([4, 6]), + np.array([3, 2]), + np.array([3, -6]), ), BrenemanExperimentResult( - "Green", (0.193, 0.542), (0.129, 0.521), (7, 5), (3, 2), (9, -7) + "Green", + np.array([0.193, 0.542]), + np.array([0.129, 0.521]), + np.array([7, 5]), + np.array([3, 2]), + np.array([9, -7]), ), BrenemanExperimentResult( - "Blue-green", (0.180, 0.516), (0.133, 0.469), (4, 6), (-3, -2), (2, -5) + "Blue-green", + np.array([0.180, 0.516]), + np.array([0.133, 0.469]), + np.array([4, 6]), + np.array([-3, -2]), + np.array([2, -5]), ), BrenemanExperimentResult( - "Blue", (0.186, 0.445), (0.158, 0.340), (13, 33), (2, 7), (1, 13) + "Blue", + np.array([0.186, 0.445]), + np.array([0.158, 0.340]), + np.array([13, 33]), + np.array([2, 7]), + np.array([1, 13]), ), BrenemanExperimentResult( - "Sky", (0.226, 0.491), (0.178, 0.426), (3, 14), (1, -3), (0, -1) + "Sky", + np.array([0.226, 0.491]), + np.array([0.178, 0.426]), + np.array([3, 14]), + np.array([1, -3]), + np.array([0, -1]), ), BrenemanExperimentResult( - "Purple", (0.278, 0.456), (0.231, 0.365), (4, 25), (0, 2), (-5, 7) + "Purple", + np.array([0.278, 0.456]), + np.array([0.231, 0.365]), + np.array([4, 25]), + np.array([0, 2]), + np.array([-5, 7]), ), ) """ @@ -207,42 +255,104 @@ def __new__( BRENEMAN_EXPERIMENT_2_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.222, 0.521), (0.204, 0.479)), BrenemanExperimentResult( - "Gray", (0.227, 0.517), (0.207, 0.486), (2, 5), (-1, 0), (0, 0) + "Illuminant", np.array([0.222, 0.521]), np.array([0.204, 0.479]) + ), + BrenemanExperimentResult( + "Gray", + np.array([0.227, 0.517]), + np.array([0.207, 0.486]), + np.array([2, 5]), + np.array([-1, 0]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Red", (0.464, 0.520), (0.449, 0.511), (22, 3), (-8, -8), (-7, -2) + "Red", + np.array([0.464, 0.520]), + np.array([0.449, 0.511]), + np.array([22, 3]), + np.array([-8, -8]), + np.array([-7, -2]), ), BrenemanExperimentResult( - "Skin", (0.286, 0.526), (0.263, 0.505), (7, 2), (0, -1), (0, -1) + "Skin", + np.array([0.286, 0.526]), + np.array([0.263, 0.505]), + np.array([7, 2]), + np.array([0, -1]), + np.array([0, -1]), ), BrenemanExperimentResult( - "Orange", (0.348, 0.546), (0.322, 0.545), (13, 3), (3, -1), (3, -2) + "Orange", + np.array([0.348, 0.546]), + np.array([0.322, 0.545]), + np.array([13, 3]), + np.array([3, -1]), + np.array([3, -2]), ), BrenemanExperimentResult( - "Brown", (0.340, 0.543), (0.316, 0.537), (11, 3), (1, 1), (0, 0) + "Brown", + np.array([0.340, 0.543]), + np.array([0.316, 0.537]), + np.array([11, 3]), + np.array([1, 1]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Yellow", (0.288, 0.554), (0.265, 0.553), (5, 2), (-2, 2), (-1, -2) + "Yellow", + np.array([0.288, 0.554]), + np.array([0.265, 0.553]), + np.array([5, 2]), + np.array([-2, 2]), + np.array([-1, -2]), ), BrenemanExperimentResult( - "Foliage", (0.244, 0.547), (0.221, 0.538), (4, 3), (-2, 1), (0, -3) + "Foliage", + np.array([0.244, 0.547]), + np.array([0.221, 0.538]), + np.array([4, 3]), + np.array([-2, 1]), + np.array([0, -3]), ), BrenemanExperimentResult( - "Green", (0.156, 0.548), (0.135, 0.532), (4, 3), (-1, 3), (3, -4) + "Green", + np.array([0.156, 0.548]), + np.array([0.135, 0.532]), + np.array([4, 3]), + np.array([-1, 3]), + np.array([3, -4]), ), BrenemanExperimentResult( - "Blue-green", (0.159, 0.511), (0.145, 0.472), (9, 7), (-1, 2), (2, 1) + "Blue-green", + np.array([0.159, 0.511]), + np.array([0.145, 0.472]), + np.array([9, 7]), + np.array([-1, 2]), + np.array([2, 1]), ), BrenemanExperimentResult( - "Blue", (0.160, 0.406), (0.163, 0.331), (23, 31), (2, -3), (-1, 3) + "Blue", + np.array([0.160, 0.406]), + np.array([0.163, 0.331]), + np.array([23, 31]), + np.array([2, -3]), + np.array([-1, 3]), ), BrenemanExperimentResult( - "Sky", (0.190, 0.481), (0.176, 0.431), (5, 24), (2, -2), (2, 0) + "Sky", + np.array([0.190, 0.481]), + np.array([0.176, 0.431]), + np.array([5, 24]), + np.array([2, -2]), + np.array([2, 0]), ), BrenemanExperimentResult( - "Purple", (0.258, 0.431), (0.244, 0.349), (4, 19), (-3, 13), (-4, 19) + "Purple", + np.array([0.258, 0.431]), + np.array([0.244, 0.349]), + np.array([4, 19]), + np.array([-3, 13]), + np.array([-4, 19]), ), ) """ @@ -257,47 +367,104 @@ def __new__( BRENEMAN_EXPERIMENT_3_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.223, 0.521), (0.206, 0.478)), BrenemanExperimentResult( - "Gray", (0.228, 0.517), (0.211, 0.494), (1, 3), (0, 2), (0, 0) + "Illuminant", np.array([0.223, 0.521]), np.array([0.206, 0.478]) + ), + BrenemanExperimentResult( + "Gray", + np.array([0.228, 0.517]), + np.array([0.211, 0.494]), + np.array([1, 3]), + np.array([0, 2]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Red", (0.462, 0.519), (0.448, 0.505), (11, 4), (-3, 6), (-4, 6) + "Red", + np.array([0.462, 0.519]), + np.array([0.448, 0.505]), + np.array([11, 4]), + np.array([-3, 6]), + np.array([-4, 6]), ), BrenemanExperimentResult( - "Skin", (0.285, 0.524), (0.267, 0.507), (6, 3), (-1, 1), (-2, 1) + "Skin", + np.array([0.285, 0.524]), + np.array([0.267, 0.507]), + np.array([6, 3]), + np.array([-1, 1]), + np.array([-2, 1]), ), BrenemanExperimentResult( - "Orange", (0.346, 0.546), (0.325, 0.541), (11, 3), (1, -2), (2, 3) + "Orange", + np.array([0.346, 0.546]), + np.array([0.325, 0.541]), + np.array([11, 3]), + np.array([1, -2]), + np.array([2, 3]), ), BrenemanExperimentResult( - "Brown", (0.338, 0.543), (0.321, 0.532), (9, 6), (-3, 2), (-3, 7) + "Brown", + np.array([0.338, 0.543]), + np.array([0.321, 0.532]), + np.array([9, 6]), + np.array([-3, 2]), + np.array([-3, 7]), ), BrenemanExperimentResult( - "Yellow", (0.287, 0.554), (0.267, 0.548), (4, 5), (1, -2), (0, 5) + "Yellow", + np.array([0.287, 0.554]), + np.array([0.267, 0.548]), + np.array([4, 5]), + np.array([1, -2]), + np.array([0, 5]), ), BrenemanExperimentResult( - "Foliage", (0.244, 0.547), (0.226, 0.531), (3, 6), (-1, 3), (-2, 8) + "Foliage", + np.array([0.244, 0.547]), + np.array([0.226, 0.531]), + np.array([3, 6]), + np.array([-1, 3]), + np.array([-2, 8]), ), BrenemanExperimentResult( - "Green", (0.157, 0.548), (0.141, 0.528), (9, 6), (2, 2), (0, 6) + "Green", + np.array([0.157, 0.548]), + np.array([0.141, 0.528]), + np.array([9, 6]), + np.array([2, 2]), + np.array([0, 6]), ), BrenemanExperimentResult( "Blue-green", - (0.160, 0.510), - (0.151, 0.486), - (8, 5), - (-2, -1), - (-2, -5), + np.array([0.160, 0.510]), + np.array([0.151, 0.486]), + np.array([8, 5]), + np.array([-2, -1]), + np.array([-2, -5]), ), BrenemanExperimentResult( - "Blue", (0.162, 0.407), (0.158, 0.375), (6, 7), (1, -6), (4, -23) + "Blue", + np.array([0.162, 0.407]), + np.array([0.158, 0.375]), + np.array([6, 7]), + np.array([1, -6]), + np.array([4, -23]), ), BrenemanExperimentResult( - "Sky", (0.191, 0.482), (0.179, 0.452), (4, 5), (0, 1), (1, -7) + "Sky", + np.array([0.191, 0.482]), + np.array([0.179, 0.452]), + np.array([4, 5]), + np.array([0, 1]), + np.array([1, -7]), ), BrenemanExperimentResult( - "Purple", (0.258, 0.432), (0.238, 0.396), (4, 8), (5, 3), (4, -11) + "Purple", + np.array([0.258, 0.432]), + np.array([0.238, 0.396]), + np.array([4, 8]), + np.array([5, 3]), + np.array([4, -11]), ), ) """ @@ -312,47 +479,104 @@ def __new__( BRENEMAN_EXPERIMENT_4_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.258, 0.523), (0.199, 0.467)), BrenemanExperimentResult( - "Gray", (0.257, 0.524), (0.205, 0.495), (2, 2), (0, 4), (0, 0) + "Illuminant", np.array([0.258, 0.523]), np.array([0.199, 0.467]) ), BrenemanExperimentResult( - "Red", (0.460, 0.521), (0.416, 0.501), (11, 6), (-6, 4), (-6, 9) + "Gray", + np.array([0.257, 0.524]), + np.array([0.205, 0.495]), + np.array([2, 2]), + np.array([0, 4]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Skin", (0.308, 0.526), (0.253, 0.503), (7, 3), (-1, 1), (-1, 0) + "Red", + np.array([0.460, 0.521]), + np.array([0.416, 0.501]), + np.array([11, 6]), + np.array([-6, 4]), + np.array([-6, 9]), ), BrenemanExperimentResult( - "Orange", (0.360, 0.544), (0.303, 0.541), (14, 5), (1, -4), (1, 2) + "Skin", + np.array([0.308, 0.526]), + np.array([0.253, 0.503]), + np.array([7, 3]), + np.array([-1, 1]), + np.array([-1, 0]), ), BrenemanExperimentResult( - "Brown", (0.350, 0.541), (0.296, 0.527), (11, 7), (-2, 4), (-3, 9) + "Orange", + np.array([0.360, 0.544]), + np.array([0.303, 0.541]), + np.array([14, 5]), + np.array([1, -4]), + np.array([1, 2]), ), BrenemanExperimentResult( - "Yellow", (0.317, 0.550), (0.260, 0.547), (9, 5), (1, -3), (0, 3) + "Brown", + np.array([0.350, 0.541]), + np.array([0.296, 0.527]), + np.array([11, 7]), + np.array([-2, 4]), + np.array([-3, 9]), ), BrenemanExperimentResult( - "Foliage", (0.258, 0.543), (0.203, 0.520), (4, 6), (0, 8), (0, 9) + "Yellow", + np.array([0.317, 0.550]), + np.array([0.260, 0.547]), + np.array([9, 5]), + np.array([1, -3]), + np.array([0, 3]), ), BrenemanExperimentResult( - "Green", (0.193, 0.543), (0.142, 0.516), (6, 9), (3, 8), (2, 6) + "Foliage", + np.array([0.258, 0.543]), + np.array([0.203, 0.520]), + np.array([4, 6]), + np.array([0, 8]), + np.array([0, 9]), + ), + BrenemanExperimentResult( + "Green", + np.array([0.193, 0.543]), + np.array([0.142, 0.516]), + np.array([6, 9]), + np.array([3, 8]), + np.array([2, 6]), ), BrenemanExperimentResult( "Blue-green", - (0.180, 0.516), - (0.140, 0.484), - (9, 5), - (-2, -1), - (-1, -9), + np.array([0.180, 0.516]), + np.array([0.140, 0.484]), + np.array([9, 5]), + np.array([-2, -1]), + np.array([-1, -9]), ), BrenemanExperimentResult( - "Blue", (0.185, 0.445), (0.151, 0.394), (8, 10), (2, -8), (8, -24) + "Blue", + np.array([0.185, 0.445]), + np.array([0.151, 0.394]), + np.array([8, 10]), + np.array([2, -8]), + np.array([8, -24]), ), BrenemanExperimentResult( - "Sky", (0.225, 0.490), (0.180, 0.448), (4, 8), (1, -1), (3, -11) + "Sky", + np.array([0.225, 0.490]), + np.array([0.180, 0.448]), + np.array([4, 8]), + np.array([1, -1]), + np.array([3, -11]), ), BrenemanExperimentResult( - "Purple", (0.278, 0.455), (0.229, 0.388), (6, 14), (1, 12), (3, 0) + "Purple", + np.array([0.278, 0.455]), + np.array([0.229, 0.388]), + np.array([6, 14]), + np.array([1, 12]), + np.array([3, 0]), ), ) """ @@ -367,18 +591,45 @@ def __new__( BRENEMAN_EXPERIMENT_5_RESULTS = ( - BrenemanExperimentResult("Gray", (0.028, 0.480), (0.212, 0.491), (2, 2)), - BrenemanExperimentResult("Red", (0.449, 0.512), (0.408, 0.514), (11, 5)), - BrenemanExperimentResult("Skin", (0.269, 0.505), (0.262, 0.511), (4, 2)), - BrenemanExperimentResult("Orange", (0.331, 0.548), (0.303, 0.545), (4, 3)), - BrenemanExperimentResult("Brown", (0.322, 0.541), (0.303, 0.538), (4, 4)), - BrenemanExperimentResult("Yellow", (0.268, 0.555), (0.264, 0.550), (3, 2)), - BrenemanExperimentResult("Foliage", (0.224, 0.538), (0.227, 0.535), (3, 3)), - BrenemanExperimentResult("Green", (0.134, 0.531), (0.159, 0.530), (9, 3)), - BrenemanExperimentResult("Blue-green", (0.145, 0.474), (0.165, 0.490), (8, 3)), - BrenemanExperimentResult("Blue", (0.163, 0.329), (0.173, 0.378), (7, 12)), - BrenemanExperimentResult("Sky", (0.179, 0.438), (0.189, 0.462), (5, 4)), - BrenemanExperimentResult("Purple", (0.245, 0.364), (0.239, 0.401), (4, 16)), + BrenemanExperimentResult( + "Gray", np.array([0.028, 0.480]), np.array([0.212, 0.491]), np.array([2, 2]) + ), + BrenemanExperimentResult( + "Red", np.array([0.449, 0.512]), np.array([0.408, 0.514]), np.array([11, 5]) + ), + BrenemanExperimentResult( + "Skin", np.array([0.269, 0.505]), np.array([0.262, 0.511]), np.array([4, 2]) + ), + BrenemanExperimentResult( + "Orange", np.array([0.331, 0.548]), np.array([0.303, 0.545]), np.array([4, 3]) + ), + BrenemanExperimentResult( + "Brown", np.array([0.322, 0.541]), np.array([0.303, 0.538]), np.array([4, 4]) + ), + BrenemanExperimentResult( + "Yellow", np.array([0.268, 0.555]), np.array([0.264, 0.550]), np.array([3, 2]) + ), + BrenemanExperimentResult( + "Foliage", np.array([0.224, 0.538]), np.array([0.227, 0.535]), np.array([3, 3]) + ), + BrenemanExperimentResult( + "Green", np.array([0.134, 0.531]), np.array([0.159, 0.530]), np.array([9, 3]) + ), + BrenemanExperimentResult( + "Blue-green", + np.array([0.145, 0.474]), + np.array([0.165, 0.490]), + np.array([8, 3]), + ), + BrenemanExperimentResult( + "Blue", np.array([0.163, 0.329]), np.array([0.173, 0.378]), np.array([7, 12]) + ), + BrenemanExperimentResult( + "Sky", np.array([0.179, 0.438]), np.array([0.189, 0.462]), np.array([5, 4]) + ), + BrenemanExperimentResult( + "Purple", np.array([0.245, 0.364]), np.array([0.239, 0.401]), np.array([4, 16]) + ), ) """ *Breneman (1987)* experiment 5 results. @@ -391,42 +642,104 @@ def __new__( BRENEMAN_EXPERIMENT_6_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.257, 0.525), (0.201, 0.482)), BrenemanExperimentResult( - "Gray", (0.267, 0.521), (0.207, 0.485), (5, 3), (-1, 0), (0, 0) + "Illuminant", np.array([0.257, 0.525]), np.array([0.201, 0.482]) ), BrenemanExperimentResult( - "Red", (0.457, 0.521), (0.398, 0.516), (9, 4), (-2, -5), (1, -9) + "Gray", + np.array([0.267, 0.521]), + np.array([0.207, 0.485]), + np.array([5, 3]), + np.array([-1, 0]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Skin", (0.316, 0.526), (0.253, 0.503), (5, 3), (-3, -2), (-1, -3) + "Red", + np.array([0.457, 0.521]), + np.array([0.398, 0.516]), + np.array([9, 4]), + np.array([-2, -5]), + np.array([1, -9]), ), BrenemanExperimentResult( - "Orange", (0.358, 0.545), (0.287, 0.550), (7, 3), (3, 0), (7, -6) + "Skin", + np.array([0.316, 0.526]), + np.array([0.253, 0.503]), + np.array([5, 3]), + np.array([-3, -2]), + np.array([-1, -3]), ), BrenemanExperimentResult( - "Brown", (0.350, 0.541), (0.282, 0.540), (6, 3), (-1, 0), (2, -5) + "Orange", + np.array([0.358, 0.545]), + np.array([0.287, 0.550]), + np.array([7, 3]), + np.array([3, 0]), + np.array([7, -6]), ), BrenemanExperimentResult( - "Yellow", (0.318, 0.551), (0.249, 0.556), (7, 2), (-1, 1), (2, -5) + "Brown", + np.array([0.350, 0.541]), + np.array([0.282, 0.540]), + np.array([6, 3]), + np.array([-1, 0]), + np.array([2, -5]), ), BrenemanExperimentResult( - "Foliage", (0.256, 0.547), (0.188, 0.537), (5, 4), (3, 1), (4, -2) + "Yellow", + np.array([0.318, 0.551]), + np.array([0.249, 0.556]), + np.array([7, 2]), + np.array([-1, 1]), + np.array([2, -5]), ), BrenemanExperimentResult( - "Green", (0.193, 0.542), (0.133, 0.520), (13, 3), (5, -2), (5, -4) + "Foliage", + np.array([0.256, 0.547]), + np.array([0.188, 0.537]), + np.array([5, 4]), + np.array([3, 1]), + np.array([4, -2]), ), BrenemanExperimentResult( - "Blue-green", (0.180, 0.516), (0.137, 0.466), (12, 10), (0, 0), (-2, 2) + "Green", + np.array([0.193, 0.542]), + np.array([0.133, 0.520]), + np.array([13, 3]), + np.array([5, -2]), + np.array([5, -4]), + ), + BrenemanExperimentResult( + "Blue-green", + np.array([0.180, 0.516]), + np.array([0.137, 0.466]), + np.array([12, 10]), + np.array([0, 0]), + np.array([-2, 2]), ), BrenemanExperimentResult( - "Blue", (0.186, 0.445), (0.156, 0.353), (12, 45), (6, 1), (2, 6) + "Blue", + np.array([0.186, 0.445]), + np.array([0.156, 0.353]), + np.array([12, 45]), + np.array([6, 1]), + np.array([2, 6]), ), BrenemanExperimentResult( - "Sky", (0.225, 0.492), (0.178, 0.428), (6, 14), (1, -1), (-1, 3) + "Sky", + np.array([0.225, 0.492]), + np.array([0.178, 0.428]), + np.array([6, 14]), + np.array([1, -1]), + np.array([-1, 3]), ), BrenemanExperimentResult( - "Purple", (0.276, 0.456), (0.227, 0.369), (6, 27), (-2, 4), (-3, 9) + "Purple", + np.array([0.276, 0.456]), + np.array([0.227, 0.369]), + np.array([6, 27]), + np.array([-2, 4]), + np.array([-3, 9]), ), ) """ @@ -441,18 +754,45 @@ def __new__( BRENEMAN_EXPERIMENT_7_RESULTS = ( - BrenemanExperimentResult("Gray", (0.208, 0.481), (0.211, 0.486), (2, 3)), - BrenemanExperimentResult("Red", (0.448, 0.512), (0.409, 0.516), (9, 2)), - BrenemanExperimentResult("Skin", (0.269, 0.505), (0.256, 0.506), (4, 3)), - BrenemanExperimentResult("Orange", (0.331, 0.549), (0.305, 0.547), (5, 4)), - BrenemanExperimentResult("Brown", (0.322, 0.541), (0.301, 0.539), (5, 2)), - BrenemanExperimentResult("Yellow", (0.268, 0.555), (0.257, 0.552), (3, 4)), - BrenemanExperimentResult("Foliage", (0.225, 0.538), (0.222, 0.536), (3, 2)), - BrenemanExperimentResult("Green", (0.135, 0.531), (0.153, 0.529), (8, 2)), - BrenemanExperimentResult("Blue-green", (0.145, 0.475), (0.160, 0.484), (3, 5)), - BrenemanExperimentResult("Blue", (0.163, 0.331), (0.171, 0.379), (4, 11)), - BrenemanExperimentResult("Sky", (0.179, 0.438), (0.187, 0.452), (4, 7)), - BrenemanExperimentResult("Purple", (0.245, 0.365), (0.240, 0.398), (4, 10)), + BrenemanExperimentResult( + "Gray", np.array([0.208, 0.481]), np.array([0.211, 0.486]), np.array([2, 3]) + ), + BrenemanExperimentResult( + "Red", np.array([0.448, 0.512]), np.array([0.409, 0.516]), np.array([9, 2]) + ), + BrenemanExperimentResult( + "Skin", np.array([0.269, 0.505]), np.array([0.256, 0.506]), np.array([4, 3]) + ), + BrenemanExperimentResult( + "Orange", np.array([0.331, 0.549]), np.array([0.305, 0.547]), np.array([5, 4]) + ), + BrenemanExperimentResult( + "Brown", np.array([0.322, 0.541]), np.array([0.301, 0.539]), np.array([5, 2]) + ), + BrenemanExperimentResult( + "Yellow", np.array([0.268, 0.555]), np.array([0.257, 0.552]), np.array([3, 4]) + ), + BrenemanExperimentResult( + "Foliage", np.array([0.225, 0.538]), np.array([0.222, 0.536]), np.array([3, 2]) + ), + BrenemanExperimentResult( + "Green", np.array([0.135, 0.531]), np.array([0.153, 0.529]), np.array([8, 2]) + ), + BrenemanExperimentResult( + "Blue-green", + np.array([0.145, 0.475]), + np.array([0.160, 0.484]), + np.array([3, 5]), + ), + BrenemanExperimentResult( + "Blue", np.array([0.163, 0.331]), np.array([0.171, 0.379]), np.array([4, 11]) + ), + BrenemanExperimentResult( + "Sky", np.array([0.179, 0.438]), np.array([0.187, 0.452]), np.array([4, 7]) + ), + BrenemanExperimentResult( + "Purple", np.array([0.245, 0.365]), np.array([0.240, 0.398]), np.array([4, 10]) + ), ) """ *Breneman (1987)* experiment 7 results. @@ -465,42 +805,104 @@ def __new__( BRENEMAN_EXPERIMENT_8_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.258, 0.524), (0.195, 0.469)), BrenemanExperimentResult( - "Gray", (0.257, 0.525), (0.200, 0.494), (2, 3), (1, 2), (0, 0) + "Illuminant", np.array([0.258, 0.524]), np.array([0.195, 0.469]) + ), + BrenemanExperimentResult( + "Gray", + np.array([0.257, 0.525]), + np.array([0.200, 0.494]), + np.array([2, 3]), + np.array([1, 2]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Red", (0.458, 0.522), (0.410, 0.508), (12, 4), (-3, 5), (-7, 2) + "Red", + np.array([0.458, 0.522]), + np.array([0.410, 0.508]), + np.array([12, 4]), + np.array([-3, 5]), + np.array([-7, 2]), ), BrenemanExperimentResult( - "Skin", (0.308, 0.526), (0.249, 0.502), (6, 2), (-1, 1), (-3, -1) + "Skin", + np.array([0.308, 0.526]), + np.array([0.249, 0.502]), + np.array([6, 2]), + np.array([-1, 1]), + np.array([-3, -1]), ), BrenemanExperimentResult( - "Orange", (0.359, 0.545), (0.299, 0.545), (12, 4), (0, -2), (-3, 0) + "Orange", + np.array([0.359, 0.545]), + np.array([0.299, 0.545]), + np.array([12, 4]), + np.array([0, -2]), + np.array([-3, 0]), ), BrenemanExperimentResult( - "Brown", (0.349, 0.540), (0.289, 0.532), (10, 4), (0, 1), (-2, 2) + "Brown", + np.array([0.349, 0.540]), + np.array([0.289, 0.532]), + np.array([10, 4]), + np.array([0, 1]), + np.array([-2, 2]), ), BrenemanExperimentResult( - "Yellow", (0.317, 0.550), (0.256, 0.549), (9, 5), (0, -3), (-3, 1) + "Yellow", + np.array([0.317, 0.550]), + np.array([0.256, 0.549]), + np.array([9, 5]), + np.array([0, -3]), + np.array([-3, 1]), ), BrenemanExperimentResult( - "Foliage", (0.260, 0.545), (0.198, 0.529), (5, 5), (3, 1), (0, 3) + "Foliage", + np.array([0.260, 0.545]), + np.array([0.198, 0.529]), + np.array([5, 5]), + np.array([3, 1]), + np.array([0, 3]), ), BrenemanExperimentResult( - "Green", (0.193, 0.543), (0.137, 0.520), (9, 5), (3, 0), (2, 1) + "Green", + np.array([0.193, 0.543]), + np.array([0.137, 0.520]), + np.array([9, 5]), + np.array([3, 0]), + np.array([2, 1]), ), BrenemanExperimentResult( - "Blue-green", (0.182, 0.516), (0.139, 0.477), (9, 4), (-3, 0), (-2, -4) + "Blue-green", + np.array([0.182, 0.516]), + np.array([0.139, 0.477]), + np.array([9, 4]), + np.array([-3, 0]), + np.array([-2, -4]), ), BrenemanExperimentResult( - "Blue", (0.184, 0.444), (0.150, 0.387), (5, 11), (3, -10), (6, -22) + "Blue", + np.array([0.184, 0.444]), + np.array([0.150, 0.387]), + np.array([5, 11]), + np.array([3, -10]), + np.array([6, -22]), ), BrenemanExperimentResult( - "Sky", (0.224, 0.489), (0.177, 0.439), (5, 6), (1, 1), (1, -7) + "Sky", + np.array([0.224, 0.489]), + np.array([0.177, 0.439]), + np.array([5, 6]), + np.array([1, 1]), + np.array([1, -7]), ), BrenemanExperimentResult( - "Purple", (0.277, 0.454), (0.226, 0.389), (4, 10), (1, 4), (1, -8) + "Purple", + np.array([0.277, 0.454]), + np.array([0.226, 0.389]), + np.array([4, 10]), + np.array([1, 4]), + np.array([1, -8]), ), ) """ @@ -515,61 +917,155 @@ def __new__( BRENEMAN_EXPERIMENT_9_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.254, 0.525), (0.195, 0.465)), BrenemanExperimentResult( - "Gray", (0.256, 0.524), (0.207, 0.496), (4, 6), (3, 2), (0, 0) + "Illuminant", np.array([0.254, 0.525]), np.array([0.195, 0.465]) ), BrenemanExperimentResult( - "Red", (0.459, 0.521), (0.415, 0.489), (20, 14), (2, 12), (-2, 21) + "Gray", + np.array([0.256, 0.524]), + np.array([0.207, 0.496]), + np.array([4, 6]), + np.array([3, 2]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Skin", (0.307, 0.525), (0.261, 0.500), (7, 7), (0, 1), (-5, 2) + "Red", + np.array([0.459, 0.521]), + np.array([0.415, 0.489]), + np.array([20, 14]), + np.array([2, 12]), + np.array([-2, 21]), ), BrenemanExperimentResult( - "Orange", (0.359, 0.545), (0.313, 0.532), (7, 5), (-2, -3), (-6, 13) + "Skin", + np.array([0.307, 0.525]), + np.array([0.261, 0.500]), + np.array([7, 7]), + np.array([0, 1]), + np.array([-5, 2]), ), BrenemanExperimentResult( - "Brown", (0.349, 0.540), (0.302, 0.510), (11, 15), (0, 12), (-5, 24) + "Orange", + np.array([0.359, 0.545]), + np.array([0.313, 0.532]), + np.array([7, 5]), + np.array([-2, -3]), + np.array([-6, 13]), ), BrenemanExperimentResult( - "Yellow", (0.317, 0.550), (0.268, 0.538), (7, 10), (1, -4), (-4, 12) + "Brown", + np.array([0.349, 0.540]), + np.array([0.302, 0.510]), + np.array([11, 15]), + np.array([0, 12]), + np.array([-5, 24]), ), BrenemanExperimentResult( - "Foliage", (0.259, 0.544), (0.212, 0.510), (10, 11), (0, 14), (-4, 22) + "Yellow", + np.array([0.317, 0.550]), + np.array([0.268, 0.538]), + np.array([7, 10]), + np.array([1, -4]), + np.array([-4, 12]), ), BrenemanExperimentResult( - "Green", (0.193, 0.542), (0.150, 0.506), (6, 10), (-1, 13), (-2, 15) + "Foliage", + np.array([0.259, 0.544]), + np.array([0.212, 0.510]), + np.array([10, 11]), + np.array([0, 14]), + np.array([-4, 22]), ), BrenemanExperimentResult( - "Blue-green", (0.181, 0.517), (0.144, 0.487), (9, 6), (-3, 0), (-1, -9) + "Green", + np.array([0.193, 0.542]), + np.array([0.150, 0.506]), + np.array([6, 10]), + np.array([-1, 13]), + np.array([-2, 15]), ), BrenemanExperimentResult( - "Blue", (0.184, 0.444), (0.155, 0.407), (4, 11), (-2, -6), (6, -36) + "Blue-green", + np.array([0.181, 0.517]), + np.array([0.144, 0.487]), + np.array([9, 6]), + np.array([-3, 0]), + np.array([-1, -9]), ), BrenemanExperimentResult( - "Sky", (0.225, 0.490), (0.183, 0.458), (5, 8), (1, -3), (2, -19) + "Blue", + np.array([0.184, 0.444]), + np.array([0.155, 0.407]), + np.array([4, 11]), + np.array([-2, -6]), + np.array([6, -36]), ), BrenemanExperimentResult( - "Purple", (0.276, 0.454), (0.233, 0.404), (7, 12), (2, 9), (0, -16) + "Sky", + np.array([0.225, 0.490]), + np.array([0.183, 0.458]), + np.array([5, 8]), + np.array([1, -3]), + np.array([2, -19]), ), - BrenemanExperimentResult("(Gray)h", (0.256, 0.525), (0.208, 0.498)), BrenemanExperimentResult( - "(Red)h", (0.456, 0.521), (0.416, 0.501), (15, 7), None, (-6, -9) + "Purple", + np.array([0.276, 0.454]), + np.array([0.233, 0.404]), + np.array([7, 12]), + np.array([2, 9]), + np.array([0, -16]), ), BrenemanExperimentResult( - "(Brown)h", (0.349, 0.539), (0.306, 0.526), (11, 8), None, (-8, 7) + "(Gray)h", np.array([0.256, 0.525]), np.array([0.208, 0.498]) ), BrenemanExperimentResult( - "(Foliage)h", (0.260, 0.545), (0.213, 0.528), (7, 9), None, (-4, 5) + "(Red)h", + np.array([0.456, 0.521]), + np.array([0.416, 0.501]), + np.array([15, 7]), + None, + np.array([-6, -9]), ), BrenemanExperimentResult( - "(Green)h", (0.193, 0.543), (0.149, 0.525), (10, 8), None, (-1, -1) + "(Brown)h", + np.array([0.349, 0.539]), + np.array([0.306, 0.526]), + np.array([11, 8]), + None, + np.array([-8, 7]), ), BrenemanExperimentResult( - "(Blue)h", (0.184, 0.444), (0.156, 0.419), (7, 8), None, (4, -45) + "(Foliage)h", + np.array([0.260, 0.545]), + np.array([0.213, 0.528]), + np.array([7, 9]), + None, + np.array([-4, 5]), ), BrenemanExperimentResult( - "(Purple)h", (0.277, 0.456), (0.236, 0.422), (6, 11), None, (-2, -29) + "(Green)h", + np.array([0.193, 0.543]), + np.array([0.149, 0.525]), + np.array([10, 8]), + None, + np.array([-1, -1]), + ), + BrenemanExperimentResult( + "(Blue)h", + np.array([0.184, 0.444]), + np.array([0.156, 0.419]), + np.array([7, 8]), + None, + np.array([4, -45]), + ), + BrenemanExperimentResult( + "(Purple)h", + np.array([0.277, 0.456]), + np.array([0.236, 0.422]), + np.array([6, 11]), + None, + np.array([-2, -29]), ), ) """ @@ -586,18 +1082,45 @@ def __new__( BRENEMAN_EXPERIMENT_10_RESULTS = ( - BrenemanExperimentResult("Gray", (0.208, 0.482), (0.213, 0.494), (3, 3)), - BrenemanExperimentResult("Red", (0.447, 0.512), (0.411, 0.506), (15, 7)), - BrenemanExperimentResult("Skin", (0.269, 0.505), (0.269, 0.511), (4, 3)), - BrenemanExperimentResult("Orange", (0.331, 0.549), (0.315, 0.536), (7, 8)), - BrenemanExperimentResult("Brown", (0.323, 0.542), (0.310, 0.526), (6, 8)), - BrenemanExperimentResult("Yellow", (0.268, 0.556), (0.268, 0.541), (3, 6)), - BrenemanExperimentResult("Foliage", (0.226, 0.538), (0.230, 0.525), (4, 8)), - BrenemanExperimentResult("Green", (0.135, 0.531), (0.158, 0.524), (6, 3)), - BrenemanExperimentResult("Blue-green", (0.145, 0.476), (0.161, 0.491), (4, 4)), - BrenemanExperimentResult("Blue", (0.163, 0.330), (0.171, 0.377), (6, 19)), - BrenemanExperimentResult("Sky", (0.179, 0.439), (0.187, 0.465), (5, 5)), - BrenemanExperimentResult("Purple", (0.245, 0.366), (0.240, 0.402), (3, 12)), + BrenemanExperimentResult( + "Gray", np.array([0.208, 0.482]), np.array([0.213, 0.494]), np.array([3, 3]) + ), + BrenemanExperimentResult( + "Red", np.array([0.447, 0.512]), np.array([0.411, 0.506]), np.array([15, 7]) + ), + BrenemanExperimentResult( + "Skin", np.array([0.269, 0.505]), np.array([0.269, 0.511]), np.array([4, 3]) + ), + BrenemanExperimentResult( + "Orange", np.array([0.331, 0.549]), np.array([0.315, 0.536]), np.array([7, 8]) + ), + BrenemanExperimentResult( + "Brown", np.array([0.323, 0.542]), np.array([0.310, 0.526]), np.array([6, 8]) + ), + BrenemanExperimentResult( + "Yellow", np.array([0.268, 0.556]), np.array([0.268, 0.541]), np.array([3, 6]) + ), + BrenemanExperimentResult( + "Foliage", np.array([0.226, 0.538]), np.array([0.230, 0.525]), np.array([4, 8]) + ), + BrenemanExperimentResult( + "Green", np.array([0.135, 0.531]), np.array([0.158, 0.524]), np.array([6, 3]) + ), + BrenemanExperimentResult( + "Blue-green", + np.array([0.145, 0.476]), + np.array([0.161, 0.491]), + np.array([4, 4]), + ), + BrenemanExperimentResult( + "Blue", np.array([0.163, 0.330]), np.array([0.171, 0.377]), np.array([6, 19]) + ), + BrenemanExperimentResult( + "Sky", np.array([0.179, 0.439]), np.array([0.187, 0.465]), np.array([5, 5]) + ), + BrenemanExperimentResult( + "Purple", np.array([0.245, 0.366]), np.array([0.240, 0.402]), np.array([3, 12]) + ), ) """ *Breneman (1987)* experiment 10 results. @@ -610,42 +1133,104 @@ def __new__( BRENEMAN_EXPERIMENT_11_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.208, 0.482), (0.174, 0.520)), BrenemanExperimentResult( - "Gray", (0.209, 0.483), (0.176, 0.513), (3, 4), (2, 2), (0, 0) + "Illuminant", np.array([0.208, 0.482]), np.array([0.174, 0.520]) + ), + BrenemanExperimentResult( + "Gray", + np.array([0.209, 0.483]), + np.array([0.176, 0.513]), + np.array([3, 4]), + np.array([2, 2]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Red", (0.450, 0.512), (0.419, 0.524), (10, 2), (3, 2), (8, -1) + "Red", + np.array([0.450, 0.512]), + np.array([0.419, 0.524]), + np.array([10, 2]), + np.array([3, 2]), + np.array([8, -1]), ), BrenemanExperimentResult( - "Skin", (0.268, 0.506), (0.240, 0.528), (6, 2), (-4, 0), (-3, 0) + "Skin", + np.array([0.268, 0.506]), + np.array([0.240, 0.528]), + np.array([6, 2]), + np.array([-4, 0]), + np.array([-3, 0]), ), BrenemanExperimentResult( - "Orange", (0.331, 0.547), (0.293, 0.553), (6, 2), (3, -1), (5, 1) + "Orange", + np.array([0.331, 0.547]), + np.array([0.293, 0.553]), + np.array([6, 2]), + np.array([3, -1]), + np.array([5, 1]), ), BrenemanExperimentResult( - "Brown", (0.323, 0.542), (0.290, 0.552), (5, 2), (-1, -3), (0, -1) + "Brown", + np.array([0.323, 0.542]), + np.array([0.290, 0.552]), + np.array([5, 2]), + np.array([-1, -3]), + np.array([0, -1]), ), BrenemanExperimentResult( - "Yellow", (0.266, 0.549), (0.236, 0.557), (4, 2), (-3, -2), (-4, 2) + "Yellow", + np.array([0.266, 0.549]), + np.array([0.236, 0.557]), + np.array([4, 2]), + np.array([-3, -2]), + np.array([-4, 2]), ), BrenemanExperimentResult( - "Foliage", (0.227, 0.538), (0.194, 0.552), (4, 2), (2, -3), (-1, 1) + "Foliage", + np.array([0.227, 0.538]), + np.array([0.194, 0.552]), + np.array([4, 2]), + np.array([2, -3]), + np.array([-1, 1]), ), BrenemanExperimentResult( - "Green", (0.146, 0.534), (0.118, 0.551), (8, 3), (4, -2), (-6, 3) + "Green", + np.array([0.146, 0.534]), + np.array([0.118, 0.551]), + np.array([8, 3]), + np.array([4, -2]), + np.array([-6, 3]), ), BrenemanExperimentResult( - "Blue-green", (0.160, 0.475), (0.130, 0.513), (9, 4), (1, -1), (-4, -3) + "Blue-green", + np.array([0.160, 0.475]), + np.array([0.130, 0.513]), + np.array([9, 4]), + np.array([1, -1]), + np.array([-4, -3]), ), BrenemanExperimentResult( - "Blue", (0.177, 0.340), (0.133, 0.427), (6, 14), (4, -17), (11, -29) + "Blue", + np.array([0.177, 0.340]), + np.array([0.133, 0.427]), + np.array([6, 14]), + np.array([4, -17]), + np.array([11, -29]), ), BrenemanExperimentResult( - "Sky", (0.179, 0.438), (0.146, 0.482), (6, 10), (1, 4), (0, -1) + "Sky", + np.array([0.179, 0.438]), + np.array([0.146, 0.482]), + np.array([6, 10]), + np.array([1, 4]), + np.array([0, -1]), ), BrenemanExperimentResult( - "Purple", (0.245, 0.366), (0.216, 0.419), (4, 13), (-3, 8), (4, -2) + "Purple", + np.array([0.245, 0.366]), + np.array([0.216, 0.419]), + np.array([4, 13]), + np.array([-3, 8]), + np.array([4, -2]), ), ) """ @@ -660,42 +1245,104 @@ def __new__( BRENEMAN_EXPERIMENT_12_RESULTS = ( - BrenemanExperimentResult("Illuminant", (0.205, 0.482), (0.174, 0.519)), BrenemanExperimentResult( - "Gray", (0.208, 0.482), (0.181, 0.507), (4, 3), (0, 1), (0, 0) + "Illuminant", np.array([0.205, 0.482]), np.array([0.174, 0.519]) ), BrenemanExperimentResult( - "Red", (0.451, 0.512), (0.422, 0.526), (20, 3), (0, -5), (10, -5) + "Gray", + np.array([0.208, 0.482]), + np.array([0.181, 0.507]), + np.array([4, 3]), + np.array([0, 1]), + np.array([0, 0]), ), BrenemanExperimentResult( - "Skin", (0.268, 0.506), (0.244, 0.525), (5, 2), (-6, 0), (-2, -1) + "Red", + np.array([0.451, 0.512]), + np.array([0.422, 0.526]), + np.array([20, 3]), + np.array([0, -5]), + np.array([10, -5]), ), BrenemanExperimentResult( - "Orange", (0.331, 0.548), (0.292, 0.553), (10, 2), (5, 2), (11, 1) + "Skin", + np.array([0.268, 0.506]), + np.array([0.244, 0.525]), + np.array([5, 2]), + np.array([-6, 0]), + np.array([-2, -1]), ), BrenemanExperimentResult( - "Brown", (0.324, 0.542), (0.286, 0.554), (8, 1), (5, -3), (10, -4) + "Orange", + np.array([0.331, 0.548]), + np.array([0.292, 0.553]), + np.array([10, 2]), + np.array([5, 2]), + np.array([11, 1]), ), BrenemanExperimentResult( - "Yellow", (0.266, 0.548), (0.238, 0.558), (6, 2), (-3, -1), (-1, -2) + "Brown", + np.array([0.324, 0.542]), + np.array([0.286, 0.554]), + np.array([8, 1]), + np.array([5, -3]), + np.array([10, -4]), ), BrenemanExperimentResult( - "Foliage", (0.227, 0.538), (0.196, 0.555), (6, 3), (3, -4), (2, -5) + "Yellow", + np.array([0.266, 0.548]), + np.array([0.238, 0.558]), + np.array([6, 2]), + np.array([-3, -1]), + np.array([-1, -2]), ), BrenemanExperimentResult( - "Green", (0.145, 0.534), (0.124, 0.551), (8, 6), (1, -1), (-8, -1) + "Foliage", + np.array([0.227, 0.538]), + np.array([0.196, 0.555]), + np.array([6, 3]), + np.array([3, -4]), + np.array([2, -5]), ), BrenemanExperimentResult( - "Blue-green", (0.160, 0.474), (0.135, 0.505), (5, 2), (1, -1), (-4, -3) + "Green", + np.array([0.145, 0.534]), + np.array([0.124, 0.551]), + np.array([8, 6]), + np.array([1, -1]), + np.array([-8, -1]), ), BrenemanExperimentResult( - "Blue", (0.178, 0.339), (0.149, 0.392), (4, 20), (-1, -5), (3, -7) + "Blue-green", + np.array([0.160, 0.474]), + np.array([0.135, 0.505]), + np.array([5, 2]), + np.array([1, -1]), + np.array([-4, -3]), ), BrenemanExperimentResult( - "Sky", (0.179, 0.440), (0.150, 0.473), (4, 8), (3, 2), (2, 0) + "Blue", + np.array([0.178, 0.339]), + np.array([0.149, 0.392]), + np.array([4, 20]), + np.array([-1, -5]), + np.array([3, -7]), ), BrenemanExperimentResult( - "Purple", (0.246, 0.366), (0.222, 0.404), (5, 15), (-4, 2), (4, 2) + "Sky", + np.array([0.179, 0.440]), + np.array([0.150, 0.473]), + np.array([4, 8]), + np.array([3, 2]), + np.array([2, 0]), + ), + BrenemanExperimentResult( + "Purple", + np.array([0.246, 0.366]), + np.array([0.222, 0.404]), + np.array([5, 15]), + np.array([-4, 2]), + np.array([4, 2]), ), ) """ @@ -711,51 +1358,76 @@ def __new__( BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES = { 1: PrimariesChromaticityCoordinates( - 1, ("A", "D65"), 1500, (0.671, 0.519), (-0.586, 0.627), (0.253, 0.016) + 1, + ("A", "D65"), + 1500, + np.array([0.671, 0.519]), + np.array([-0.586, 0.627]), + np.array([0.253, 0.016]), ), 2: PrimariesChromaticityCoordinates( 2, ("Projector", "D55"), 1500, - (0.675, 0.523), - (-0.466, 0.617), - (0.255, 0.018), + np.array([0.675, 0.523]), + np.array([-0.466, 0.617]), + np.array([0.255, 0.018]), ), 3: PrimariesChromaticityCoordinates( 3, ("Projector", "D55"), 75, - (0.664, 0.510), - (-0.256, 0.729), - (0.244, 0.003), + np.array([0.664, 0.510]), + np.array([-0.256, 0.729]), + np.array([0.244, 0.003]), ), 4: PrimariesChromaticityCoordinates( - 4, ("A", "D65"), 75, (0.674, 0.524), (-0.172, 0.628), (0.218, -0.026) + 4, + ("A", "D65"), + 75, + np.array([0.674, 0.524]), + np.array([-0.172, 0.628]), + np.array([0.218, -0.026]), ), 6: PrimariesChromaticityCoordinates( - 6, ("A", "D55"), 11100, (0.659, 0.506), (-0.141, 0.615), (0.249, 0.009) + 6, + ("A", "D55"), + 11100, + np.array([0.659, 0.506]), + np.array([-0.141, 0.615]), + np.array([0.249, 0.009]), ), 8: PrimariesChromaticityCoordinates( - 8, ("A", "D65"), 350, (0.659, 0.505), (-0.246, 0.672), (0.235, -0.006) + 8, + ("A", "D65"), + 350, + np.array([0.659, 0.505]), + np.array([-0.246, 0.672]), + np.array([0.235, -0.006]), ), 9: PrimariesChromaticityCoordinates( - 9, ("A", "D65"), 15, (0.693, 0.546), (-0.446, 0.773), (0.221, -0.023) + 9, + ("A", "D65"), + 15, + np.array([0.693, 0.546]), + np.array([-0.446, 0.773]), + np.array([0.221, -0.023]), ), 11: PrimariesChromaticityCoordinates( 11, ("D55", "green"), 1560, - (0.680, 0.529), - (0.018, 0.576), - (0.307, 0.080), + np.array([0.680, 0.529]), + np.array([0.018, 0.576]), + np.array([0.307, 0.080]), ), 12: PrimariesChromaticityCoordinates( 12, ("D55", "green"), 75, - (0.661, 0.505), - (0.039, 0.598), - (0.345, 0.127), + np.array([0.661, 0.505]), + np.array([0.039, 0.598]), + np.array([0.345, 0.127]), ), } if is_documentation_building(): # pragma: no cover diff --git a/colour/corresponding/prediction.py b/colour/corresponding/prediction.py index 95613adc4..c21722846 100644 --- a/colour/corresponding/prediction.py +++ b/colour/corresponding/prediction.py @@ -38,7 +38,7 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass import numpy as np @@ -58,6 +58,7 @@ ArrayLike, Literal, LiteralChromaticAdaptationTransform, + NDArrayFloat, Tuple, ) from colour.models import ( @@ -70,6 +71,8 @@ ) from colour.utilities import ( CanonicalMapping, + MixinDataclassIterable, + as_float_array, as_float_scalar, attest, domain_range_scale, @@ -98,23 +101,8 @@ ] -class CorrespondingColourDataset( - namedtuple( - "CorrespondingColourDataset", - ( - "name", - "XYZ_r", - "XYZ_t", - "XYZ_cr", - "XYZ_ct", - "Y_r", - "Y_t", - "B_r", - "B_t", - "metadata", - ), - ) -): +@dataclass(frozen=True) +class CorrespondingColourDataset(MixinDataclassIterable): """ Define a corresponding colour dataset. @@ -154,13 +142,28 @@ class CorrespondingColourDataset( :cite:`Luo1999` """ + name: int + XYZ_r: NDArrayFloat + XYZ_t: NDArrayFloat + XYZ_cr: NDArrayFloat + XYZ_ct: NDArrayFloat + Y_r: float + Y_t: float + B_r: float + B_t: float + metadata: dict -class CorrespondingChromaticitiesPrediction( - namedtuple( - "CorrespondingChromaticitiesPrediction", - ("name", "uv_t", "uv_m", "uv_p"), - ) -): + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "XYZ_r", as_float_array(self.XYZ_r)) + object.__setattr__(self, "XYZ_t", as_float_array(self.XYZ_t)) + object.__setattr__(self, "XYZ_cr", as_float_array(self.XYZ_cr)) + object.__setattr__(self, "XYZ_ct", as_float_array(self.XYZ_ct)) + + +@dataclass(frozen=True) +class CorrespondingChromaticitiesPrediction(MixinDataclassIterable): """ Define a chromatic adaptation model prediction. @@ -176,6 +179,18 @@ class CorrespondingChromaticitiesPrediction( Chromaticity coordinates :math:`uv_p^p` of predicted colour. """ + name: int + uv_t: NDArrayFloat + uv_m: NDArrayFloat + uv_p: NDArrayFloat + + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "uv_t", as_float_array(self.uv_t)) + object.__setattr__(self, "uv_m", as_float_array(self.uv_m)) + object.__setattr__(self, "uv_p", as_float_array(self.uv_p)) + def convert_experiment_results_Breneman1987( experiment: Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], @@ -197,7 +212,7 @@ def convert_experiment_results_Breneman1987( Examples -------- >>> from pprint import pprint - >>> pprint(tuple(convert_experiment_results_Breneman1987(2))) + >>> pprint(tuple(convert_experiment_results_Breneman1987(2).values)) ... # doctest: +ELLIPSIS (2, array([ 0.9582463..., 1. , 0.9436325...]), @@ -266,7 +281,7 @@ def convert_experiment_results_Breneman1987( xy_to_XYZ( np.hstack( [ - Luv_uv_to_xy(illuminant_chromaticities[1:3]), + Luv_uv_to_xy(illuminant_chromaticities.values[1:3]), full((2, 1), Y_r), ] ) @@ -279,7 +294,7 @@ def convert_experiment_results_Breneman1987( xyY_cr.append( np.hstack( [ - Luv_uv_to_xy(experiment_result[2]), + Luv_uv_to_xy(experiment_result.values[2]), samples_luminance[i] * Y_r, ] ) @@ -287,7 +302,7 @@ def convert_experiment_results_Breneman1987( xyY_ct.append( np.hstack( [ - Luv_uv_to_xy(experiment_result[1]), + Luv_uv_to_xy(experiment_result.values[1]), samples_luminance[i] * Y_t, ] ) diff --git a/colour/corresponding/tests/test_prediction.py b/colour/corresponding/tests/test_prediction.py index d8dc12141..50d654684 100644 --- a/colour/corresponding/tests/test_prediction.py +++ b/colour/corresponding/tests/test_prediction.py @@ -123,8 +123,8 @@ ], ] ), - Y_r=np.array(1500), - Y_t=np.array(1500), + Y_r=1500, + Y_t=1500, B_r=0.3, B_t=0.3, metadata={}, diff --git a/colour/difference/cam02_ucs.py b/colour/difference/cam02_ucs.py index 2e88deaf1..f91063766 100644 --- a/colour/difference/cam02_ucs.py +++ b/colour/difference/cam02_ucs.py @@ -21,7 +21,7 @@ import numpy as np from colour.hints import ArrayLike, NDArrayFloat -from colour.models.cam02_ucs import COEFFICIENTS_UCS_LUO2006 +from colour.models.cam02_ucs import COEFFICIENTS_UCS_LUO2006, Coefficients_UCS_Luo2006 from colour.utilities import as_float, tsplit __author__ = "Colour Developers" @@ -40,7 +40,7 @@ def delta_E_Luo2006( - Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike, coefficients: ArrayLike + Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike, coefficients: Coefficients_UCS_Luo2006 ) -> NDArrayFloat: """ Return the difference :math:`\\Delta E'` between two given @@ -99,7 +99,7 @@ def delta_E_Luo2006( J_p_1, a_p_1, b_p_1 = tsplit(Jpapbp_1) J_p_2, a_p_2, b_p_2 = tsplit(Jpapbp_2) - K_L, _c_1, _c_2 = tsplit(coefficients) + K_L, _c_1, _c_2 = coefficients.values d_E = np.sqrt( ((J_p_1 - J_p_2) / K_L) ** 2 + (a_p_1 - a_p_2) ** 2 + (b_p_1 - b_p_2) ** 2 diff --git a/colour/graph/conversion.py b/colour/graph/conversion.py index b791f14cb..34b03609b 100644 --- a/colour/graph/conversion.py +++ b/colour/graph/conversion.py @@ -15,8 +15,8 @@ import re import sys import textwrap -from collections import namedtuple from copy import copy +from dataclasses import dataclass from functools import partial from pprint import pformat @@ -226,9 +226,8 @@ ] -class Conversion_Specification( - namedtuple("Conversion_Specification", ("source", "target", "conversion_function")) -): +@dataclass(frozen=True) +class Conversion_Specification: """ Conversion specification for *Colour* graph for automatic colour conversion describing two nodes and the edge in the graph. @@ -243,15 +242,15 @@ class Conversion_Specification( Callable converting from the ``source`` node to the ``target`` node. """ - def __new__( - cls, source: str, target: str, conversion_function: Callable - ) -> Conversion_Specification: - """ - Return a new instance of the - :class:`colour.graph.conversion.Conversion_Specification` class. - """ + source: str + target: str + conversion_function: Callable - return super().__new__(cls, source.lower(), target.lower(), conversion_function) + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "source", self.source.lower()) + object.__setattr__(self, "target", self.target.lower()) def CIECAM02_to_JMh_CIECAM02( diff --git a/colour/io/luts/lut.py b/colour/io/luts/lut.py index bd6359249..30c5c7216 100644 --- a/colour/io/luts/lut.py +++ b/colour/io/luts/lut.py @@ -42,6 +42,7 @@ List, Literal, NDArrayFloat, + Self, Sequence, Type, cast, @@ -357,7 +358,7 @@ def __repr__(self) -> str: return multiline_repr(self, attributes) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the *LUT* is equal to given other object. @@ -379,7 +380,7 @@ def __eq__(self, other: Any) -> bool: ] ) - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the *LUT* is not equal to given other object. @@ -413,7 +414,7 @@ def __add__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "+") - def __iadd__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __iadd__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for in-place addition. @@ -430,7 +431,7 @@ def __iadd__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "+", True) - def __sub__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __sub__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for subtraction. @@ -447,7 +448,7 @@ def __sub__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "-") - def __isub__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __isub__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for in-place subtraction. @@ -464,7 +465,7 @@ def __isub__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "-", True) - def __mul__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __mul__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for multiplication. @@ -481,7 +482,7 @@ def __mul__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "*") - def __imul__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __imul__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for in-place multiplication. @@ -498,7 +499,7 @@ def __imul__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "*", True) - def __div__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __div__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for division. @@ -515,7 +516,7 @@ def __div__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "/") - def __idiv__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __idiv__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for in-place division. @@ -535,7 +536,7 @@ def __idiv__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: __itruediv__ = __idiv__ __truediv__ = __div__ - def __pow__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __pow__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for exponentiation. @@ -552,7 +553,7 @@ def __pow__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: return self.arithmetical_operation(a, "**") - def __ipow__(self, a: ArrayLike | AbstractLUT) -> AbstractLUT: + def __ipow__(self, a: ArrayLike | AbstractLUT) -> Self: """ Implement support for in-place exponentiation. @@ -574,7 +575,7 @@ def arithmetical_operation( a: ArrayLike | AbstractLUT, operation: Literal["+", "-", "*", "/", "**"], in_place: bool = False, - ) -> AbstractLUT: + ) -> Self: """ Perform given arithmetical operation with :math:`a` operand, the operation can be either performed on a copy or in-place, must be diff --git a/colour/io/luts/operator.py b/colour/io/luts/operator.py index 3db8f4884..d8a72717b 100644 --- a/colour/io/luts/operator.py +++ b/colour/io/luts/operator.py @@ -411,7 +411,7 @@ def __repr__(self) -> str: ] ) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the *LUT* operator is equal to given other object. @@ -438,7 +438,7 @@ def __eq__(self, other: Any) -> bool: ] ) - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the *LUT* operator is not equal to given other object. diff --git a/colour/io/luts/sequence.py b/colour/io/luts/sequence.py index 9e6a576db..9b1bc510a 100644 --- a/colour/io/luts/sequence.py +++ b/colour/io/luts/sequence.py @@ -261,7 +261,7 @@ def __repr__(self) -> str: return f"{self.__class__.__name__}(\n{operations}\n)" - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the *LUT* sequence is equal to given other object. @@ -284,7 +284,7 @@ def __eq__(self, other: Any) -> bool: return all(self[i] == other[i] for i in range(len(self))) - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the *LUT* sequence is not equal to given other object. diff --git a/colour/io/tm2714.py b/colour/io/tm2714.py index de2457a17..2c3f066d0 100644 --- a/colour/io/tm2714.py +++ b/colour/io/tm2714.py @@ -687,7 +687,7 @@ def __hash__(self) -> int: ) ) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the header is equal to given other object. @@ -727,7 +727,7 @@ def __eq__(self, other: Any) -> bool: ) return False - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the header is not equal to given other object. diff --git a/colour/models/cam02_ucs.py b/colour/models/cam02_ucs.py index aae251777..9c9110a57 100644 --- a/colour/models/cam02_ucs.py +++ b/colour/models/cam02_ucs.py @@ -27,7 +27,7 @@ from __future__ import annotations -from collections import namedtuple +from dataclasses import dataclass import numpy as np @@ -35,6 +35,7 @@ from colour.hints import Any, ArrayLike, NDArrayFloat, cast from colour.utilities import ( CanonicalMapping, + MixinDataclassIterable, as_float_array, from_range_100, from_range_degrees, @@ -74,14 +75,17 @@ ] -class Coefficients_UCS_Luo2006( - namedtuple("Coefficients_UCS_Luo2006", ("K_L", "c_1", "c_2")) -): +@dataclass(frozen=True) +class Coefficients_UCS_Luo2006(MixinDataclassIterable): """ Define the class storing *Luo et al. (2006)* fitting coefficients for the *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* colourspaces. """ + K_L: float + c_1: float + c_2: float + COEFFICIENTS_UCS_LUO2006: CanonicalMapping = CanonicalMapping( { @@ -97,7 +101,7 @@ class Coefficients_UCS_Luo2006( def JMh_CIECAM02_to_UCS_Luo2006( - JMh: ArrayLike, coefficients: ArrayLike + JMh: ArrayLike, coefficients: Coefficients_UCS_Luo2006 ) -> NDArrayFloat: """ Convert from *CIECAM02* :math:`JMh` correlates array to one of the @@ -168,7 +172,7 @@ def JMh_CIECAM02_to_UCS_Luo2006( M = to_domain_100(M) h = to_domain_degrees(h) - _K_L, c_1, c_2 = tsplit(coefficients) + _K_L, c_1, c_2 = coefficients.values J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J) M_p = (1 / c_2) * np.log1p(c_2 * M) @@ -181,7 +185,7 @@ def JMh_CIECAM02_to_UCS_Luo2006( def UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp: ArrayLike, coefficients: ArrayLike + Jpapbp: ArrayLike, coefficients: Coefficients_UCS_Luo2006 ) -> NDArrayFloat: """ Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or @@ -233,7 +237,7 @@ def UCS_Luo2006_to_JMh_CIECAM02( """ J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp)) - _K_L, c_1, c_2 = tsplit(coefficients) + _K_L, c_1, c_2 = coefficients.values J = -J_p / (c_1 * J_p - 1 - 100 * c_1) @@ -611,7 +615,7 @@ def CAM02UCS_to_JMh_CIECAM02(Jpapbp: ArrayLike) -> NDArrayFloat: def XYZ_to_UCS_Luo2006( - XYZ: ArrayLike, coefficients: ArrayLike, **kwargs: Any + XYZ: ArrayLike, coefficients: Coefficients_UCS_Luo2006, **kwargs: Any ) -> NDArrayFloat: """ Convert from *CIE XYZ* tristimulus values to one of the @@ -698,7 +702,7 @@ def XYZ_to_UCS_Luo2006( def UCS_Luo2006_to_XYZ( - Jpapbp: ArrayLike, coefficients: ArrayLike, **kwargs: Any + Jpapbp: ArrayLike, coefficients: Coefficients_UCS_Luo2006, **kwargs: Any ) -> NDArrayFloat: """ Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or diff --git a/colour/plotting/corresponding.py b/colour/plotting/corresponding.py index 2cf8675df..23445ecea 100644 --- a/colour/plotting/corresponding.py +++ b/colour/plotting/corresponding.py @@ -123,7 +123,7 @@ def plot_corresponding_chromaticities_prediction( ) for result in results: - _name, uv_t, uv_m, uv_p = result + _name, uv_t, uv_m, uv_p = result.values axes.arrow( uv_t[0], uv_t[1], diff --git a/colour/utilities/array.py b/colour/utilities/array.py index 523929e24..57ddb8e41 100644 --- a/colour/utilities/array.py +++ b/colour/utilities/array.py @@ -44,6 +44,7 @@ NDArrayFloat, NDArrayInt, Real, + Self, Sequence, Type, cast, @@ -311,7 +312,7 @@ class MixinDataclassArithmetic(MixinDataclassArray): - :class:`colour.utilities.MixinDataclassFields` """ - def __add__(self, a: Any) -> Dataclass: + def __add__(self, a: Any) -> Self: """ Implement support for addition. @@ -328,7 +329,7 @@ def __add__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "+") - def __iadd__(self, a: Any) -> Dataclass: + def __iadd__(self, a: Any) -> Self: """ Implement support for in-place addition. @@ -345,7 +346,7 @@ def __iadd__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "+", True) - def __sub__(self, a: Any) -> Dataclass: + def __sub__(self, a: Any) -> Self: """ Implement support for subtraction. @@ -362,7 +363,7 @@ def __sub__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "-") - def __isub__(self, a: Any) -> Dataclass: + def __isub__(self, a: Any) -> Self: """ Implement support for in-place subtraction. @@ -379,7 +380,7 @@ def __isub__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "-", True) - def __mul__(self, a: Any) -> Dataclass: + def __mul__(self, a: Any) -> Self: """ Implement support for multiplication. @@ -396,7 +397,7 @@ def __mul__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "*") - def __imul__(self, a: Any) -> Dataclass: + def __imul__(self, a: Any) -> Self: """ Implement support for in-place multiplication. @@ -413,7 +414,7 @@ def __imul__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "*", True) - def __div__(self, a: Any) -> Dataclass: + def __div__(self, a: Any) -> Self: """ Implement support for division. @@ -430,7 +431,7 @@ def __div__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "/") - def __idiv__(self, a: Any) -> Dataclass: + def __idiv__(self, a: Any) -> Self: """ Implement support for in-place division. @@ -450,7 +451,7 @@ def __idiv__(self, a: Any) -> Dataclass: __itruediv__ = __idiv__ __truediv__ = __div__ - def __pow__(self, a: Any) -> Dataclass: + def __pow__(self, a: Any) -> Self: """ Implement support for exponentiation. @@ -467,7 +468,7 @@ def __pow__(self, a: Any) -> Dataclass: return self.arithmetical_operation(a, "**") - def __ipow__(self, a: Any) -> Dataclass: + def __ipow__(self, a: Any) -> Self: """ Implement support for in-place exponentiation. @@ -1052,7 +1053,7 @@ def __init__( self._scale = scale self._previous_scale = get_domain_range_scale() - def __enter__(self) -> domain_range_scale: + def __enter__(self) -> Self: """Set the new domain-range scale upon entering the context manager.""" set_domain_range_scale(self._scale) @@ -1825,7 +1826,7 @@ def __init__(self, enable: bool) -> None: self._enable = enable self._previous_state = is_ndarray_copy_enabled() - def __enter__(self) -> ndarray_copy_enable: + def __enter__(self) -> Self: """ Set the *Colour* :class:`numpy.ndarray` copy enabled state upon entering the context manager. diff --git a/colour/utilities/common.py b/colour/utilities/common.py index a2af44db0..5a5054b40 100644 --- a/colour/utilities/common.py +++ b/colour/utilities/common.py @@ -40,6 +40,7 @@ Iterable, Literal, Mapping, + Self, Sequence, TypeVar, ) @@ -149,7 +150,7 @@ def __init__(self, enable: bool) -> None: self._enable = enable self._previous_state = is_caching_enabled() - def __enter__(self) -> caching_enable: + def __enter__(self) -> Self: """ Set the *Colour* caching enabled state upon entering the context manager. @@ -500,7 +501,7 @@ class disable_multiprocessing: multiprocessing state. """ - def __enter__(self) -> disable_multiprocessing: + def __enter__(self) -> Self: """ Disable *Colour* multiprocessing state upon entering the context manager. diff --git a/colour/utilities/deprecation.py b/colour/utilities/deprecation.py index c1670831d..4fd243267 100644 --- a/colour/utilities/deprecation.py +++ b/colour/utilities/deprecation.py @@ -8,12 +8,12 @@ from __future__ import annotations import sys -from collections import namedtuple +from dataclasses import dataclass from importlib import import_module from operator import attrgetter from colour.hints import Any, ModuleType -from colour.utilities import attest, optional, usage_warning +from colour.utilities import MixinDataclassIterable, attest, optional, usage_warning __author__ = "Colour Developers" __copyright__ = "Copyright 2013 Colour Developers" @@ -40,7 +40,8 @@ ] -class ObjectRenamed(namedtuple("ObjectRenamed", ("name", "new_name"))): +@dataclass(frozen=True) +class ObjectRenamed(MixinDataclassIterable): """ A class used for an object that has been renamed. @@ -52,6 +53,9 @@ class ObjectRenamed(namedtuple("ObjectRenamed", ("name", "new_name"))): Object new name. """ + name: str + new_name: str + def __str__(self) -> str: """ Return a formatted string representation of the class. @@ -65,7 +69,8 @@ def __str__(self) -> str: return f'"{self.name}" object has been renamed to "{self.new_name}".' -class ObjectRemoved(namedtuple("ObjectRemoved", ("name",))): +@dataclass(frozen=True) +class ObjectRemoved(MixinDataclassIterable): """ A class used for an object that has been removed. @@ -75,6 +80,8 @@ class ObjectRemoved(namedtuple("ObjectRemoved", ("name",))): Object name that has been removed. """ + name: str + def __str__(self) -> str: """ Return a formatted string representation of the class. @@ -88,7 +95,8 @@ def __str__(self) -> str: return f'"{self.name}" object has been removed from the API.' -class ObjectFutureRename(namedtuple("ObjectFutureRename", ("name", "new_name"))): +@dataclass(frozen=True) +class ObjectFutureRename(MixinDataclassIterable): """ A class used for future object name deprecation, i.e., object name will change in a future release. @@ -101,6 +109,9 @@ class ObjectFutureRename(namedtuple("ObjectFutureRename", ("name", "new_name"))) Object future release name. """ + name: str + new_name: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -117,7 +128,8 @@ def __str__(self) -> str: ) -class ObjectFutureRemove(namedtuple("ObjectFutureRemove", ("name",))): +@dataclass(frozen=True) +class ObjectFutureRemove(MixinDataclassIterable): """ A class used for future object removal. @@ -127,6 +139,8 @@ class ObjectFutureRemove(namedtuple("ObjectFutureRemove", ("name",))): Object name that will be removed in a future release. """ + name: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -143,9 +157,8 @@ def __str__(self) -> str: ) -class ObjectFutureAccessChange( - namedtuple("ObjectFutureAccessChange", ("access", "new_access")) -): +@dataclass(frozen=True) +class ObjectFutureAccessChange(MixinDataclassIterable): """ A class used for future object access deprecation, i.e., object access will change in a future release. @@ -158,6 +171,9 @@ class ObjectFutureAccessChange( Object future release access. """ + access: str + new_access: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -174,7 +190,8 @@ def __str__(self) -> str: ) -class ObjectFutureAccessRemove(namedtuple("ObjectFutureAccessRemove", ("name",))): +@dataclass(frozen=True) +class ObjectFutureAccessRemove(MixinDataclassIterable): """ A class used for future object access removal, i.e., object access will be removed in a future release. @@ -185,6 +202,8 @@ class ObjectFutureAccessRemove(namedtuple("ObjectFutureAccessRemove", ("name",)) Object name whose access will removed in a future release. """ + name: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -198,7 +217,8 @@ def __str__(self) -> str: return f'"{self.name}" object access will be removed in a future release.' -class ArgumentRenamed(namedtuple("ArgumentRenamed", ("name", "new_name"))): +@dataclass(frozen=True) +class ArgumentRenamed(MixinDataclassIterable): """ A class used for an argument that has been renamed. @@ -210,6 +230,9 @@ class ArgumentRenamed(namedtuple("ArgumentRenamed", ("name", "new_name"))): Argument new name. """ + name: str + new_name: str + def __str__(self) -> str: """ Return a formatted string representation of the class. @@ -223,7 +246,8 @@ def __str__(self) -> str: return f'"{self.name}" argument has been renamed to "{self.new_name}".' -class ArgumentRemoved(namedtuple("ArgumentRemoved", ("name",))): +@dataclass(frozen=True) +class ArgumentRemoved(MixinDataclassIterable): """ A class used for an argument that has been removed. @@ -233,6 +257,8 @@ class ArgumentRemoved(namedtuple("ArgumentRemoved", ("name",))): Argument name that has been removed. """ + name: str + def __str__(self) -> str: """ Return a formatted string representation of the class. @@ -246,7 +272,8 @@ def __str__(self) -> str: return f'"{self.name}" argument has been removed from the API.' -class ArgumentFutureRename(namedtuple("ArgumentFutureRename", ("name", "new_name"))): +@dataclass(frozen=True) +class ArgumentFutureRename(MixinDataclassIterable): """ A class used for future argument name deprecation, i.e., argument name will change in a future release. @@ -259,6 +286,9 @@ class ArgumentFutureRename(namedtuple("ArgumentFutureRename", ("name", "new_name Argument future release name. """ + name: str + new_name: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -275,7 +305,8 @@ def __str__(self) -> str: ) -class ArgumentFutureRemove(namedtuple("ArgumentFutureRemove", ("name",))): +@dataclass(frozen=True) +class ArgumentFutureRemove(MixinDataclassIterable): """ A class used for future argument removal. @@ -285,6 +316,8 @@ class ArgumentFutureRemove(namedtuple("ArgumentFutureRemove", ("name",))): Argument name that will be removed in a future release. """ + name: str + def __str__(self) -> str: """ Return a formatted string representation of the deprecation type. @@ -357,8 +390,9 @@ def __getattr__(self, attribute: str) -> Any: return ( getattr(self._module, attribute) if isinstance(change, ObjectFutureRemove) - else get_attribute(change[1]) + else get_attribute(change.values[1]) ) + raise AttributeError(str(change)) return getattr(self._module, attribute) @@ -554,7 +588,7 @@ def handle_arguments_deprecation(changes: dict, **kwargs: Any) -> dict: if isinstance(change, ArgumentFutureRemove): continue - kwargs[change[1]] = kwargs.pop(kwarg) + kwargs[change.values[1]] = kwargs.pop(kwarg) else: kwargs.pop(kwarg) usage_warning(str(change)) diff --git a/colour/utilities/structures.py b/colour/utilities/structures.py index a2226b6ad..278c4f210 100644 --- a/colour/utilities/structures.py +++ b/colour/utilities/structures.py @@ -508,7 +508,7 @@ def __len__(self) -> int: return len(self._data) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """ Return whether the delimiter and case-insensitive :class:`dict`-like object is equal to given other object. @@ -538,7 +538,7 @@ def __eq__(self, other: Any) -> bool: return self._data == other_mapping.data - def __ne__(self, other: Any) -> bool: + def __ne__(self, other: object) -> bool: """ Return whether the delimiter and case-insensitive :class:`dict`-like object is not equal to given other object. diff --git a/colour/utilities/verbose.py b/colour/utilities/verbose.py index 2fb8c5c49..480fe7a73 100644 --- a/colour/utilities/verbose.py +++ b/colour/utilities/verbose.py @@ -31,6 +31,7 @@ Literal, LiteralWarning, Mapping, + Self, TextIO, Type, cast, @@ -587,7 +588,7 @@ class suppress_stdout: Hello World! """ - def __enter__(self) -> suppress_stdout: + def __enter__(self) -> Self: """Redirect the standard output upon entering the context manager.""" self._stdout = sys.stdout diff --git a/pyproject.toml b/pyproject.toml index 0259cc082..8059580be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -181,6 +181,7 @@ select = [ "PL", # pylint # "PT", # flake8-pytest-style # "PTH", # flake8-use-pathlib [Enable] + "PYI", # flake8-pyi "Q", # flake8-quotes "RET", # flake8-return "RUF", # Ruff @@ -215,6 +216,9 @@ ignore = [ "PLR0913", # Too many arguments in function definition "PLR0915", # Too many statements "PLR2004", # Magic value used in comparison, consider replacing `.*` with a constant variable + "PYI036", # Star-args in `.*` should be annotated with `object` + "PYI051", # `Literal[".*"]` is redundant in a union with `str` + "PYI056", # Calling `.append()` on `__all__` may not be supported by all type checkers (use `+=` instead) "RUF022", # [*] `__all__` is not sorted "TRY003", # Avoid specifying long messages outside the exception class "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)`