//! ---------------------------------------------------- //! ---------------------------------------------------- const std = @import("std"); const Vec = @import("vec.zig").Vec; /// ---------------------------------------------------- /// ---------------------------------------------------- pub const Color = extern struct { const Self = @This(); // // FIELDS // r: f32 = 0, g: f32 = 0, b: f32 = 0, a: f32 = 1, // // CONSTRUCTORS // /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn rgb(r: f32, g: f32, b: f32) Self { return .{ .r = r, .g = g, .b = b, .a = 1 }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn rgba(r: f32, g: f32, b: f32, a: f32) Self { return .{ .r = r, .g = g, .b = b, .a = a }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn hex(comptime value: u32) Self { const r = @as(f32, @as(u8, @truncate(value >> 16))) / 255.0; const g = @as(f32, @as(u8, @truncate(value >> 8))) / 255.0; const b = @as(f32, @as(u8, @truncate(value >> 0))) / 255.0; const a = if (value > 0xFFFFFF) @as(f32, @as(u8, @truncate(value >> 24))) / 255.0 else 1.0; return .{ .r = r, .g = g, .b = b, .a = a }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn fromVec4(v: Vec(f32, 4)) Self { return .{ .r = v.value[0], .g = v.value[1], .b = v.value[2], .a = v.value[3] }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn toVec4(self: Self) Vec(f32, 4) { return Vec(f32, 4).new(.{ self.r, self.g, self.b, self.a }); } // // ARITHMETIC // /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn add(self: Self, other: Self) Self { return .{ .r = self.r + other.r, .g = self.g + other.g, .b = self.b + other.b, .a = self.a + other.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn sub(self: Self, other: Self) Self { return .{ .r = self.r - other.r, .g = self.g - other.g, .b = self.b - other.b, .a = self.a - other.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn mul(self: Self, other: Self) Self { return .{ .r = self.r * other.r, .g = self.g * other.g, .b = self.b * other.b, .a = self.a * other.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn div(self: Self, other: Self) Self { return .{ .r = self.r / other.r, .g = self.g / other.g, .b = self.b / other.b, .a = self.a / other.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn scale(self: Self, scalar: f32) Self { return .{ .r = self.r * scalar, .g = self.g * scalar, .b = self.b * scalar, .a = self.a * scalar, }; } // // COLOR OPERATIONS // /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn lerp(self: Self, other: Self, t: f32) Self { return .{ .r = self.r + (other.r - self.r) * t, .g = self.g + (other.g - self.g) * t, .b = self.b + (other.b - self.b) * t, .a = self.a + (other.a - self.a) * t, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn blend(self: Self, other: Self) Self { const out_a = other.a + self.a * (1.0 - other.a); if (out_a == 0) return .{ .r = 0, .g = 0, .b = 0, .a = 0 }; return .{ .r = (other.r * other.a + self.r * self.a * (1.0 - other.a)) / out_a, .g = (other.g * other.a + self.g * self.a * (1.0 - other.a)) / out_a, .b = (other.b * other.a + self.b * self.a * (1.0 - other.a)) / out_a, .a = out_a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn premultiply(self: Self) Self { return .{ .r = self.r * self.a, .g = self.g * self.a, .b = self.b * self.a, .a = self.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn luminance(self: Self) f32 { return self.r * 0.2126 + self.g * 0.7152 + self.b * 0.0722; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn toLinear(self: Self) Self { const srgb_to_linear = struct { inline fn map(c: f32) f32 { if (c <= 0.04045) return c / 12.92; return std.math.pow(f32, (c + 0.055) / 1.055, 2.4); } }; return .{ .r = srgb_to_linear.map(self.r), .g = srgb_to_linear.map(self.g), .b = srgb_to_linear.map(self.b), .a = self.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn toSRGB(self: Self) Self { const linear_to_srgb = struct { inline fn map(c: f32) f32 { if (c <= 0.0031308) return c * 12.92; return 1.055 * std.math.pow(f32, c, 1.0 / 2.4) - 0.055; } }; return .{ .r = linear_to_srgb.map(self.r), .g = linear_to_srgb.map(self.g), .b = linear_to_srgb.map(self.b), .a = self.a, }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn toHSV(self: Self) struct { h: f32, s: f32, v: f32 } { const max = @max(self.r, @max(self.g, self.b)); const min = @min(self.r, @min(self.g, self.b)); const delta = max - min; const v = max; const s = if (max == 0) 0 else delta / max; var h: f32 = 0; if (delta != 0) { if (max == self.r) { h = 60.0 * @mod((self.g - self.b) / delta, 6.0); } else if (max == self.g) { h = 60.0 * ((self.b - self.r) / delta + 2.0); } else { h = 60.0 * ((self.r - self.g) / delta + 4.0); } } return .{ .h = h, .s = s, .v = v }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub inline fn fromHSV(h: f32, s: f32, v: f32) Self { const c = v * s; const hp = h / 60.0; const x = c * (1.0 - @abs(@mod(hp, 2.0) - 1.0)); const m = v - c; var r: f32 = 0; var g: f32 = 0; var b: f32 = 0; if (hp < 1) { r = c; g = x; } else if (hp < 2) { r = x; g = c; } else if (hp < 3) { g = c; b = x; } else if (hp < 4) { g = x; b = c; } else if (hp < 5) { r = x; b = c; } else { r = c; b = x; } return .{ .r = r + m, .g = g + m, .b = b + m, .a = 1 }; } // // CONSTANTS // pub const white = Self{ .r = 1, .g = 1, .b = 1, .a = 1 }; pub const black = Self{ .r = 0, .g = 0, .b = 0, .a = 1 }; pub const transparent = Self{ .r = 0, .g = 0, .b = 0, .a = 0 }; pub const red = Self{ .r = 1, .g = 0, .b = 0, .a = 1 }; pub const green = Self{ .r = 0, .g = 1, .b = 0, .a = 1 }; pub const blue = Self{ .r = 0, .g = 0, .b = 1, .a = 1 }; pub const yellow = Self{ .r = 1, .g = 1, .b = 0, .a = 1 }; pub const cyan = Self{ .r = 0, .g = 1, .b = 1, .a = 1 }; pub const magenta = Self{ .r = 1, .g = 0, .b = 1, .a = 1 }; pub const orange = Self{ .r = 1, .g = 0.55, .b = 0, .a = 1 }; pub const purple = Self{ .r = 0.5, .g = 0, .b = 0.5, .a = 1 }; pub const pink = Self{ .r = 1, .g = 0.75, .b = 0.8, .a = 1 }; pub const brown = Self{ .r = 0.6, .g = 0.3, .b = 0.1, .a = 1 }; pub const gray = Self{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 }; };