A discrete color palette generator with support for fixed colors, optimized for color vision deficient viewers. Features different optimization algorithms and a multi-objective optimization framework for advanced color palette generation.
Installation
You can install the development version of huerd from GitHub with:
# install.packages("pak")
pak::pak("sims1253/huerd")Basic Example
Generate a palette with 5 colors:
library(huerd)
palette <- generate_palette(8, progress = FALSE)
print(palette)
#>
#> -- huerd Color Palette (8 colors) --
#> Colors:
#> [ 1] #6C0000
#> [ 2] #007F77
#> [ 3] #0084FF
#> [ 4] #FF0000
#> [ 5] #E900FF
#> [ 6] #9792B2
#> [ 7] #00B1EA
#> [ 8] #00DBFF
#>
#> -- Quality Metrics Summary --
#> * Min. Perceptual Distance (OKLAB): 0.115
#> * Optimizer Performance Ratio : 37.0%
#> * Min. CVD-Safe Distance (OKLAB) : 0.066
#>
#> -- Generation Details --
#> * Optimizer Iterations: 487
#> * Optimizer Status: NLOPT_XTOL_REACHED: Optimization stopped because xtol_rel or xtol_abs (above) was reached.Constrained Color Palettes
Include specific colors while optimizing the remaining colors:
library(huerd)
palette <- generate_palette(
n = 8,
include_colors = c("#4A6B8A", "#E5A04C"),
progress = FALSE
)
print(palette)
#>
#> -- huerd Color Palette (8 colors) --
#> Colors:
#> [ 1] #700000
#> [ 2] #114661
#> [ 3] #A73800
#> [ 4] #4A6B8A
#> [ 5] #CE673B
#> [ 6] #00AAAD
#> [ 7] #E5A04C
#> [ 8] #FFC7D8
#>
#> -- Quality Metrics Summary --
#> * Min. Perceptual Distance (OKLAB): 0.132
#> * Optimizer Performance Ratio : 42.7%
#> * Min. CVD-Safe Distance (OKLAB) : 0.130
#>
#> -- Generation Details --
#> * Optimizer Iterations: 533
#> * Optimizer Status: NLOPT_XTOL_REACHED: Optimization stopped because xtol_rel or xtol_abs (above) was reached.Multi-Optimizer Support
Choose from 4 different optimization algorithms based on your needs:
library(huerd)
# COBYLA: Default deterministic optimizer for general use
cobyla_palette <- generate_palette(6, optimizer = "nloptr_cobyla", progress = FALSE)
# SANN: Stochastic simulated annealing for higher quality
sann_palette <- generate_palette(6, optimizer = "sann", progress = FALSE)
# DIRECT: Global optimization for reproducibility (may need tuning)
direct_palette <- generate_palette(6, optimizer = "nlopt_direct", progress = FALSE)
# Nelder-Mead: Derivative-free local optimization
# As an alternative deterministic approach
neldermead_palette <- generate_palette(6, optimizer = "nlopt_neldermead", progress = FALSE)
cat("COBYLA:", paste(cobyla_palette, collapse = ", "), "\n")
#> COBYLA: #2600BA, #970000, #0000FF, #008A00, #FF0000, #BAA69E
cat("SANN:", paste(sann_palette, collapse = ", "), "\n")
#> SANN: #300000, #6E0000, #0000D7, #00A100, #CE74FF, #00FFFF
cat("DIRECT:", paste(direct_palette, collapse = ", "), "\n")
#> DIRECT: #636363, #636363, #636363, #636363, #636363, #636363
cat("Nelder-Mead:", paste(neldermead_palette, collapse = ", "), "\n")
#> Nelder-Mead: #9D0000, #FF0000, #FF77C5, #A7C100, #FFB8A2, #00FBFFMulti-Objective Framework
The package includes a multi-objective optimization framework:
library(huerd)
# Current: Pure distance optimization (default)
distance_palette <- generate_palette(
n = 6,
weights = c(distance = 1), # Explicit distance weighting
optimizer = "nloptr_cobyla",
progress = FALSE
)
# Future versions will support additional objectives like:
# weights = c(distance = 0.7, aesthetics = 0.3)
# weights = c(distance = 0.8, uniformity = 0.2)
print(distance_palette)
#>
#> -- huerd Color Palette (6 colors) --
#> Colors:
#> [ 1] #001E00
#> [ 2] #A50000
#> [ 3] #FF0000
#> [ 4] #00A5FF
#> [ 5] #00FFFF
#> [ 6] #FFF300
#>
#> -- Quality Metrics Summary --
#> * Min. Perceptual Distance (OKLAB): 0.189
#> * Optimizer Performance Ratio : 51.6%
#> * Min. CVD-Safe Distance (OKLAB) : 0.136
#>
#> -- Generation Details --
#> * Optimizer Iterations: 329
#> * Optimizer Status: NLOPT_XTOL_REACHED: Optimization stopped because xtol_rel or xtol_abs (above) was reached.Diagnostic Dashboard
Get a quick overview of your palette properties:
library(huerd)
palette <- generate_palette(8, progress = FALSE)
plot_palette_analysis(palette, force_font_scale = 0.6)
Palette Quality Evaluation
Or look at the numerical evaluation results:
library(huerd)
palette <- generate_palette(8, progress = FALSE)
evaluation <- evaluate_palette(palette)
# Access raw metrics (no subjective scoring)
cat("Minimum distance:", evaluation$distances$min, "\n")
#> Minimum distance: 0.2073131
cat("Performance ratio:", evaluation$distances$performance_ratio * 100, "%\n")
#> Performance ratio: 66.9047 %
cat("CVD worst case:", evaluation$cvd_safety$worst_case_min_distance, "\n")
#> CVD worst case: 0.08815642Custom Parameters
Fine-tune the generation process with advanced options:
library(huerd)
palette <- generate_palette(
n = 8,
initialization = "harmony", # Color harmony-based initialization
init_lightness_bounds = c(0.3, 0.8), # Constrain lightness range
max_iterations = 2000, # Increased iterations
optimizer = "nloptr_cobyla", # Use COBYLA for optimization
progress = FALSE
)
print(palette)
#>
#> -- huerd Color Palette (8 colors) --
#> Colors:
#> [ 1] #CE4572
#> [ 2] #00AC8F
#> [ 3] #00BACF
#> [ 4] #A6AF5F
#> [ 5] #B9AFFC
#> [ 6] #D0BE8F
#> [ 7] #FFB9E7
#> [ 8] #67FFA4
#>
#> -- Quality Metrics Summary --
#> * Min. Perceptual Distance (OKLAB): 0.093
#> * Optimizer Performance Ratio : 29.9%
#> * Min. CVD-Safe Distance (OKLAB) : 0.065
#>
#> -- Generation Details --
#> * Optimizer Iterations: 573
#> * Optimizer Status: NLOPT_XTOL_REACHED: Optimization stopped because xtol_rel or xtol_abs (above) was reached.Complete Workflow Example
library(huerd)
# 1. Generate brand palette with advanced optimization
brand_palette <- generate_palette(
n = 8,
include_colors = c("#1f77b4", "#ff7f0e"), # Fixed brand colors
fixed_aesthetic_influence = 0.9,
initialization = "harmony",
optimizer = "sann",
max_iterations = 5000,
weights = c(distance = 1),
return_metrics = TRUE,
progress = TRUE
)
#> Preparing for palette generation...
#> Adapting initialization from fixed colors' aesthetics...
#> Initializing 6 free colors (method: harmony)...
#> Optimizing 6 free colors using sann...
#> Finalizing palette...
#> Done.
# 2. Diagnostic analysis
plot_palette_analysis(brand_palette, force_font_scale = 0.6)
# 3. Quality evaluation
evaluation <- evaluate_palette(brand_palette)
cat("Min distance:", round(evaluation$distances$min, 3), "\n")
#> Min distance: 0.18
cat("Performance:", round(evaluation$distances$performance_ratio * 100, 1), "%\n")
#> Performance: 57.9 %
# 4. CVD accessibility check
cvd_safe <- is_cvd_safe(brand_palette)
if (cvd_safe) {
cat("Palette is CVD-accessible\n")
} else {
cat("Palette may challenge CVD viewers\n")
}
#> Palette is CVD-accessible
# 5. CVD simulation for verification
cvd_simulation <- simulate_palette_cvd(brand_palette, cvd_type = "all")
print(cvd_simulation)
#>
#> -- huerd CVD Simulation Result (Multiple Types, Severity: 1.00) --
#> Palette for: original
#> [ 1] #450000
#> [ 2] #005800
#> [ 3] #3100DD
#> [ 4] #1F77B4
#> [ 5] #FF0055
#> [ 6] #FF7F0E
#> [ 7] #F5B7FF
#> [ 8] #45FF00
#> Palette for: protan
#> [ 1] #181400
#> [ 2] #5A4E00
#> [ 3] #004EE2
#> [ 4] #5A79B7
#> [ 5] #666355
#> [ 6] #A59100
#> [ 7] #B0C8FF
#> [ 8] #FFE600
#> Palette for: deutan
#> [ 1] #292300
#> [ 2] #52480D
#> [ 3] #003ADA
#> [ 4] #456CB3
#> [ 5] #9F914E
#> [ 6] #C4AE05
#> [ 7] #BECFFD
#> [ 8] #F1D83A
#> Palette for: tritan
#> [ 1] #4D0001
#> [ 2] #005549
#> [ 3] #005B81
#> [ 4] #00868D
#> [ 5] #FF0032
#> [ 6] #FF616D
#> [ 7] #F7BED1
#> [ 8] #00F7D9
# 6. Display final palette (colors are brightness-sorted)
print(brand_palette)
#>
#> -- huerd Color Palette (8 colors) --
#> Colors:
#> [ 1] #450000
#> [ 2] #005800
#> [ 3] #3100DD
#> [ 4] #1F77B4
#> [ 5] #FF0055
#> [ 6] #FF7F0E
#> [ 7] #F5B7FF
#> [ 8] #45FF00
#>
#> -- Quality Metrics Summary --
#> * Min. Perceptual Distance (OKLAB): 0.180
#> * Optimizer Performance Ratio : 57.9%
#> * Min. CVD-Safe Distance (OKLAB) : 0.093
#>
#> -- Generation Details --
#> * Optimizer Iterations: 5000
#> * Optimizer Status: Optimization converged