Skip to content
Open
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
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,15 @@ dotnet_diagnostic.SA1133.severity = none

# IDE0001: Simplify Names
dotnet_diagnostic.IDE0001.severity = warning

# IDE0079: Remove unnecessary suppression
dotnet_diagnostic.IDE0079.severity = none

# S1244: Floating point numbers should not be tested for equality
dotnet_diagnostic.S1244.severity = none

# IDE0045: Convert to conditional expression
dotnet_diagnostic.IDE0045.severity = none

# IDE0046: Convert to conditional expression
dotnet_diagnostic.IDE0046.severity = none
7 changes: 7 additions & 0 deletions MiraAPI.Example/ExampleColors.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using MiraAPI.Colors;
using MiraAPI.Colors.ColorSpaces;
using MiraAPI.Utilities;
using UnityEngine;

namespace MiraAPI.Example;
Expand All @@ -14,4 +16,9 @@ public static class ExampleColors
public static CustomColor Rose { get; } = new("color.rose", new Color(0.98f, 0.26f, 0.62f));

public static CustomColor Gold { get; } = new("color.gold", new Color(1.0f, 0.84f, 0.0f));

public static VariableColor Rainbow { get; } = new("color.rainbow", t => new HsbColor(MathUtilities.PingPong(t, 1f), 1f, 1f))
{
ColorBrightness = CustomColorBrightness.Lighter,
};
}
105 changes: 105 additions & 0 deletions MiraAPI/Colors/ColorSpaces/CmykColor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Diagnostics.CodeAnalysis;
using MiraAPI.Utilities;
using UnityEngine;

namespace MiraAPI.Colors.ColorSpaces;

/// <summary>
/// A struct that denotes the CMYK (Cyan, Magenta, Yellow, Key) color space.
/// </summary>
/// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The key component.</param>
/// <param name="a">The alpha component.</param>
[Serializable]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Unity parity.")]
public record struct CmykColor(float c, float m, float y, float k, float a = 1f)
{
/// <summary>
/// Implicitly converts a <see cref="CmykColor"/> to a Unity <see cref="Color"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator Color(CmykColor c)
{
return ToColor(c);
}

/// <summary>
/// Implicitly converts a Unity <see cref="Color"/> to a <see cref="CmykColor"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator CmykColor(Color c)
{
return FromColor(c);
}

/// <summary>
/// Converts a <see cref="CmykColor"/> to a standard Unity RGB <see cref="Color"/>.
/// </summary>
/// <param name="cmyk">The CMYK color to convert.</param>
/// <returns>A Unity <see cref="Color"/> representing the same visual color.</returns>
public static Color ToColor(CmykColor cmyk)
{
var r = (1f - cmyk.c) * (1f - cmyk.k);
var g = (1f - cmyk.m) * (1f - cmyk.k);
var b = (1f - cmyk.y) * (1f - cmyk.k);

return new Color(MathUtilities.Clamp01(r), MathUtilities.Clamp01(g), MathUtilities.Clamp01(b), cmyk.a);
}

/// <summary>
/// Converts a standard Unity RGB <see cref="Color"/> to a <see cref="CmykColor"/>.
/// </summary>
/// <param name="color">The Unity Color to convert.</param>
/// <returns>A <see cref="CmykColor"/> representing the same visual color.</returns>
public static CmykColor FromColor(Color color)
{
var r = color.r;
var g = color.g;
var b = color.b;

var k = 1f - MathF.Max(r, MathF.Max(g, b));

if (k >= 1f)
return new CmykColor(0f, 0f, 0f, 1f, color.a);

var invK = 1f / (1f - k);

var c = (1f - r - k) * invK;
var m = (1f - g - k) * invK;
var y = (1f - b - k) * invK;

return new CmykColor(MathUtilities.Clamp01(c), MathUtilities.Clamp01(m), MathUtilities.Clamp01(y), MathUtilities.Clamp01(k), color.a);
}

/// <summary>
/// Linearly interpolates between two <see cref="CmykColor"/> values.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors, clamped to a 0 to 1 range.</param>
/// <returns>The interpolated <see cref="CmykColor"/>.</returns>
public static CmykColor Lerp(CmykColor a, CmykColor b, float t)
{
return LerpUnclamped(a, b, MathUtilities.Clamp01(t));
}

/// <summary>
/// Linearly interpolates between two <see cref="CmykColor"/> values without clamping the interpolant.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors.</param>
/// <returns>The interpolated <see cref="CmykColor"/>.</returns>
public static CmykColor LerpUnclamped(CmykColor a, CmykColor b, float t)
{
return new CmykColor(
MathUtilities.LerpUnclamped(a.c, b.c, t),
MathUtilities.LerpUnclamped(a.m, b.m, t),
MathUtilities.LerpUnclamped(a.y, b.y, t),
MathUtilities.LerpUnclamped(a.k, b.k, t),
MathUtilities.LerpUnclamped(a.a, b.a, t));
}
}
136 changes: 136 additions & 0 deletions MiraAPI/Colors/ColorSpaces/HsbColor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Diagnostics.CodeAnalysis;
using MiraAPI.Utilities;
using UnityEngine;

