Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion coloraide/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,5 @@ def parse_version(ver: str) -> Version:
return Version(major, minor, micro, release, pre, post, dev)


__version_info__ = Version(8, 8, 1, "final")
__version_info__ = Version(8, 9, 0, "final")
__version__ = __version_info__._get_canonical()
23 changes: 22 additions & 1 deletion coloraide/interpolate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,9 @@ def carryforward_convert(color: Color, space: str, hue_index: int, powerless: bo
'L': False, 'V': False, 'a': False, 'b': False
}

# Analogous components: all undefined
all_undefined = all(math.isnan(c) for c in color[:-1])

# Gather undefined channels
if isinstance(cs1, RGBish):
for i, name in zip(cs1.indexes(), ('R', 'G', 'B')):
Expand All @@ -838,18 +841,34 @@ def carryforward_convert(color: Color, space: str, hue_index: int, powerless: bo
for i, name in zip(cs1.indexes(), ('L', 'C', 'H')):
if math.isnan(color[i]):
channels[name] = True
# Analogous components: chroma and hue
if channels['C'] and channels['H']:
channels['a'] = True
channels['b'] = True
elif isinstance(cs1, Labish):
for i, name in zip(cs1.indexes(), ('L', 'a', 'b')):
if math.isnan(color[i]):
channels[name] = True
# Analogous components: chroma and hue
if channels['a'] and channels['b']:
channels['C'] = True
channels['H'] = True
elif isinstance(cs1, HSLish):
for i, name in zip(cs1.indexes(), ('H', 'C', 'L')):
if math.isnan(color[i]):
channels[name] = True
# Analogous components: chroma and hue
if channels['C'] and channels['H']:
channels['a'] = True
channels['b'] = True
elif isinstance(cs1, HSVish):
for i, name in zip(cs1.indexes(), ('H', 'C', 'V')):
if math.isnan(color[i]):
channels[name] = True
# Analogous components: chroma and hue
if channels['C'] and channels['H']:
channels['a'] = True
channels['b'] = True
elif cs1.is_polar():
if math.isnan(color[cs1.hue_index()]): # type: ignore[attr-defined]
channels['H'] = True
Expand All @@ -859,7 +878,9 @@ def carryforward_convert(color: Color, space: str, hue_index: int, powerless: bo
carry.append(-1)

# Channels that need to be carried forward
if isinstance(cs2, RGBish):
if all_undefined:
carry.extend(cs2.indexes())
elif isinstance(cs2, RGBish):
indexes = cs2.indexes()
for e, name in enumerate(('R', 'G', 'B')):
if channels[name]:
Expand Down
4 changes: 4 additions & 0 deletions docs/src/markdown/about/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ icon: lucide/scroll-text
---
# Changelog

## 8.9

- **NEW**: Add support for CSS analogous component sets in interpolation and mixing.

## 8.8.1

- **ENHANCE**: Minor speed improvements related to chromatic adaptation.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/markdown/examples/3d_models.html
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ <h1>ColorAide Color Space Models</h1>
let colorSpaces = null
let colorGamuts = null
let lastModel = null
let package = 'coloraide-8.8.1-py3-none-any.whl'
let package = 'coloraide-8.9-py3-none-any.whl'
const defaultSpace = 'lab'
const defaultGamut = 'srgb'
const exceptions = new Set(['hwb', 'ryb', 'ryb-biased'])
Expand Down
2 changes: 1 addition & 1 deletion docs/src/markdown/examples/colorpicker.html
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ <h1>ColorAide Color Picker</h1>
let pyodide = null
let webspace = ''
let initial = 'oklab(0.69 0.13 -0.1 / 0.85)'
let package = 'coloraide-8.8.1-py3-none-any.whl'
let package = 'coloraide-8.9-py3-none-any.whl'

const base = `${window.location.origin}/${window.location.pathname.split('/')[1]}/playground/`
package = base + package
Expand Down
9 changes: 7 additions & 2 deletions docs/src/markdown/interpolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1323,8 +1323,8 @@ derived from `RGBish`, so `x`, `y`, and `z` is treated like super saturated `r`,
Space\ Type | Channel\ Equivalents
------------ | --------
`RGBish` | `r`, `g`, `b`
`LABish` | `l`
`LCHish` | `l`, `c`, `h`
`Labish` | `l`
`LChish` | `l`, `c`, `h`
`HSLish` | `h`, `s`, `l`
`HSVish` | `h`, `s`, `v`
`Cylindrical`| `h`
Expand All @@ -1343,6 +1343,11 @@ Opponent a | `a`
Opponent b | `b`
Value | `v`

Additionally, `carryforward` is applied to analogous sets as well. This means that a combination of components are
considered equivalent to another set of components. For instance, if an `LChish` space has an undefined chroma and hue,
when converting to a `Labish` space, `a` and `b` would be considered undefined (and vice versa). If all color components
are undefined, then when converted, all components in the new space would also be undefined.

## Powerless Hues

> [!example] Experimental
Expand Down
2 changes: 1 addition & 1 deletion docs/src/zensical.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ extra_javascript:
- assets/pymdownx-extras/extra-loader-Ccztcqfq.js
- https://cdn.jsdelivr.net/npm/ace-builds@1.43.0/src-min-noconflict/ace.js
- https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.min.js
- playground-config-3ba60a2e.js
- playground-config-7363a2a9.js
- https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.js
- assets/coloraide-extras/extra-notebook.js
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var colorNotebook = {
"playgroundWheels": ['micropip', 'pygments-2.19.2-py3-none-any.whl', 'coloraide-8.8.1-py3-none-any.whl'],
"notebookWheels": ['pyyaml', 'markdown-3.10.1-py3-none-any.whl', 'pymdown_extensions-10.20-py3-none-any.whl', 'micropip', 'pygments-2.19.2-py3-none-any.whl', 'coloraide-8.8.1-py3-none-any.whl'],
"playgroundWheels": ['micropip', 'pygments-2.19.2-py3-none-any.whl', 'coloraide-8.9-py3-none-any.whl'],
"notebookWheels": ['pyyaml', 'markdown-3.10.1-py3-none-any.whl', 'pymdown_extensions-10.20-py3-none-any.whl', 'micropip', 'pygments-2.19.2-py3-none-any.whl', 'coloraide-8.9-py3-none-any.whl'],
"defaultPlayground": "import coloraide\ncoloraide.__version__\nColor('red')"
}
31 changes: 31 additions & 0 deletions tests/test_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2457,3 +2457,34 @@ def test_spectral_no_colors(self):

with self.assertRaises(ValueError):
Color.weighted_mix([], method='spectral')

def test_carryforward_sets(self):
"""Test analogous sets."""

result = Color.interpolate(
['color(srgb none none none)', 'oklch(70% 0.1 240)'],
space='oklch',
carryforward=True
)(0.5)
self.assertColorEqual(result, Color('oklch(0.7 0.1 240)'))

result = Color.interpolate(
['oklab(40% none none)', 'oklch(70% 0.1 240)'],
space='oklch',
carryforward=True
)(0.5)
self.assertColorEqual(result, Color('oklch(0.55 0.1 240)'))

result = Color.interpolate(
['oklch(40% none none)', 'oklab(70% 0.1 -1.5)'],
space='oklab',
carryforward=True
)(0.5)
self.assertColorEqual(result, Color('oklab(0.55 0.1 -1.5)'))

result = Color.interpolate(
['hsl(none none 40%)', 'oklab(70% 0.1 -1.5)'],
space='oklab',
carryforward=True
)(0.5)
self.assertColorEqual(result, Color('oklab(0.60514 0.1 -1.5)'))
2 changes: 1 addition & 1 deletion zensical.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ extra_javascript:
- assets/pymdownx-extras/extra-loader-Ccztcqfq.js
- https://cdn.jsdelivr.net/npm/ace-builds@1.43.0/src-min-noconflict/ace.js
- https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.min.js
- playground-config-3ba60a2e.js
- playground-config-7363a2a9.js
- https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.js
- assets/coloraide-extras/extra-notebook-CU9d9Z53.js
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
Expand Down
Loading