Coverage for volume/tests/test_spectrum.py: 100%

45 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1"""Define the unit tests for the :mod:`colour.volume.spectrum` module.""" 

2 

3from __future__ import annotations 

4 

5from itertools import product 

6 

7import numpy as np 

8 

9from colour.colorimetry import ( 

10 MSDS_CMFS, 

11 SPECTRAL_SHAPE_DEFAULT, 

12 SpectralShape, 

13 reshape_msds, 

14) 

15from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

16from colour.utilities import ignore_numpy_errors, is_scipy_installed 

17from colour.volume import ( 

18 XYZ_outer_surface, 

19 generate_pulse_waves, 

20 is_within_visible_spectrum, 

21) 

22 

23__author__ = "Colour Developers" 

24__copyright__ = "Copyright 2013 Colour Developers" 

25__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

26__maintainer__ = "Colour Developers" 

27__email__ = "colour-developers@colour-science.org" 

28__status__ = "Production" 

29 

30__all__ = [ 

31 "TestGeneratePulseWaves", 

32 "TestXYZOuterSurface", 

33 "TestIsWithinVisibleSpectrum", 

34] 

35 

36 

37class TestGeneratePulseWaves: 

38 """ 

39 Define :func:`colour.volume.spectrum.generate_pulse_waves` 

40 definition unit tests methods. 

41 """ 

42 

43 def test_generate_pulse_waves(self) -> None: 

44 """ 

45 Test :func:`colour.volume.spectrum.generate_pulse_waves` 

46 definition. 

47 """ 

48 

49 np.testing.assert_array_equal( 

50 generate_pulse_waves(5), 

51 np.array( 

52 [ 

53 [0.0, 0.0, 0.0, 0.0, 0.0], 

54 [1.0, 0.0, 0.0, 0.0, 0.0], 

55 [0.0, 1.0, 0.0, 0.0, 0.0], 

56 [0.0, 0.0, 1.0, 0.0, 0.0], 

57 [0.0, 0.0, 0.0, 1.0, 0.0], 

58 [0.0, 0.0, 0.0, 0.0, 1.0], 

59 [1.0, 1.0, 0.0, 0.0, 0.0], 

60 [0.0, 1.0, 1.0, 0.0, 0.0], 

61 [0.0, 0.0, 1.0, 1.0, 0.0], 

62 [0.0, 0.0, 0.0, 1.0, 1.0], 

63 [1.0, 0.0, 0.0, 0.0, 1.0], 

64 [1.0, 1.0, 1.0, 0.0, 0.0], 

65 [0.0, 1.0, 1.0, 1.0, 0.0], 

66 [0.0, 0.0, 1.0, 1.0, 1.0], 

67 [1.0, 0.0, 0.0, 1.0, 1.0], 

68 [1.0, 1.0, 0.0, 0.0, 1.0], 

69 [1.0, 1.0, 1.0, 1.0, 0.0], 

70 [0.0, 1.0, 1.0, 1.0, 1.0], 

71 [1.0, 0.0, 1.0, 1.0, 1.0], 

72 [1.0, 1.0, 0.0, 1.0, 1.0], 

73 [1.0, 1.0, 1.0, 0.0, 1.0], 

74 [1.0, 1.0, 1.0, 1.0, 1.0], 

75 ] 

76 ), 

77 ) 

78 

