Skip to content

Commit 03cfd67

Browse files
committed
Reduce duplicated environment code
1 parent a664148 commit 03cfd67

File tree

3 files changed

+36
-86
lines changed

3 files changed

+36
-86
lines changed

coloraide/spaces/cam02_jmh.py

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
from ..cat import WHITES, CAT02
1515
from ..channels import Channel, FLG_ANGLE
1616
from .lch import ACHROMATIC_THRESHOLD
17-
from ..types import Vector, VectorLike
17+
from ..types import Vector
1818
from .cam16_jmh import (
1919
M1,
20-
SURROUND,
2120
hue_quadrature,
2221
inv_hue_quadrature,
2322
eccentricity,
2423
adapt,
2524
unadapt
2625
)
26+
from .cam16_jmh import Environment as _Environment
2727

2828
# CAT02
2929
M02 = CAT02.MATRIX
@@ -46,7 +46,7 @@
4646
]
4747

4848

49-
class Environment:
49+
class Environment(_Environment):
5050
"""
5151
Class to calculate and contain any required environmental data (viewing conditions included).
5252
@@ -78,54 +78,12 @@ class Environment:
7878
discounting: Whether we are discounting the illuminance. Done when eye is assumed to be fully adapted.
7979
"""
8080

81-
def __init__(
82-
self,
83-
*,
84-
white: VectorLike,
85-
adapting_luminance: float,
86-
background_luminance: float,
87-
surround: str,
88-
discounting: bool
89-
):
90-
"""
91-
Initialize environmental viewing conditions.
92-
93-
Using the specified viewing conditions, and general environmental data,
94-
initialize anything that we can ahead of time to speed up the process.
95-
"""
96-
97-
self.discounting = discounting
98-
self.ref_white = util.xy_to_xyz(white)
99-
self.surround = surround
100-
101-
# The average luminance of the environment in `cd/m^2cd/m` (a.k.a. nits)
102-
self.la = adapting_luminance
103-
# The relative luminance of the nearby background
104-
self.yb = background_luminance
105-
# Absolute luminance of the reference white.
106-
xyz_w = util.scale100(self.ref_white)
107-
self.yw = xyz_w[1]
81+
def calculate_adaptation(self, xyz_w: Vector) -> None:
82+
"""Calculate the adaptation of the reference point and related variables."""
10883

10984
# Cone response for reference white
11085
self.rgb_w = alg.matmul_x3(M02, xyz_w, dims=alg.D2_D1)
11186

112-
# Surround: dark, dim, and average
113-
f, self.c, self.nc = SURROUND[self.surround]
114-
115-
k = 1 / (5 * self.la + 1)
116-
k4 = k ** 4
117-
118-
# Factor of luminance level adaptation
119-
self.fl = (k4 * self.la + 0.1 * (1 - k4) * (1 - k4) * math.pow(5 * self.la, 1 / 3))
120-
self.fl_root = math.pow(self.fl, 0.25)
121-
122-
self.n = self.yb / self.yw
123-
self.z = 1.48 + math.sqrt(self.n)
124-
self.nbb = 0.725 * math.pow(self.n, -0.2)
125-
self.ncb = self.nbb
126-
127-
# Degree of adaptation calculating if not discounting illuminant (assumed eye is fully adapted)
128-
self.d = alg.clamp(f * (1 - 1 / 3.6 * math.exp((-self.la - 42) / 92)), 0, 1) if not discounting else 1
12987
self.d_rgb = [(self.yw * (self.d / coord) + 1 - self.d) for coord in self.rgb_w]
13088
self.d_rgb_inv = [1 / coord for coord in self.d_rgb]
13189
self.rgb_cw = alg.multiply_x3(self.d_rgb, self.rgb_w, dims=alg.D1)
@@ -262,15 +220,15 @@ def xyz_to_cam(xyz: Vector, env: Environment, calc_hue_quadrature: bool = False)
262220
# Colorfulness
263221
M = C * env.fl_root
264222

223+
# Saturation
224+
s = 50 * alg.nth_root(env.c * alpha / (env.a_w + 4), 2)
225+
265226
# Hue
266227
h = util.constrain_hue(math.degrees(h_rad))
267228

268229
# Hue quadrature
269230
H = hue_quadrature(h) if calc_hue_quadrature else alg.NaN
270231

271-
# Saturation
272-
s = 50 * alg.nth_root(env.c * alpha / (env.a_w + 4), 2)
273-
274232
return [J, C, h, s, Q, M, H]
275233

276234

coloraide/spaces/cam16_jmh.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,6 @@ def __init__(
167167
xyz_w = util.scale100(self.ref_white)
168168
self.yw = xyz_w[1]
169169

170-
# Cone response for reference white
171-
self.rgb_w = alg.matmul_x3(M16, xyz_w, dims=alg.D2_D1)
172-
173170
# Surround: dark, dim, and average
174171
f, self.c, self.nc = SURROUND[self.surround]
175172

@@ -187,6 +184,14 @@ def __init__(
187184

188185
# Degree of adaptation calculating if not discounting illuminant (assumed eye is fully adapted)
189186
self.d = alg.clamp(f * (1 - 1 / 3.6 * math.exp((-self.la - 42) / 92)), 0, 1) if not discounting else 1
187+
self.calculate_adaptation(xyz_w)
188+
189+
def calculate_adaptation(self, xyz_w: Vector) -> None:
190+
"""Calculate the adaptation of the reference point and related variables."""
191+
192+
# Cone response for reference white
193+
self.rgb_w = alg.matmul_x3(M16, xyz_w, dims=alg.D2_D1)
194+
190195
self.d_rgb = [alg.lerp(1, self.yw / coord, self.d) for coord in self.rgb_w]
191196
self.d_rgb_inv = [1 / coord for coord in self.d_rgb]
192197

@@ -322,15 +327,15 @@ def xyz_to_cam(xyz: Vector, env: Environment, calc_hue_quadrature: bool = False)
322327
# Colorfulness
323328
M = C * env.fl_root
324329

330+
# Saturation
331+
s = 50 * alg.nth_root(env.c * alpha / (env.a_w + 4), 2)
332+
325333
# Hue
326334
h = util.constrain_hue(math.degrees(h_rad))
327335

328336
# Hue quadrature
329337
H = hue_quadrature(h) if calc_hue_quadrature else alg.NaN
330338

331-
# Saturation
332-
s = 50 * alg.nth_root(env.c * alpha / (env.a_w + 4), 2)
333-
334339
return [J, C, h, s, Q, M, H]
335340

336341

coloraide/spaces/hellwig_jmh.py

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from .. import algebra as alg
3333
from ..cat import WHITES
3434
from ..channels import Channel, FLG_ANGLE
35-
from ..types import Vector, VectorLike
35+
from ..types import Vector
3636

3737

3838
def hue_angle_dependency(h: float) -> float:
@@ -94,32 +94,19 @@ class Environment(_Environment):
9494
discounting: Whether we are discounting the illuminance. Done when eye is assumed to be fully adapted.
9595
"""
9696

97-
def __init__(
98-
self,
99-
*,
100-
white: VectorLike,
101-
adapting_luminance: float,
102-
background_luminance: float,
103-
surround: str,
104-
discounting: bool
105-
):
106-
"""
107-
Initialize environmental viewing conditions.
108-
109-
Using the specified viewing conditions, and general environmental data,
110-
initialize anything that we can ahead of time to speed up the process.
111-
"""
112-
113-
super().__init__(
114-
white=white,
115-
adapting_luminance=adapting_luminance,
116-
background_luminance=background_luminance,
117-
surround=surround,
118-
discounting=discounting
119-
)
120-
121-
# `nbb` is not needed anymore, so remove it.
122-
self.a_w /= self.nbb
97+
def calculate_adaptation(self, xyz_w: Vector) -> None:
98+
"""Calculate the adaptation of the reference point and related variables."""
99+
100+
# Cone response for reference white
101+
self.rgb_w = alg.matmul_x3(M16, xyz_w, dims=alg.D2_D1)
102+
103+
self.d_rgb = [alg.lerp(1, self.yw / coord, self.d) for coord in self.rgb_w]
104+
self.d_rgb_inv = [1 / coord for coord in self.d_rgb]
105+
106+
# Achromatic response
107+
self.rgb_cw = alg.multiply_x3(self.rgb_w, self.d_rgb, dims=alg.D1)
108+
rgb_aw = adapt(self.rgb_cw, self.fl)
109+
self.a_w = (2 * rgb_aw[0] + rgb_aw[1] + 0.05 * rgb_aw[2])
123110

124111

125112
def cam_to_xyz(
@@ -248,15 +235,15 @@ def xyz_to_cam(xyz: Vector, env: Environment, calc_hue_quadrature: bool = False)
248235
# Chroma
249236
C = 35 * (M / env.a_w)
250237

238+
# Saturation
239+
s = 100 * alg.zdiv(M, Q)
240+
251241
# Hue
252242
h = util.constrain_hue(math.degrees(h_rad))
253243

254244
# Hue quadrature
255245
H = hue_quadrature(h) if calc_hue_quadrature else alg.NaN
256246

257-
# Saturation
258-
s = 100 * alg.zdiv(M, Q)
259-
260247
# Lightness: Helmholtz-Kohlrausch effect
261248
Jhk = J + hue_angle_dependency(h_rad) * alg.spow(C, 0.587)
262249

0 commit comments

Comments
 (0)