Mark Joshwel
f00abf2d9c
took me like too long to realise i needed to gamut clip or else i was getting what seemed like the illogical emulation of imaginary colours
135 lines
No EOL
4.1 KiB
C#
135 lines
No EOL
4.1 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
public class OklchColourPicker : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// perceptual lightness value of the colour (0-100)
|
|
/// </summary>
|
|
public double lightness;
|
|
|
|
/// <summary>
|
|
/// chroma value of the colour (0-0.5)
|
|
/// </summary>
|
|
public double chroma;
|
|
|
|
/// <summary>
|
|
/// hue value of the colour (0-360)
|
|
/// </summary>
|
|
public double hue;
|
|
|
|
/// <summary>
|
|
/// slider for the chroma value
|
|
/// </summary>
|
|
private Slider _chromaSlider;
|
|
|
|
/// <summary>
|
|
/// slider for the hue value
|
|
/// </summary>
|
|
private Slider _hueSlider;
|
|
|
|
/// <summary>
|
|
/// slider for the lightness value
|
|
/// </summary>
|
|
private Slider _lightnessSlider;
|
|
|
|
/// <summary>
|
|
/// visual element for the response colour preview
|
|
/// </summary>
|
|
private VisualElement _responseColour;
|
|
|
|
/// <summary>
|
|
/// function to set the initial values of the sliders
|
|
/// </summary>
|
|
private void Start()
|
|
{
|
|
_lightnessSlider.value = 74.61f;
|
|
_chromaSlider.value = 0.0868f;
|
|
_hueSlider.value = 335.72f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// function to subscribe slider events to their respective functions
|
|
/// </summary>
|
|
public void OnEnable()
|
|
{
|
|
var ui = GetComponent<UIDocument>().rootVisualElement;
|
|
|
|
_lightnessSlider = ui.Q<Slider>("ResponseLightnessSlider");
|
|
_lightnessSlider.RegisterCallback<ChangeEvent<float>>(OnLightnessChange);
|
|
|
|
_chromaSlider = ui.Q<Slider>("ResponseChromaSlider");
|
|
_chromaSlider.RegisterCallback<ChangeEvent<float>>(OnChromaChange);
|
|
|
|
_hueSlider = ui.Q<Slider>("ResponseHueSlider");
|
|
_hueSlider.RegisterCallback<ChangeEvent<float>>(OnHueChange);
|
|
|
|
_responseColour = ui.Q<VisualElement>("ResponseColour");
|
|
}
|
|
|
|
// /// <summary>
|
|
// /// update the response preview colour
|
|
// /// </summary>
|
|
// private void Update()
|
|
// {
|
|
// _responseColour.style.backgroundColor = ToColor();
|
|
// }
|
|
|
|
/// <summary>
|
|
/// handle lightness slider change
|
|
/// </summary>
|
|
/// <param name="evt">change event</param>
|
|
private void OnLightnessChange(ChangeEvent<float> evt)
|
|
{
|
|
lightness = Math.Clamp(evt.newValue, 0d, 100d);
|
|
_responseColour.style.backgroundColor = ToColor();
|
|
}
|
|
|
|
/// <summary>
|
|
/// handle chroma slider change
|
|
/// </summary>
|
|
/// <param name="evt">change event</param>
|
|
private void OnChromaChange(ChangeEvent<float> evt)
|
|
{
|
|
chroma = Math.Clamp(evt.newValue, 0d, 0.5d);
|
|
_responseColour.style.backgroundColor = ToColor();
|
|
}
|
|
|
|
/// <summary>
|
|
/// handle hue slider change
|
|
/// </summary>
|
|
/// <param name="evt">change event</param>
|
|
private void OnHueChange(ChangeEvent<float> evt)
|
|
{
|
|
hue = Math.Clamp(evt.newValue, 0d, 360d);
|
|
_responseColour.style.backgroundColor = ToColor();
|
|
}
|
|
|
|
/// <summary>
|
|
/// convert the oklch colour to a unity rgba colour object
|
|
/// </summary>
|
|
/// <returns>a unity rgba color object</returns>
|
|
private Color ToColor()
|
|
{
|
|
// clamp values
|
|
var cL = Math.Clamp(lightness / 100.0d, 0d, 1d);
|
|
var cC = Math.Clamp(chroma, 0d, 0.5d);
|
|
var cH = Math.Clamp(hue, 0d, 360d);
|
|
|
|
// convert [OKL]Ch to [OKL]ab
|
|
var hueRadians = cH * Math.PI / 180.0d;
|
|
var a = cC * Math.Cos(hueRadians);
|
|
var b = cC * Math.Sin(hueRadians);
|
|
|
|
// bring it to linear sRGB, clip it, then bring it back to non-linear sRGB
|
|
var lsrgb = Colorimetry.oklab_to_linear_srgb(new Colorimetry.Lab((float)cL, (float)a, (float)b));
|
|
var clippedLsrgb = Colorimetry.gamut_clip_preserve_chroma(lsrgb);
|
|
var srgb = new Color(
|
|
Math.Clamp((float)Colorimetry.srgb_nonlinear_transform_f(clippedLsrgb.r), 0.0f, 1.0f),
|
|
Math.Clamp((float)Colorimetry.srgb_nonlinear_transform_f(clippedLsrgb.g), 0.0f, 1.0f),
|
|
Math.Clamp((float)Colorimetry.srgb_nonlinear_transform_f(clippedLsrgb.b), 0.0f, 1.0f));
|
|
|
|
return srgb;
|
|
}
|
|
} |