-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] Allow optional seeding from getColor #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
eb5a12a
5c08ce2
fe3e7b8
01a2ece
40f918a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,43 +18,73 @@ export default class toColor { | |
| constructor(seed, options) { | ||
| this.options = options || {}; | ||
| if (typeof seed === 'string' || typeof seed === 'number') { | ||
| this.seed = typeof seed === 'string' ? this._stringToInteger(seed) : seed; | ||
| this.rootSeed = | ||
| typeof seed === 'string' ? this._stringToInteger(seed) : seed; | ||
| } else { | ||
| throw new TypeError('Seed value must be a number or string'); | ||
| } | ||
|
|
||
| this.seed = this.rootSeed; | ||
| this.known = []; | ||
| this.cache = new Map(); | ||
| } | ||
|
|
||
| getColor(count = 0) { | ||
| getColor(key) { | ||
| if (typeof key === 'string') { | ||
| if (this.cache.has(key)) return this.cache.get(key); | ||
|
|
||
| const color = this._getDeterministicColor(key); | ||
| this.cache.set(key, color); | ||
| return color; | ||
| } | ||
|
|
||
| return this._getSequentialColor(); | ||
| } | ||
|
|
||
| _getDeterministicColor(key) { | ||
| const combined = this._stringToInteger(`${this.rootSeed}:${key}`); | ||
|
|
||
| const h = this._mapIndexToHue(combined); | ||
| const s = this._mapIndexToRange(combined >> 2, 60, 100); | ||
| const l = this._mapIndexToRange(combined >> 3, 35, 80); | ||
|
|
||
| return this._colorWithModifiers(h, s, l); | ||
| } | ||
|
|
||
| _getSequentialColor(count = 0) { | ||
| const h = this._pickHue(); | ||
| const s = this._pickSaturation(); | ||
| const l = this._pickLightness(); | ||
|
|
||
| const { hsl } = this._HSLuvify(h, s, l); | ||
| const PASSABLE_DISTANCE = 60; | ||
|
|
||
| // The larger `count` grows, we need to divide actual distance to avoid | ||
| // hitting a maxiumum call stack error. | ||
| const ACTUAL_DISTANCE = PASSABLE_DISTANCE / Math.pow(1.05, count); | ||
|
|
||
| // Detect color similarity. If values are too close to one another, call | ||
| // getColor until enough dissimilarity is achieved. | ||
| if ( | ||
| this.known.length && | ||
| this.known.some( | ||
| (v) => differenceCiede2000(v, hsl.formatted) < ACTUAL_DISTANCE | ||
| ) | ||
| ) { | ||
| return this.getColor(count + 1); | ||
| return this._getSequentialColor(count + 1); | ||
| } else { | ||
| this.known.push(hsl.formatted); | ||
| // Apply modifiers after distribution check + regeneration to ensure | ||
| // colors with brightness/saturation adjustments remain the same. | ||
| return this._colorWithModifiers(h, s, l); | ||
| } | ||
| } | ||
|
|
||
| _mapIndexToHue(index) { | ||
| // A hybrid approach to color distance checking in _getSequentialColor but | ||
| // for `_getDeterministicColor`. Attempts to “spread” hash values evenly to | ||
| // reduce the same hues appearing next to one another. | ||
| const golden = 0.61803398875; | ||
| return Math.round(((index * golden) % 1) * this.HUE_MAX); | ||
| } | ||
|
Comment on lines
+76
to
+82
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI proposed this idea as a cheap way to spread hues more distributed across the palette as fetching a deterministic value and comparing it using color difference would require knowing the whole list of values ahead of time.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's actually not a bad approach, and picked an irrational number too, so it never repeats, which is cool. I'd maybe use a constant like |
||
|
|
||
| _mapIndexToRange(index, min, max) { | ||
| return min + (index % (max - min)); | ||
| } | ||
|
|
||
| _clamp = (n, min, max) => (n <= min ? min : n >= max ? max : n); | ||
|
|
||
| _colorWithModifiers = (h, s, l) => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.