4

I've looked at this one but does not help with the random part. Is there a better way to randomly generate 3 different colors so that the square, the circle, and the text stand out visually from one another in the code below. The colors have to be randomly generated and also distinct enough from one another. The current code works maybe only half the time

plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
    ann = FALSE, axes = FALSE, asp = 1)

cols = colorRampPalette(sample(2:9,2), alpha = TRUE)(8)

polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])

symbols(x = 5, y = 5, circles = 4, inches = FALSE,
    add = TRUE, bg = cols[4], fg = NA)

text(x = 5, y = 5, labels = "Hi", col = cols[7], font = 2, cex = 3)

EXAMPLES

GOOD

enter image description here

POLYGON: "#FF00FFFF", CIRCLE: "#916DFFFF", TEXT: "#24DAFFFF"

BAD

enter image description here

POLYGON: "#00FFFFFF", CIRCLE: "#51E3E3FF", TEXT: "#A2C7C7FF"

Community
  • 1
  • 1
d.b
  • 32,245
  • 6
  • 36
  • 77
  • 2
    Can you define 'stand out better from each other'? Why is this not a simple case of maximizing the numerical distance in RGB- or HSV-space (or some other transform space)? It would probably help immensely if you post a couple of good-and-bad images and label each with the text(#TTTTTT), circle(#CCCCCC), square(#SSSSSS) values used, so readers can get a feel for this. – smci Apr 14 '17 at 23:11
  • 1
    d.b, calculate us the (Euclidean?) distance in both RGB- and HSV- space for both those color-palettes, normalized to a unit cube/cylinder. Like I said, I think that should illuminate things. This is called **color difference** – smci Apr 14 '17 at 23:25
  • **Near-identical question: [How to compare two colors for similarity/difference](http://stackoverflow.com/questions/9018016/how-to-compare-two-colors)**, but more general – smci Apr 14 '17 at 23:30
  • If we arbitrarily set the constraint that the circle is black(/white), does that make this question more objective? – smci Apr 14 '17 at 23:36
  • some nice java answers [here](http://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors) could readily be ported to R. Also the `library(randomcoloR)` has a neat function `distinctColorPalette()` – dww Apr 15 '17 at 02:08

2 Answers2

4

My original answer, which uses the hcl color space, often generated color combinations that were hard to distinguish. This updated answer uses the Lab color space, which is scaled based on the perceptual distance between colors, so similar distances in Lab space should correspond to similar perceptual color differences. In Lab, L is luminance or brightness on a scale of 0 to 100. a represents green to red, and b represents blue to yellow, with both on a scale of -100 to 100.

The code below generates two random values for a and b. If we think of these two values as representing a point in the ab plane, we generate two more colors with maximum perceptual distance from each other by rotating this point first by 120 degrees and then by 240 degrees. We then choose a single L value to give us three equally spaced colors.

Below I've packaged this in a function to make it easy to generate several plots with random colors. I've also set a minimum absolute value for a and b so that we don't get colors that are too similar, and included an Lval argument for choosing the L value of the Lab colors.

Based on a several runs, it looks like this approach performs much better than my original hcl version (although this may be due not only to the use of Lab space instead of hcl space, but also because I used only one dimension of hcl space but two dimensions of Lab space).

library(colorspace)

random.colors = function(Lval=80, ABmin=50) {
  
  # 120 deg rotation matrix
  aa = 2*pi/3
  rot = matrix(c(cos(aa), -sin(aa), sin(aa), cos(aa)), nrow=2, byrow=TRUE)
  
  # Generate random A and B points in LAB space
  x = runif(2, ABmin, 100) * sample(c(-1,1), 2,replace=TRUE) 
  
  # Create three equally spaced colors in Lab space and convert to RGB
  cols = LAB(cbind(rep(Lval,3), rbind(x, x %*% rot, x %*% rot %*% rot)))
  cols = rgb(convertColor(cols@coords, from="Lab", to="sRGB"))
  
  plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
       ann = FALSE, axes = FALSE, asp = 1)
  
  polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])
  
  symbols(x = 5, y = 5, circles = 4, inches = FALSE,
          add = TRUE, bg = cols[2], fg = NA)
  
  text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)
}

par(mfrow=c(3,3), mar=rep(0,4))
replicate(9,random.colors())  

enter image description here

For simplicity, the example above constrains the a and b values to be a constant distance from the origin (in ab-space) and uses the same L value for all three colors. You could instead extend this method to use all three dimensions of the Lab space. In addition, instead of requiring a constant distance from the origin, you could pick the first color at random and then require that the next two colors be picked such that all three colors are maximally separated from each other in Lab space.

Original Answer

You could generate colors that are equally spaced in the hue dimension (that is, have the maximum possible hue separation from each other). For example:

set.seed(60)
cols = hcl(runif(1,0,359.99) + c(0,120,240), 100, 65)

plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
     ann = FALSE, axes = FALSE, asp = 1)

polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])

symbols(x = 5, y = 5, circles = 4, inches = FALSE,
        add = TRUE, bg = cols[2], fg = NA)

text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)

enter image description here

Here are nine more random draws. As you can see, there are some combinations that don't work too well. But perhaps you can play around with different ways of slicing up the color space to see if you can get something better.

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
2

One super simple way would be to sample among the eight "standard" colours.

par(mar=c(0, 0, 0, 0))
set.seed(1)
plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
    ann = FALSE, axes = FALSE, asp = 1)

cols <- sample(2:8, 3)

polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])

symbols(x = 5, y = 5, circles = 4, inches = FALSE,
    add = TRUE, bg = cols[2], fg = NA)

text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)

enter image description here

AkselA
  • 8,153
  • 2
  • 21
  • 34