Chromatic Scales
Chromatic Scales are scales that output colors.
The ScalesChromatic object offers somes factories for creating pre-configured color scales:
| Scales factory ScalesChromatic.*
 | Type | Subtype | Example of use | 
|---|
| Continuous.* linearRGB...
 | Continuous |  | Create your own linear color scale | 
| Discrete.* dark8, pale12...
 | Discrete |  | Display distinct categories with no specific order | 
| Sequential.SingleHue.* blues, greens, reds...
 | Sequential | Single hue | Show continuous data on a single-color scheme | 
| Sequential.MultiHue.* viridis, plasma...
 | Sequential | Multi hue | Show continuous data on multi-color scheme (better for large domains) | 
| Sequential.Diverging.* spectral, red_blue...
 | Sequential | Diverging | Highlight divergence of continuous data (temperatures...) | 
| Sequential.Cyclical.* rainbow...
 | Sequential | Cyclical | Good for radial visuals as start color == end color
 | 
Scales are managed in there own module. You have to import the dependency inside your project
(io.data2viz.scale) and in the import directive in your code.
 
Continuous scales
The ScalesChromatic.Continuous.* object contains factories to create linear color scales.
These scales already contains a color interpolator so you just need to set the domain and the range, as you
would do with a numeric linear scale.
There are several color interpolators available:
- defaultRGB: simple RGB interpolator
- linearRGB: better results on bright colors than the previous one
- linearLAB: interpolator in the LAB color space
- linearHSL,- linearHCL: interpolators in HSL and HCL color spaces
- linearHSLLong,- linearHCLLong: same as above, but if your colors are separated with more
than 180° on the chromatic wheel these interpolators will take the "longest way" from one color to another
Learn more about the bias of "default RGB interpolation" in this video.
 
