Skip to content

Latest commit

 

History

History
206 lines (174 loc) · 10 KB

gradients.org

File metadata and controls

206 lines (174 loc) · 10 KB

Contents

Namespace: thi.ng.color.gradients

Cosine based gradient generation

Based on method by developed iq (Iñigo Quílez): http://v.gd/B2aySt

Online gradient designer / editor: http://dev.thi.ng/gradients/

Presets

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):

http://media.thi.ng/color/presets/rainbow1.svghttp://media.thi.ng/color/presets/rainbow2.svg
:rainbow1:rainbow2
http://media.thi.ng/color/presets/rainbow3.svghttp://media.thi.ng/color/presets/rainbow4.svg
:rainbow3:rainbow4
http://media.thi.ng/color/presets/yellow-magenta-cyan.svghttp://media.thi.ng/color/presets/orange-blue.svg
:yellow-magenta-cyan preset:orange-blue
http://media.thi.ng/color/presets/green-magenta.svghttp://media.thi.ng/color/presets/green-red.svg
:green-magenta:green-red
http://media.thi.ng/color/presets/green-cyan.svghttp://media.thi.ng/color/presets/blue-cyan.svg
:green-cyan:blue-cyan
http://media.thi.ng/color/presets/yellow-red.svghttp://media.thi.ng/color/presets/red-blue.svg
:yellow-red:red-blue
http://media.thi.ng/color/presets/yellow-green-blue.svghttp://media.thi.ng/color/presets/blue-white-red.svg
:yellow-green-blue:blue-white-red
http://media.thi.ng/color/presets/cyan-magenta.svghttp://media.thi.ng/color/presets/yellow-purple-magenta.svg
:cyan-magenta:yellow-purple-magenta
http://media.thi.ng/color/presets/green-blue-orange.svghttp://media.thi.ng/color/presets/orange-magenta-blue.svg
:green-blue-orange:orange-magenta-blue
http://media.thi.ng/color/presets/blue-magenta-orange.svghttp://media.thi.ng/color/presets/magenta-green.svg
:blue-magenta-orange:magenta-green

Implementation

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)))))

Gradient coefficient calculation

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]])))

Example usage

;; 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]))

Gradient presets visualization

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")))))

Complete namespace definition

(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>>