Based on method by developed iq (Iñigo Quílez): http://v.gd/B2aySt
Online gradient designer / editor: http://dev.thi.ng/gradients/
Here we define a number of RGB gradient presets, primarily useful for data visualization purposes:
(def cosine-schemes
{:rainbow1 [[0.5 0.5 0.5] [0.5 0.5 0.5] [1.0 1.0 1.0] [0 0.3333 0.6666]]
:rainbow2 [[0.5 0.5 0.5] [0.666 0.666 0.666] [1.0 1.0 1.0] [0 0.3333 0.6666]]
:rainbow3 [[0.5 0.5 0.5] [0.75 0.75 0.75] [1.0 1.0 1.0] [0 0.3333 0.6666]]
:rainbow4 [[0.5 0.5 0.5] [1 1 1] [1.0 1.0 1.0] [0 0.3333 0.6666]]
:yellow-magenta-cyan [[1 0.5 0.5] [0.5 0.5 0.5] [0.75 1.0 0.6666] [0.8 1.0 0.3333]]
:orange-blue [[0.5 0.5 0.5] [0.5 0.5 0.5] [0.8 0.8 0.5] [0 0.2 0.5]]
:green-magenta [[0.6666 0.5 0.5] [0.5 0.6666 0.5] [0.6666 0.666 0.5] [0.2 0.0 0.5]]
:green-red [[0.5 0.5 0] [0.5 0.5 0] [0.5 0.5 0] [0.5 0.0 0]]
:green-cyan [[0.0 0.5 0.5] [0 0.5 0.5] [0.0 0.3333 0.5] [0.0 0.6666 0.5]]
:yellow-red [[0.5 0.5 0] [0.5 0.5 0] [0.1 0.5 0] [0.0 0.0 0]]
:blue-cyan [[0.0 0.5 0.5] [0 0.5 0.5] [0.0 0.5 0.3333] [0.0 0.5 0.6666]]
:red-blue [[0.5 0 0.5] [0.5 0 0.5] [0.5 0 0.5] [0 0 0.5]]
:yellow-green-blue [[0.650 0.5 0.310] [-0.650 0.5 0.6] [0.333 0.278 0.278] [0.660 0.0 0.667]]
:blue-white-red [[0.660 0.56 0.680] [0.718 0.438 0.720] [0.520 0.8 0.520] [-0.430 -0.397 -0.083]]
:cyan-magenta [[0.610 0.498 0.650] [0.388 0.498 0.350] [0.530 0.498 0.620] [3.438 3.012 4.025]]
:yellow-purple-magenta [[0.731 1.098 0.192] [0.358 1.090 0.657] [1.077 0.360 0.328] [0.965 2.265 0.837]]
:green-blue-orange [[0.892 0.725 0.000] [0.878 0.278 0.725] [0.332 0.518 0.545] [2.440 5.043 0.732]]
:orange-magenta-blue [[0.821 0.328 0.242] [0.659 0.481 0.896] [0.612 0.340 0.296] [2.820 3.026 -0.273]]
:blue-magenta-orange [[0.938 0.328 0.718] [0.659 0.438 0.328] [0.388 0.388 0.296] [2.538 2.478 0.168]]
:magenta-green [[0.590 0.811 0.120] [0.410 0.392 0.590] [0.940 0.548 0.278] [-4.242 -6.611 -4.045]]})
Diagrams of the presets showing their channel curves and resulting gradients (using 100 samples):
The two functions below implement the gradient generation function:
cosine-gradient-color
to compute single colors and
cosine-gradient-scheme
to create a vector of n tuples covering the
full gradient range.
Note: These function are not restricted to RGB colors and can be used in many other contexts - think of it as an ND waveform generator…
(defn cosine-gradient-color
[offset amp fmod phase t]
(col/rgba
(mapv
(fn [a b c d] (m/clamp (+ a (* b (Math/cos (* TWO_PI (+ (* c t) d))))) 0 1))
offset amp fmod phase)))
(defn cosine-gradient
"Takes a length n and 4 cosine coefficients (for colors usually 3 or
4-element vectors) and produces vector of n new RGBA colors, with
each of its elements defined by an AM & FM cosine wave and clamped
to the [0 1] interval. The fn `t` is used to ramp the gradient and
remap the interpolation interval (e.g. use m/mix-circular)"
([n spec]
(apply cosine-gradient n m/mix* spec))
([n t spec]
(apply cosine-gradient n t spec))
([n t offset amp fmod phase]
(mapv #(cosine-gradient-color offset amp fmod phase (t 0 1 %)) (m/norm-range (dec n)))))
Based on @dimovich’s gist, this function computes the cosine gradient coefficients for a gradient between two given colors.
(defn cosine-coefficients
"Computes coefficients defining a cosine gradient between
the two given colors. The colors can be in any color space,
but the resulting gradient will always be computed in RGB.
amp = (R1 - R2) / 2
dc = R1 - amp
freq = -0.5"
([c1 c2]
(let [colors (map #(select-keys (col/as-rgba %) [:r :g :b]) [c1 c2])
amp (apply mapv (fn [[_ v1] [_ v2]] (* 0.5 (- v1 v2))) colors)
offset (mapv (fn [[_ v1] a] (- v1 a)) (first colors) amp)]
[offset
amp
[-0.500 -0.500 -0.500]
[0.000 0.000 0.000]])))
;; using a preset
(def my-grad
(->> :red-blue
(grad/cosine-schemes)
(grad/cosine-gradient 100)))
;; specifying cosine coefficients directly
(def my-grad
(grad/cosine-gradient 100 [0.5 0 0.5] [0.5 0 0.5] [0.5 0 0.5] [0 0 0.5]))
This following snippet is not part of the library and only used to create the above preset visualizations. Requires http://thi.ng/geom to be added to your REPL/project in order to run…
When tangling this file, the code below will be saved in the
/babel/dev/
subdir of this project…
(require '[thi.ng.color.core :as col])
(require '[thi.ng.color.gradients :as grad])
(require '[thi.ng.geom.viz.core :as viz])
(require '[thi.ng.geom.svg.core :as svg])
(require '[thi.ng.math.core :as m])
(defn channel-specs
[colors]
(map-indexed
(fn [idx col]
{:values (viz/uniform-domain-points [0 1] (map #(nth (deref %) idx) colors))
:attribs {:fill "none" :stroke col}
:layout viz/svg-line-plot})
["red" "green" "blue"]))
(defn color-bars
[x1 x2 y w h colors]
(let [n (dec (count colors))]
(for [i (m/norm-range n)]
(svg/rect [(m/mix* x1 x2 i) y] w h {:fill (colors (int (* i n)))}))))
(doseq [[id coeffs] grad/cosine-schemes]
(let [colors (grad/cosine-gradient 100 coeffs)]
(->> {:x-axis (viz/linear-axis
{:domain [0 1] :range [50 580] :major 0.5 :minor 0.125 :pos 250})
:y-axis (viz/linear-axis
{:domain [0 1] :range [250 20] :major 0.2 :minor 0.1 :pos 50
:label-dist 15 :label-style {:text-anchor "end"}})
:grid {:minor-x true :minor-y true}
:data (channel-specs colors)}
(viz/svg-plot2d-cartesian)
(svg/svg
{:width 600 :height 300}
(color-bars 50 570 280 10 20 colors))
(svg/serialize)
(spit (str (name id) ".svg")))))
(ns thi.ng.color.gradients
#?(:cljs
(:require-macros
[thi.ng.math.macros :as mm]))
(:require
[thi.ng.math.core :as m :refer [PI TWO_PI]]
[thi.ng.color.core :as col]
#?(:clj [thi.ng.math.macros :as mm])))
<<generators>>