import io.data2viz.color.*
import io.data2viz.scale.*
import io.data2viz.math.*
import io.data2viz.geom.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    val valuesDomain = listOf(.0, 100.0)
    val colorRange = listOf("#33A7D8".col, "#FECE3E".col)
    // scale with linear interpolation in 2 color spaces : RGB & HCL
    val scaleRGB = ScalesChromatic.Continuous.linearRGB {
        domain = valuesDomain
        range = colorRange
    }
    val scaleHCL = ScalesChromatic.Continuous.linearHCL {
        domain = valuesDomain
        range = colorRange
    }
    viz {
        size = size(800, 50)
        (0..100).forEach {
            rect {
              x = 10 + it * 5.0
              size = size(5, 19)
              fill = scaleRGB(it)
            }
            rect {
              x = 10 + it * 5.0
              y = 31.0
              size = size(5, 19)
              fill = scaleHCL(it)
            }
            if (it % 10 == 0) {
                text {
                    x = 10 + it * 5.0
                    y = 26.0
                    textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
                    textContent = "$it"
                }
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Discrete scales
Discrete scales are pre-configured color scales map colors to different categories.
To mark the difference between comparable objects (where we would use gradients of colors) these scales propose
some color schemes with very distinct colors.
You can create your own category color scale using the Scales.Discrete.* object, but
the ScalesChromatic.Discrete factory will offer some ready to use color scales.
The factories names finish with a number indicating the total colors in the range of the scale,
ScalesChromatic.Discrete.accent8 for example contains 8 distinct colors.
 
The colors in these scales are mostly derived from Cynthia A. Brewer’s ColorBrewer.
 
import io.data2viz.color.*
import io.data2viz.scale.*
import io.data2viz.math.*
import io.data2viz.geom.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    val days = listOf("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday")
    // this scale map names of the days (as String) to colors
    val scale = ScalesChromatic.Discrete.category10<String> { domain = days }
    viz {
        size = size(800, 50)
        days.forEachIndexed { index, dayName ->
            text {
                x = 30 + index * 70.0
                y = 25.0
                textColor = scale(dayName)
                textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
                textContent = "$dayName"
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Sequential scales
Sequential scales are pre-configured scales, the color scheme is already defined so the range is fixed.
These scales are distributed among 4 categories in ScalesChromatic.Sequential.*
Single hue scales
Use ScalesChromatic.Sequential.SingleHue.* to create a new single hue scale.
import io.data2viz.color.*
import io.data2viz.scale.*
import io.data2viz.math.*
import io.data2viz.geom.*
import io.data2viz.viz.*
import io.data2viz.color.Colors.Web.white
import io.data2viz.color.Colors.Web.black
fun main() {
    //sampleStart
    // for a sequential scale, the range is already defined
    val scale = ScalesChromatic.Sequential.SingleHue.purples {
        domain = StrictlyContinuous(0.0, 40.0)
    }
    viz {
        size = size(800, 50)
        (0..40).forEach {
            val color = scale(it.toDouble())
            rect {
                x = it * 17.0
                size = size(16, 50)
                fill = color
            }
            text {
                x = 8 + it * 17.0
                y = 25.0
                textColor = if (color.luminance() > 50.pct) black else white
                textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
                textContent = "$it"
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Multi hue scales
Use ScalesChromatic.Sequential.MultiHue.* to create a new multi hue scale.
import io.data2viz.color.*
import io.data2viz.scale.*
import io.data2viz.math.*
import io.data2viz.geom.*
import io.data2viz.viz.*
import io.data2viz.color.Colors.Web.white
import io.data2viz.color.Colors.Web.black
fun main() {
    //sampleStart
    // for a sequential scale, the range is already defined
    val scale = ScalesChromatic.Sequential.MultiHue.viridis {
        domain = StrictlyContinuous(0.0, 40.0)
    }
    viz {
        size = size(800, 50)
        (0..40).forEach {
            val color = scale(it.toDouble())
            rect {
                x = it * 17.0
                size = size(16, 50)
                fill = color
            }
            text {
                x = 8 + it * 17.0
                y = 25.0
                textColor = if (color.luminance() > 50.pct) black else white
                textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
                textContent = "$it"
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Diverging scales
Use ScalesChromatic.Sequential.Diverging.* to create a new diverging scale.
import io.data2viz.color.*
import io.data2viz.scale.*
import io.data2viz.math.*
import io.data2viz.geom.*
import io.data2viz.viz.*
import io.data2viz.color.Colors.Web.white
import io.data2viz.color.Colors.Web.black
fun main() {
    //sampleStart
    // note the inverted domain to bind blue to -15 and red to +15
    val scale = ScalesChromatic.Sequential.Diverging.red_yelow_blue() {
        domain = StrictlyContinuous(15.0, -15.0)
    }
    viz {
        size = Size(800.0, 50.0)
        (-15..15).forEach {
            val color = scale(it.toDouble())
            rect {
                x = (it + 15) * 21.0
                size = size(20, 50)
                fill = color
            }
            text {
                x = 10 + (it + 15) * 21.0
                y = 25.0
                textColor = if (color.luminance() > 50.pct) black else white
                textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
                textContent = "$it"
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Cyclical scales
Use ScalesChromatic.Sequential.Cyclical.* to create a new diverging scale.
Cyclical color scales obviously do not clamp data, they just cycle over when you scale a value which is outside
the domain.
 
import io.data2viz.color.*
import io.data2viz.math.*
import io.data2viz.scale.*
import io.data2viz.geom.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    // on this cyclical scale: scale(0) == scale(360) == scale(720)...
    val scale = ScalesChromatic.Sequential.Cyclical.sineBow {
        domain = StrictlyContinuous(.0, 360.0)
    }
    viz {
        size = size(800, 200)
        (0..360).forEach {
            line {
                val angle = it.deg
                x1 = 300.0 + 60 * angle.cos
                x2 = 300.0 + 100 * angle.cos
                y1 = 100.0 + 60 * angle.sin
                y2 = 100.0 + 100 * angle.sin
                stroke = scale(it.toDouble())
                strokeWidth = 3.0
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}