79 np.testing.assert_array_equal( 

80 generate_pulse_waves(5, "Pulse Wave Width"), 

81 np.array( 

82 [ 

83 [0.0, 0.0, 0.0, 0.0, 0.0], 

84 [1.0, 0.0, 0.0, 0.0, 0.0], 

85 [1.0, 1.0, 0.0, 0.0, 0.0], 

86 [1.0, 1.0, 0.0, 0.0, 1.0], 

87 [1.0, 1.0, 1.0, 0.0, 1.0], 

88 [0.0, 1.0, 0.0, 0.0, 0.0], 

89 [0.0, 1.0, 1.0, 0.0, 0.0], 

90 [1.0, 1.0, 1.0, 0.0, 0.0], 

91 [1.0, 1.0, 1.0, 1.0, 0.0], 

92 [0.0, 0.0, 1.0, 0.0, 0.0], 

93 [0.0, 0.0, 1.0, 1.0, 0.0], 

94 [0.0, 1.0, 1.0, 1.0, 0.0], 

95 [0.0, 1.0, 1.0, 1.0, 1.0], 

96 [0.0, 0.0, 0.0, 1.0, 0.0], 

97 [0.0, 0.0, 0.0, 1.0, 1.0], 

98 [0.0, 0.0, 1.0, 1.0, 1.0], 

99 [1.0, 0.0, 1.0, 1.0, 1.0], 

100 [0.0, 0.0, 0.0, 0.0, 1.0], 

101 [1.0, 0.0, 0.0, 0.0, 1.0], 

102 [1.0, 0.0, 0.0, 1.0, 1.0], 

103 [1.0, 1.0, 0.0, 1.0, 1.0], 

104 [1.0, 1.0, 1.0, 1.0, 1.0], 

105 ] 

106 ), 

107 ) 

108 

109 np.testing.assert_equal( 

110 np.sort(generate_pulse_waves(5), axis=0), 

111 np.sort(generate_pulse_waves(5, "Pulse Wave Width"), axis=0), 

112 ) 

113 

114 np.testing.assert_array_equal( 

115 generate_pulse_waves(5, "Pulse Wave Width", True), 

116 np.array( 

117 [ 

118 [0.0, 0.0, 0.0, 0.0, 0.0], 

119 [1.0, 0.0, 0.0, 0.0, 0.0], 

120 [1.0, 1.0, 0.0, 0.0, 1.0], 

121 [0.0, 1.0, 0.0, 0.0, 0.0], 

122 [1.0, 1.0, 1.0, 0.0, 0.0], 

123 [0.0, 0.0, 1.0, 0.0, 0.0], 

124 [0.0, 1.0, 1.0, 1.0, 0.0], 

125 [0.0, 0.0, 0.0, 1.0, 0.0], 

126 [0.0, 0.0, 1.0, 1.0, 1.0], 

127 [0.0, 0.0, 0.0, 0.0, 1.0], 

128 [1.0, 0.0, 0.0, 1.0, 1.0], 

129 [1.0, 1.0, 1.0, 1.0, 1.0], 

130 ] 

131 ), 

132 ) 

133 

134 

135class TestXYZOuterSurface: 

136 """ 

137 Define :func:`colour.volume.spectrum.XYZ_outer_surface` 

138 definition unit tests methods. 

139 """ 

140 

141 def test_XYZ_outer_surface(self) -> None: 

142 """ 

143 Test :func:`colour.volume.spectrum.XYZ_outer_surface` 

144 definition. 

145 """ 

146 

147 shape = SpectralShape( 

148 SPECTRAL_SHAPE_DEFAULT.start, SPECTRAL_SHAPE_DEFAULT.end, 84 

149 ) 

150 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] 

151 

