Skip to content

Commit 7d01504

Browse files
authored
Update random_color_hex.py
1 parent a1d491c commit 7d01504

1 file changed

Lines changed: 50 additions & 30 deletions

File tree

random_color_hex.py

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import secrets
66
import time as tym
7+
import atexit
8+
from ColorCalculus import DeepColorMath
79

810
class RandomColorHex:
911
"""Stateful random color generator.
@@ -22,29 +24,21 @@ class RandomColorHex:
2224
"""
2325
AllTheColors=[]
2426
EpochCount={'S':0,'M':0,'L':0,'SL':0}
25-
EpochSize={'S':8400,'M':770,'L':220,'SL':36}
27+
EpochSize={'S':663,'M':68,'L':40,'SL':23}
28+
_auto_reset_registered=False
2629

2730
@classmethod
28-
def reset(cls):
31+
def _reset(cls):
2932
"""Clear class-level state that tracks previously used colors."""
3033
cls.AllTheColors.clear()
34+
cls.EpochCount={'S':0,'M':0,'L':0,'SL':0}
3135

3236
@classmethod
33-
def _preflight_cycle_reset(cls):
34-
"""Reset rules:
35-
- If CycleResetInt is odd ⇒ reset before doing anything.
36-
- If CycleResetInt == 10 ⇒ reset and wrap to 0 before doing anything.
37-
"""
38-
if cls.CycleResetInt == 10:
39-
cls.reset()
40-
cls.CycleResetInt = 0 # wrap
41-
elif cls.CycleResetInt % 2 == 1:
42-
cls.reset()
43-
44-
@classmethod
45-
def _bump_cycle(cls):
46-
"""Advance 0→1→…→10→0."""
47-
cls.CycleResetInt = (cls.CycleResetInt + 1) % 11
37+
def _register_auto_reset(cls):
38+
"""Register automatic reset on program exit."""
39+
if not cls._auto_reset_registered:
40+
atexit.register(cls._reset)
41+
cls._auto_reset_registered=True
4842

4943
def __init__(self):
5044
"""Initialize internal buffers and near-white masks.
@@ -58,6 +52,8 @@ def __init__(self):
5852
"""
5953
self.RandomHexCode=[] #So you can access the code later for any instance
6054
self.NearWhiteMasks=['FHFHFH','FXFXFX','FHFHFX','XFHFHF','EHFHFH','HHHHHH'] #neutral, warm, cool, very light gray
55+
self._register_auto_reset()
56+
self.MassProduction=False
6157

6258
def MatchesMask(self, hex6, mask):
6359
"""Return True if the 6-char hex string matches a mask.
@@ -101,6 +97,8 @@ def AreColorsClose(self, InputColor, MetricBar):
10197
Uses Euclidean distance in RGB space between the candidate color and
10298
all entries in `self.AllTheColors`. If any distance ≤ `MetricBar`, the
10399
color is considered “too close”.
100+
101+
OUTDATED, USE ONLY FOR BASICMAIN
104102
"""
105103
R1=int(InputColor[0:2],16)
106104
G1=int(InputColor[2:4],16)
@@ -115,6 +113,16 @@ def AreColorsClose(self, InputColor, MetricBar):
115113
return True
116114
return False
117115

116+
def AreColorsClosePerceptual(self, InputColor, threshold):
117+
"""Return True if `InputColor` is within perceptual `threshold` of any prior color.
118+
119+
Uses CIEDE2000 perceptual distance from ColorCalculus.
120+
"""
121+
for prev in self.AllTheColors:
122+
if DeepColorMath.ciede2000(InputColor, prev) < threshold:
123+
return True
124+
return False
125+
118126
def IsNearWhite(self, hex6:str):
119127
"""Return True if the color is near white / very light.
120128
@@ -204,16 +212,16 @@ def BasicMain(SuperLightColorsAllowed=True, SuperDarkColorsAllowed=True):
204212
RandomColorHex.EpochCount['S']+=1
205213
return out
206214

207-
def main(self, SuperLightColorsAllowed=True, SuperDarkColorsAllowed=True,HowDifferentShouldColorsBe='s'):
215+
def main(self, SuperLightColorsAllowed=True, SuperDarkColorsAllowed=True,HowDifferentShouldColorsBe='m'):
208216
match HowDifferentShouldColorsBe:
209217
case 'M'|'m':
210-
MetricBar=25; mode='M'
218+
mode='M'; PerceptualThreshold=25
211219
case 'S'|'s':
212-
MetricBar=10; mode='S'
220+
mode='S'; PerceptualThreshold=10
213221
case "L"|"l":
214-
MetricBar=40; mode='L'
222+
mode='L'; PerceptualThreshold=30
215223
case "SL"|"sl"|"sL"|"Sl":
216-
MetricBar=80; mode='SL'
224+
mode='SL'; PerceptualThreshold=40
217225
case _:
218226
raise ValueError('Invalid HowDifferentShouldColorsBe parameter! Please type "s" (small), "m" (medium), "l" (large), or "sl" (super large).')
219227
if RandomColorHex.EpochCount[mode]>=RandomColorHex.EpochSize[mode]:
@@ -228,20 +236,30 @@ def main(self, SuperLightColorsAllowed=True, SuperDarkColorsAllowed=True,HowDiff
228236
"Note! It seems you're generating a lot of colors. The algorithm will keep searching, "
229237
"but it's going to take a while!\n"
230238
"This may be because the distance metric is too large (HowDifferentShouldColorsBe).\n"
231-
"Generally, anything over 220 colors with L set up starts having trouble.\n"
232-
"Super Large starts having trouble at 36\n"
233-
"Small can do ~8400\n"
234-
"Medium can do ~770\n"
239+
"Generally, anything over 40 colors with L set up starts having trouble.\n"
240+
"Super Large starts having trouble at 23\n"
241+
"Small can do ~663\n"
242+
"Medium can do ~68\n"
235243
"For quicker results, please use either BasicMain() or HowDifferentShouldColorsBe='S' or 'M'."
236244
)
237245
OneNotice=False
246+
247+
if (tym.time()-start)>80 or self.MassProduction:
248+
self.MassProduction=True
249+
print("Timeout reached (80 seconds). Switching to BasicMain mode for remaining colors.")
250+
#Generate color without distance checking
251+
return self.BasicMain(SuperLightColorsAllowed=SuperLightColorsAllowed, SuperDarkColorsAllowed=SuperDarkColorsAllowed)
252+
238253
OutputtedString=''.join(self.RandomHexCode)
239254
if not SuperLightColorsAllowed and self.IsNearWhite(OutputtedString):
240-
self.RandomHex(); continue
255+
self.RandomHex()
256+
continue
241257
if not SuperDarkColorsAllowed and self.IsNearBlack(OutputtedString):
242-
self.RandomHex(); continue
243-
if self.AreColorsClose(OutputtedString, MetricBar):
244-
self.RandomHex(); continue
258+
self.RandomHex()
259+
continue
260+
if self.AreColorsClosePerceptual(OutputtedString, PerceptualThreshold):
261+
self.RandomHex()
262+
continue
245263
break
246264
self.AllTheColors.append(''.join(self.RandomHexCode))
247265
self.RandomHexCode.insert(0,'#')
@@ -295,6 +313,8 @@ def John_3_Verse_16():
295313
c=RandomColorHex()
296314
print(c.main())
297315
print(c.main(SuperLightColorsAllowed=False, SuperDarkColorsAllowed=False, HowDifferentShouldColorsBe='m'))
316+
for index in range(5000):
317+
print(f"{index}, {c.main(HowDifferentShouldColorsBe='m')}")
298318
print(c.BasicMain())
299319
c.Credits()
300320
c.Help()

0 commit comments

Comments
 (0)