Initial commit
This commit is contained in:
commit
c9105bf4ce
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/target
|
||||||
|
.direnv/
|
||||||
|
.envrc
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
#
|
||||||
|
# already existing elements were commented out
|
||||||
|
|
||||||
|
#/target
|
||||||
|
/Cargo.lock
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "unicornlor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
69
flake.lock
Normal file
69
flake.lock
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1659877975,
|
||||||
|
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1664178928,
|
||||||
|
"narHash": "sha256-+WVCZH/3Ifef4Da9N1tkGnmfX0QwtkJQz013QuImu10=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b542cc75fa03a3a29350d4c3b69739e946268a93",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-22.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1664247556,
|
||||||
|
"narHash": "sha256-J4vazHU3609ekn7dr+3wfqPo5WGlZVAgV7jfux352L0=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "524db9c9ea7bc7743bb74cdd45b6d46ea3fcc2ab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
64
flake.nix
Normal file
64
flake.nix
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs = {
|
||||||
|
type = "github";
|
||||||
|
owner = "NixOS";
|
||||||
|
repo = "nixpkgs";
|
||||||
|
ref = "nixos-22.05";
|
||||||
|
};
|
||||||
|
|
||||||
|
flake-utils = {
|
||||||
|
type = "github";
|
||||||
|
owner = "numtide";
|
||||||
|
repo = "flake-utils";
|
||||||
|
ref = "master";
|
||||||
|
};
|
||||||
|
|
||||||
|
rust-overlay = {
|
||||||
|
type = "github";
|
||||||
|
owner = "oxalica";
|
||||||
|
repo = "rust-overlay";
|
||||||
|
ref = "master";
|
||||||
|
inputs = {
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
|
nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
flake-utils,
|
||||||
|
nixpkgs,
|
||||||
|
rust-overlay,
|
||||||
|
}: let
|
||||||
|
inherit (flake-utils.lib) eachSystem system;
|
||||||
|
mySystems = [
|
||||||
|
system.aarch64-linux
|
||||||
|
system.x86_64-darwin
|
||||||
|
system.x86_64-linux
|
||||||
|
];
|
||||||
|
eachMySystem = eachSystem mySystems;
|
||||||
|
in
|
||||||
|
eachMySystem (system: let
|
||||||
|
overlays = [(import rust-overlay)];
|
||||||
|
pkgs = import nixpkgs {inherit overlays system;};
|
||||||
|
my-rust = pkgs.rust-bin.stable.latest.default.override {
|
||||||
|
extensions = ["rust-src"];
|
||||||
|
};
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
in {
|
||||||
|
devShells = {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
rust-analyzer
|
||||||
|
# Clippy, rustfmt, etc...
|
||||||
|
my-rust
|
||||||
|
];
|
||||||
|
|
||||||
|
RUST_SRC_PATH = "${my-rust}/lib/rustlib/src/rust/library";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
135
src/lib.rs
Normal file
135
src/lib.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct HsvColor {
|
||||||
|
h: f32,
|
||||||
|
s: f32,
|
||||||
|
v: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HsvColor {
|
||||||
|
pub fn new(h: f32, s: f32, v: f32) -> Self {
|
||||||
|
Self { h, s, v }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the color to its RGB equivalent.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the color has a Hue value outside of the range `0..=360`.
|
||||||
|
pub fn to_rgb(self) -> RgbColor {
|
||||||
|
// See https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
|
||||||
|
let chroma = self.s * self.v;
|
||||||
|
let h_prime = self.h / 60.0;
|
||||||
|
// second largest component
|
||||||
|
let second = chroma * (1.0 - ((h_prime % 2.0) - 1.0).abs());
|
||||||
|
let (r, g, b) = match h_prime {
|
||||||
|
h if (0.0..1.0).contains(&h) => (chroma, second, 0.0),
|
||||||
|
h if (1.0..2.0).contains(&h) => (second, chroma, 0.0),
|
||||||
|
h if (2.0..3.0).contains(&h) => (0.0, chroma, second),
|
||||||
|
h if (3.0..4.0).contains(&h) => (0.0, second, chroma),
|
||||||
|
h if (4.0..5.0).contains(&h) => (second, 0.0, chroma),
|
||||||
|
h if (5.0..=6.0).contains(&h) => (chroma, 0.0, second),
|
||||||
|
_ => panic!(
|
||||||
|
"Unexpected Hue value during HSV to RGB conversion: {}",
|
||||||
|
self.h
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let m = self.v - chroma;
|
||||||
|
let (r, g, b) = (r + m, g + m, b + m);
|
||||||
|
let (r, g, b) = (r * 255.0, g * 255.0, b * 255.0);
|
||||||
|
let (r, g, b) = (r.round(), g.round(), b.round());
|
||||||
|
debug_assert!((0.0..=255.0).contains(&r));
|
||||||
|
debug_assert!((0.0..=255.0).contains(&g));
|
||||||
|
debug_assert!((0.0..=255.0).contains(&b));
|
||||||
|
let (r, g, b) = (r as u8, g as u8, b as u8);
|
||||||
|
|
||||||
|
RgbColor { r, g, b }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct RgbColor {
|
||||||
|
r: u8,
|
||||||
|
g: u8,
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RgbColor {
|
||||||
|
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
|
Self { r, g, b }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u32(self) -> u32 {
|
||||||
|
(self.r as u32) << 16 | (self.g as u32) << 8 | (self.b as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not_so_random_color() -> RgbColor {
|
||||||
|
HsvColor::new(rand::thread_rng().gen_range(0.0..360.0), 0.5, 0.9).to_rgb()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgb_to_u32() {
|
||||||
|
assert_eq!(RgbColor::new(255, 255, 255).to_u32(), 0xFFFFFF);
|
||||||
|
assert_eq!(RgbColor::new(0, 0, 0).to_u32(), 0x000000);
|
||||||
|
assert_eq!(RgbColor::new(255, 0, 0).to_u32(), 0xFF0000);
|
||||||
|
assert_eq!(RgbColor::new(0, 0, 255).to_u32(), 0x0000FF);
|
||||||
|
assert_eq!(RgbColor::new(0, 255, 0).to_u32(), 0x00FF00);
|
||||||
|
assert_eq!(RgbColor::new(82, 252, 3).to_u32(), 0x52FC03);
|
||||||
|
assert_eq!(RgbColor::new(3, 32, 252).to_u32(), 0x0320FC);
|
||||||
|
assert_eq!(RgbColor::new(86, 3, 252).to_u32(), 0x5603FC);
|
||||||
|
assert_eq!(RgbColor::new(252, 136, 3).to_u32(), 0xFC8803);
|
||||||
|
assert_eq!(RgbColor::new(252, 40, 3).to_u32(), 0xFC2803);
|
||||||
|
assert_eq!(RgbColor::new(3, 252, 211).to_u32(), 0x03FCD3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hsv_to_rgb() {
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(217.0, 0.73, 0.96).to_rgb(),
|
||||||
|
RgbColor::new(66, 135, 245)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(122.0, 0.51, 0.61).to_rgb(),
|
||||||
|
RgbColor::new(76, 156, 79)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(54.0, 0.91, 0.85).to_rgb(),
|
||||||
|
RgbColor::new(217, 197, 20)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(0.0, 0.73, 0.96).to_rgb(),
|
||||||
|
RgbColor::new(245, 66, 66)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(360.0, 0.73, 0.96).to_rgb(),
|
||||||
|
RgbColor::new(245, 66, 66)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(0.0, 0.0, 0.0).to_rgb(),
|
||||||
|
RgbColor::new(0, 0, 0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(0.0, 0.0, 1.0).to_rgb(),
|
||||||
|
RgbColor::new(255, 255, 255)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(0.0, 1.0, 1.0).to_rgb(),
|
||||||
|
RgbColor::new(255, 0, 0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(120.0, 1.0, 1.0).to_rgb(),
|
||||||
|
RgbColor::new(0, 255, 0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
HsvColor::new(240.0, 1.0, 1.0).to_rgb(),
|
||||||
|
RgbColor::new(0, 0, 255)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue