# Colours! - A colourful demonstration of R's graphics. # Copyright (C) December 2008 Neil Fraser # http://neil.fraser.name/ # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General # Public License as published by the Free Software Foundation. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # http://www.gnu.org/ # Height and width of the image. # All values work, but 2^n+1 squares are ideal (65x65, 129x129, 257x257, etc). rows = 257 cols = 257 # Pick a random number between two end values. midpoint = function(end1, end2) { if (end1 > end2) { return(runif(1, end2, end1)) } else { return(runif(1, end1, end2)) } } # Recursively fill one row. # Used to bootstrap the recursive area fill. # Also used to fudge our way out of ?x2 rectangles. fillrow = function(grid, row, endleft, endright) { if (endleft + 1 == endright) return(grid) # Set the dot in the middle. middleX = floor((endright - endleft) / 2 + endleft) grid[middleX, row] = midpoint(grid[endleft, row], grid[endright, row]) # Go fill the two new sub-rows grid = fillrow(grid, row, endleft, middleX) grid = fillrow(grid, row, middleX, endright) return(grid) } # Recursively fill one column. # Used to bootstrap the recursive area fill. # Also used to fudge our way out of ?x2 rectangles. fillcol = function(grid, col, endtop, endbottom) { if (endtop + 1 == endbottom) return(grid) # Set the dot in the middle. middleY = floor((endbottom - endtop) / 2 + endtop) grid[col, middleY] = midpoint(grid[col, endtop], grid[col, endbottom]) # Go fill the two new sub-columns grid = fillcol(grid, col, endtop, middleY) grid = fillcol(grid, col, middleY, endbottom) return(grid) } # Recursively fill the area. fillarea = function(grid, endtop, endbottom, endleft, endright) { if ((endleft + 1 == endright) && (endtop + 1 == endbottom)) { # This is just a 2x2 square. Nothing to do. } else if (endleft + 1 == endright) { # This is just a 2x? rectangle. Fill in some vertical space. grid = fillcol(grid, endright, endtop, endbottom) } else if (endtop + 1 == endbottom) { # This is just a ?x2 rectangle. Fill in some horizontal space. grid = fillrow(grid, endbottom, endleft, endright) } else { # Wide open space; something to sink our recursive teeth into... # Set the dot half way along the bottom row. middleX = floor((endright - endleft) / 2 + endleft) grid[middleX, endbottom] = midpoint(grid[endleft, endbottom], grid[endright, endbottom]) # Set the dot half way along the right column. middleY = floor((endbottom - endtop) / 2 + endtop) grid[endright, middleY] = midpoint(grid[endright, endtop], grid[endright, endbottom]) # Set the dot in the middle (midpoint of two midpoints). centre1 = midpoint(grid[middleX, endtop], grid[middleX, endbottom]) centre2 = midpoint(grid[endright, middleY], grid[endleft, middleY]) grid[middleX, middleY] = midpoint(centre1, centre2) # Go fill the four new quadrants. grid = fillarea(grid, endtop, middleY, endleft, middleX) grid = fillarea(grid, middleY, endbottom, endleft, middleX) grid = fillarea(grid, endtop, middleY, middleX, endright) grid = fillarea(grid, middleY, endbottom, middleX, endright) } return(grid) } # The grid of points that make up the image. grid = array(0, dim=c(cols, rows)) # Flip a coin to determine which diagonally opposite corners get the master colours. if (runif(1) < 0.5) { grid[1, 1] = 0 grid[cols, rows] = 1 grid[cols, 1] = runif(1) grid[1, rows] = runif(1) } else { grid[1, rows] = 0 grid[cols, 1] = 1 grid[1, 1] = runif(1) grid[cols, rows] = runif(1) } # Draw the top row and left column. grid = fillrow(grid, 1, 1, cols) grid = fillcol(grid, 1, 1, rows) # Now we have the top edge, the left edge, and the bottom/right dot. # We are all setup to fill the remaining area. grid = fillarea(grid, 1, rows, 1, cols) # RGB colour declarations for the master colours while (TRUE) { c1 = runif(3) c2 = runif(3) # Keep looping until you get dissimilar colours. if ((abs(c1[1] - c2[1]) + abs(c1[2] - c2[2]) + abs(c1[3] - c2[3])) > 1) { break } } rgb.palette = colorRampPalette(c(rgb(c1[1], c1[2], c1[3]), rgb(c2[1], c2[2], c2[3]))) # Output the image image(grid, col=rgb.palette(255), xaxt="n", yaxt="n") # Add some text. text(1, 0, "http://neil.fraser.name/", adj=c(0,0), srt=90)