namespace MiraAPI.Colors.ColorSpaces;

/// <summary>
/// A struct that denotes the HSB (Hue, Saturation, Brightness) color space.
/// </summary>
/// <param name="h">The hue component.</param>
/// <param name="s">The saturation component.</param>
/// <param name="b">The brightness component.</param>
/// <param name="a">The alpha component.</param>
[Serializable]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Unity parity.")]
public record struct HsbColor(float h, float s, float b, float a = 1f)
{
/// <summary>
/// Implicitly converts an <see cref="HsbColor"/> to a Unity <see cref="Color"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator Color(HsbColor c)
{
return ToColor(c);
}

/// <summary>
/// Implicitly converts a Unity <see cref="Color"/> to an <see cref="HsbColor"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator HsbColor(Color c)
{
return FromColor(c);
}

/// <summary>
/// Converts an <see cref="HsbColor"/> to a standard Unity RGB <see cref="Color"/>.
/// </summary>
/// <param name="hsb">The HSB color to convert.</param>
/// <returns>A Unity <see cref="Color"/> representing the same visual color.</returns>
public static Color ToColor(HsbColor hsb)
{
if (hsb.s <= float.Epsilon)
return new Color(MathUtilities.Clamp01(hsb.b), MathUtilities.Clamp01(hsb.b), MathUtilities.Clamp01(hsb.b), hsb.a);

var c = hsb.b * hsb.s;
var hPrime = hsb.h * 6f;
var x = c * (1f - MathF.Abs((hPrime % 2f) - 1f));
var m = hsb.b - c;

var (r, g, b) = hPrime switch
{
< 1f => (c, x, 0f),
< 2f => (x, c, 0f),
< 3f => (0f, c, x),
< 4f => (0f, x, c),
< 5f => (x, 0f, c),
<= 6f => (c, 0f, x),
_ => (0f, 0f, 0f),
};

return new Color(
MathUtilities.Clamp01(r + m),
MathUtilities.Clamp01(g + m),
MathUtilities.Clamp01(b + m),
hsb.a);
}

/// <summary>
/// Converts a standard Unity RGB <see cref="Color"/> to an <see cref="HsbColor"/>.
/// </summary>
/// <param name="color">The Unity Color to convert.</param>
/// <returns>An <see cref="HsbColor"/> representing the same visual color.</returns>
public static HsbColor FromColor(Color color)
{
var max = MathF.Max(color.r, MathF.Max(color.g, color.b));
var min = MathF.Min(color.r, MathF.Min(color.g, color.b));
var delta = max - min;

var h = 0f;

if (delta > 0f)
{
if (max == color.r)
h = (color.g - color.b) / delta % 6f;
else if (max == color.g)
h = (color.b - color.r) / delta + 2f;
else
h = (color.r - color.g) / delta + 4f;

h /= 6f;

if (h < 0f)
h += 1f;
}

var s = max == 0f ? 0f : delta / max;
var b = max;

return new HsbColor(h, s, b, color.a);
}

/// <summary>
/// Linearly interpolates between two <see cref="HsbColor"/> values, using the shortest path for the hue.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors, clamped to a 0 to 1 range.</param>
/// <returns>The interpolated <see cref="HsbColor"/>.</returns>
public static HsbColor Lerp(HsbColor a, HsbColor b, float t)
{
return LerpUnclamped(a, b, MathUtilities.Clamp01(t));
}

/// <summary>
/// Linearly interpolates between two <see cref="HsbColor"/> values without clamping the interpolant.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors.</param>
/// <returns>The interpolated <see cref="HsbColor"/>.</returns>
public static HsbColor LerpUnclamped(HsbColor a, HsbColor b, float t)
{
var hueDifference = MathUtilities.Repeat(b.h - a.h, 1f);

if (hueDifference > 0.5f)
hueDifference -= 1f;

return new HsbColor(
MathUtilities.Repeat(a.h + (hueDifference * t), 1f),
MathUtilities.LerpUnclamped(a.s, b.s, t),
MathUtilities.LerpUnclamped(a.b, b.b, t),
MathUtilities.LerpUnclamped(a.a, b.a, t));
}
}
Loading
Loading