152 np.testing.assert_allclose( 

153 XYZ_outer_surface(reshape_msds(cmfs, shape)), 

154 np.array( 

155 [ 

156 [0.00000000e00, 0.00000000e00, 0.00000000e00], 

157 [9.63613812e-05, 2.90567768e-06, 4.49612264e-04], 

158 [2.59105294e-01, 2.10312980e-02, 1.32074689e00], 

159 [1.05610219e-01, 6.20382435e-01, 3.54235713e-02], 

160 [7.26479803e-01, 3.54608696e-01, 2.10051491e-04], 

161 [1.09718745e-02, 3.96354538e-03, 0.00000000e00], 

162 [3.07925724e-05, 1.11197622e-05, 0.00000000e00], 

163 [2.59201656e-01, 2.10342037e-02, 1.32119651e00], 

164 [3.64715514e-01, 6.41413733e-01, 1.35617047e00], 

165 [8.32090022e-01, 9.74991131e-01, 3.56336228e-02], 

166 [7.37451677e-01, 3.58572241e-01, 2.10051491e-04], 

167 [1.10026671e-02, 3.97466514e-03, 0.00000000e00], 

168 [1.27153954e-04, 1.40254398e-05, 4.49612264e-04], 

169 [3.64811875e-01, 6.41416639e-01, 1.35662008e00], 

170 [1.09119532e00, 9.96022429e-01, 1.35638052e00], 

171 [8.43061896e-01, 9.78954677e-01, 3.56336228e-02], 

172 [7.37482470e-01, 3.58583361e-01, 2.10051491e-04], 

173 [1.10990285e-02, 3.97757082e-03, 4.49612264e-04], 

174 [2.59232448e-01, 2.10453234e-02, 1.32119651e00], 

175 [1.09129168e00, 9.96025335e-01, 1.35683013e00], 

176 [1.10216719e00, 9.99985975e-01, 1.35638052e00], 

177 [8.43092689e-01, 9.78965796e-01, 3.56336228e-02], 

178 [7.37578831e-01, 3.58586267e-01, 6.59663755e-04], 

179 [2.70204323e-01, 2.50088688e-02, 1.32119651e00], 

180 [3.64842668e-01, 6.41427759e-01, 1.35662008e00], 

181 [1.10226355e00, 9.99988880e-01, 1.35683013e00], 

182 [1.10219798e00, 9.99997094e-01, 1.35638052e00], 

183 [8.43189050e-01, 9.78968702e-01, 3.60832350e-02], 

184 [9.96684125e-01, 3.79617565e-01, 1.32140656e00], 

185 [3.75814542e-01, 6.45391304e-01, 1.35662008e00], 

186 [1.09132247e00, 9.96036455e-01, 1.35683013e00], 

187 [1.10229434e00, 1.00000000e00, 1.35683013e00], 

188 ] 

189 ), 

190 atol=TOLERANCE_ABSOLUTE_TESTS, 

191 ) 

192 

193 

194class TestIsWithinVisibleSpectrum: 

195 """ 

196 Define :func:`colour.volume.spectrum.is_within_visible_spectrum` 

197 definition unit tests methods. 

198 """ 

199 

200 def test_is_within_visible_spectrum(self) -> None: 

201 """ 

202 Test :func:`colour.volume.spectrum.is_within_visible_spectrum` 

203 definition. 

204 """ 

205 

206 if not is_scipy_installed(): # pragma: no cover 

207 return 

208 

209 assert is_within_visible_spectrum(np.array([0.3205, 0.4131, 0.5100])) 

210 

211 assert not is_within_visible_spectrum(np.array([-0.0005, 0.0031, 0.0010])) 

212 

213 assert is_within_visible_spectrum(np.array([0.4325, 0.3788, 0.1034])) 

214 

215 assert not is_within_visible_spectrum(np.array([0.0025, 0.0088, 0.0340])) 

216 

217 def test_n_dimensional_is_within_visible_spectrum(self) -> None: 

218 """ 

219 Test :func:`colour.volume.spectrum.is_within_visible_spectrum` 

220 definition n-dimensional arrays support. 

221 """ 

222 

223 if not is_scipy_installed(): # pragma: no cover 

224 return 

225 

226 a = np.array([0.3205, 0.4131, 0.5100]) 

227 b = is_within_visible_spectrum(a) 

228 

229 a = np.tile(a, (6, 1)) 

230 b = np.tile(b, 6) 

231 np.testing.assert_allclose(is_within_visible_spectrum(a), b) 

232 

233 a = np.reshape(a, (2, 3, 3)) 

234 b = np.reshape(b, (2, 3)) 

235 np.testing.assert_allclose(is_within_visible_spectrum(a), b) 

236 

237 @ignore_numpy_errors 

238 def test_nan_is_within_visible_spectrum(self) -> None: 

239 """ 

240 Test :func:`colour.volume.spectrum.is_within_visible_spectrum` 

241 definition nan support. 

242 """ 

243 

244 if not is_scipy_installed(): # pragma: no cover 

245 return 

246 

247 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] 

248 cases = np.array(list(set(product(cases, repeat=3)))) 

249 is_within_visible_spectrum(cases)