Colors and gradients
Colors are used everywhere in data visualizations and -as a way of displaying data- they need to
be chosen with care to ensure that the perception of the color carry the right information.
Data2viz provides a lot of helpful functions to create several colors and gradients and still maintain
a high level of readability and accessibility in visuals.
In data2viz, colors are managed in their own module:
- import the dependency inside your project (io.data2viz.color)
- add the import directive in your code (import io.data2viz.color.*)
 
Color creation
The Colors object provides several ways to retrieve colors and gradients.
Web colors
The first option is to use a named color. All CSS colors are accessible as references through
the Colors.Web object.
For example, Colors.Web.darkturquoise returns a reference
on the dark turquoise html color. As colors are immutable objects, it's then possible to
reuse and pass references of these named colors.
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    viz {
        rect {
            size = size(50, 50)
            fill = Colors.Web.blueviolet
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Hex colors
Another usual option to create a color is through its hexadecimal code. There are
2 extension vals that simplify the creation of a color from Int and String.
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    viz {
        rect {
            size = size(50, 50)
            fill = 0x87ceeb.col        // <- Int extension val
        }
        rect {
            x = 50.0
            size = size(50, 50)
            fill = "#800080".col       // <- String extension val
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Color spaces
You can also use the values from 0 to 255 of the RGB channels to create a color calling
Colors.rgb.
Beside RGB, data2viz allows the use of different color spaces to create colors:
- HSL (Hue, Saturation, Lightness),
- HCL (Hue, Chroma, Lightness) and
- LAB (also known as CIE Lab).
For each of them a factory function is available in Colors, taking
the transparency alpha as a last parameter with a d
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
fun main() {
    //sampleStart
    viz {
        size = size(600, 50)
        rect {                      // pure red fill
            size = size(50, 50)
            fill = Colors.rgb(255, 0, 0)
        }
        rect {                      // filled with 50% transparency
            x = 50.0
            size = size(50, 50)
            fill = Colors.rgb(255, 0, 0, 50.pct)
        }
        rect {                      // fill defined in HSL color space
            x = 100.0
            size = size(50, 50)
            fill = Colors.hsl(38.82.deg, 100.pct, 50.pct)
        }
        rect {                      // fill defined in HCL color space
            x = 150.0
            size = size(50, 50)
            fill = Colors.hcl(167.95.deg, 46.55, 92.03.pct)
        }
        rect {                      // fill defined in LAB color space
            x = 200.0
            size = size(50, 50)
            fill = Colors.lab(30.83.pct, 26.05, -42.08)
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Color manipulation
Data2viz provides several functions to manipulate colors based on color perception.
Luminance & Contrast
Some color spaces like LAB or LCH use a parameter to determine the "lightness" of a color, but the
hue impacts the lightness we perceived from it (the luminance).
For example, blue and yellow seems to have very different brightness even if these 2 colors are created using
the same "lightness" parameter in HSL.
The luminance() function returns the perceived lightness of a given color.
The contrast we perceive is tightly bound to the luminance of 2 given colors. The contrast()
computes the perceived contrast ratio of 2 colors.
Check the WCAG for more info about contrast and readability.
 
import io.data2viz.viz.*
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.color.Colors.Web.white
import io.data2viz.color.Colors.Web.black
fun main() {
    viz {
        size = size(800, 250)
        //sampleStart
        (0 until 360 step 30).forEach {
            val angle = it.deg
            val position = point(250 + angle.cos * 100, 125 + angle.sin * 100)
            val color = Colors.hsl(angle, 100.pct, 50.pct)
            circle {                    // draw a circle with "pure-color"
                fill = color
                radius = 25.0
                x = position.x
                y = position.y
            }
            circle {                    // draw a circle with the desaturated color
                fill = color.desaturate(10.0)
                radius = 25.0
                x = position.x + 270
                y = position.y
            }
            text {                      // indicate the perceived lightness of the color
                x = position.x
                y = position.y
                textColor = if (color.luminance() > 50.pct) black else white
                textContent = "${(color.luminance().value*100).toInt()}%"
                textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
            }
        } //sampleEnd
        text {
            x = 250.0
            y = 125.0
            textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
            textContent = "LUMINANCE"
        }
        text {
            x = 520.0
            y = 125.0
            textAlign = textAlign(TextHAlign.MIDDLE, TextVAlign.MIDDLE)
            textContent = "DESATURATED COLORS"
        }
    }.bindRendererOnNewCanvas()
}
Change brightness
brighten() and darken() functions allow to easily change the brightness of a given color.
brighten(x) is equivalent to darken(-x)
 
import io.data2viz.viz.*
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
fun main() {
//sampleStart
    viz {
        size = size(600, 50)
        val myColor = Colors.hsl(260.deg, 100.pct, 10.pct)
        (0..10).forEach {
            rect {
                x = it * 50.0
                size = size(50, 50)
                fill = myColor.brighten(it / 2.0)
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Change saturation
The saturate() and desaturate() functions change the saturation of a given color.
saturate(x) is equivalent to desaturate(-x)
 
import io.data2viz.viz.*
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
fun main() {
//sampleStart
    viz {
        size = size(600, 50)
        val myColor = Colors.hsl(240.deg, 100.pct, 50.pct)
        (0..7).forEach {
            rect {
                x = it * 50.0
                size = size(50, 50)
                fill = myColor.desaturate(it.toDouble())
            }
        }
    }.bindRendererOnNewCanvas() //sampleEnd
}
Gradients
Most of the data2viz visual elements accepts a ColorOrGradient object for its fill color.
A gradient is defined by giving at least 2 ColorStop, each corresponding to a color and its position along
the gradient (in percentage).
Gradient positioning (as defined by its ColorStop) is absolute, not relative to the positions of the shapes using it.
 
Linear gradient
A Linear gradient is created using the Colors.Gradient.linear() builder.
- start: starting point of the gradient
- end: ending point of the gradient
Next you call withColor() given a Color and a percentage to set the base color then add any number of ColorStop
using andColor().
 
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
fun main() {
//sampleStart
   viz {
       size = size(800, 100)
       val linearGradient = Colors.Gradient.linear(point(0, 0), point(800, 0))
          .withColor(Colors.Web.hotpink, 20.pct)      // under 20% color is "hot pink"
          .andColor(Colors.Web.blueviolet, 50.pct)    // middle (50%) is "blue violet"
          .andColor(Colors.Web.skyblue, 80.pct)       // from 80% color is "sky blue"
       // we set stroke & strokewidth here, and use style delegation to pass it down
       stroke = linearGradient
       strokeWidth = 30.0
       line {
           x1 = 100.0
           y1 = 18.0
           x2 = 500.0
           y2 = 18.0
       }
       line {
           x1 = 300.0
           y1 = 50.0
           x2 = 700.0
           y2 = 50.0
       }
       line {
           x1 = .0
           y1 = 82.0
           x2 = 800.0
           y2 = 82.0
       }
   }.bindRendererOnNewCanvas() //sampleEnd
}
Radial gradient
If you want to paint a shape using a radial gradient, use the Colors.Gradient.radial() builder.
- center: starting point of the gradient
- radius: radius of the gradient
Next you call withColor() given a Color and a percentage to set the base color then add any number of ColorStop
using andColor().
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
fun main() {
   //sampleStart
   val radialGradient = Colors.Gradient.radial(point(400, 100), 100.0)
       .withColor(Colors.Web.hotpink, 0.pct)       // gradient center is "hot pink"
       .andColor(Colors.Web.skyblue, 100.pct)      // gradient end "is sky blue"
   viz {
       size = size(800, 200)
       circle {
           x = 400.0
           y = 100.0
           radius = 100.0
           fill = radialGradient
       }
   }.bindRendererOnNewCanvas() //sampleEnd
}