// Colours! - A colourful demonstration of POV-Ray's programming. // Copyright (C) September 2004 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). #declare rows = 257; #declare cols = 257; // The grid of points that make up the image. #declare grid = array[cols][rows]; // Create two random number streams. // Change this seed for different colours. #declare RND1 = seed(19); // Change this seed for different patterns. #declare RND2 = seed(19); // Initialise the grid to a neutral colour. // Only needed if part of the fill is disabled. // #declare fillx = 0; // #while (fillx < cols) // #declare filly = 0; // #while (filly < rows) // #declare grid[fillx][filly] = 0.5; // #declare filly=filly+1; // #end // #declare fillx=fillx+1; // #end // midpoint(end1, end2) => number // Pick a random number between two end values. #macro midpoint(end1, end2) (rand(RND2) * abs(end2 - end1) + min(end1, end2)) #end // Recursively fill one row. // Used to bootstrap the recursive area fill. // Also used to fudge our way out of ?x2 rectangles. #macro fillrow(row, endleft, endright) #if (endleft + 1 != endright) // Set the dot in the middle. #local middleX = floor((endright - endleft) / 2 + endleft); #declare grid[middleX][row] = midpoint(grid[endleft][row], grid[endright][row]); // Go fill the two new sub-rows fillrow(row, endleft, middleX) fillrow(row, middleX, endright) #end #end // Recursively fill one column. // Used to bootstrap the recursive area fill. // Also used to fudge our way out of ?x2 rectangles. #macro fillcol(col, endtop, endbottom) #if (endtop + 1 != endbottom) // Set the dot in the middle. #local middleY = floor((endbottom - endtop) / 2 + endtop); #declare grid[col][middleY] = midpoint(grid[col][endtop], grid[col][endbottom]); // Go fill the two new sub-columns fillcol(col, endtop, middleY) fillcol(col, middleY, endbottom) #end #end // fillarea(endtop, endbottom, endleft, endright) // Recursively fill the area. #macro fillarea (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. fillcol(endright, endtop, endbottom) #else #if (endtop + 1 = endbottom) // This is just a ?x2 rectangle. Fill in some horizontal space. fillrow(endbottom, endleft, endright) #else // Wide open space; something to sink our recursive teeth into... // Set the dot half way along the bottom row. #local middleX = floor((endright - endleft) / 2 + endleft); #declare grid[middleX][endbottom] = midpoint(grid[endleft][endbottom], grid[endright][endbottom]); // Set the dot half way along the right column. #local middleY = floor((endbottom - endtop) / 2 + endtop); #declare grid[endright][middleY] = midpoint(grid[endright][endtop], grid[endright][endbottom]); // Set the dot in the middle (midpoint of two midpoints). #local centre1 = midpoint(grid[middleX][endtop], grid[middleX][endbottom]); #local centre2 = midpoint(grid[endright][middleY], grid[endleft][middleY]); #declare grid[middleX][middleY] = midpoint(centre1, centre2); // Go fill the four new quadrants. fillarea(endtop, middleY, endleft, middleX) fillarea(middleY, endbottom, endleft, middleX) fillarea(endtop, middleY, middleX, endright) fillarea(middleY, endbottom, middleX, endright) #end #end #end #end // Flip a coin to determine which diagonally opposite corners get the master colours. #if (rand(RND2) >= .5) #declare grid[0][0] = 0; #declare grid[cols-1][rows-1] = 1.0; #declare grid[cols-1][0] = rand(RND2); #declare grid[0][rows-1] = rand(RND2); #else #declare grid[0][rows-1] = 0; #declare grid[cols-1][0] = 1.0; #declare grid[0][0] = rand(RND2); #declare grid[cols-1][rows-1] = rand(RND2); #end // Draw the bottom row and left column. fillrow(0, 0, cols-1) fillcol(0, 0, rows-1) // Now we have the bottom edge, the left edge, and the top/right dot. // We are all setup to fill the remaining area. fillarea(0, rows-1, 0, cols-1) // RGB colour declarations for the master colours #declare c1r = 0; #declare c1g = 0; #declare c1b = 0; #declare c2r = 0; #declare c2g = 0; #declare c2b = 0; #while ((abs(c1r - c2r) + abs(c1g - c2g) + abs(c1b - c2b)) < 1.0) #declare c1r = rand(RND1); #declare c1g = rand(RND1); #declare c1b = rand(RND1); #declare c2r = rand(RND1); #declare c2g = rand(RND1); #declare c2b = rand(RND1); #end // Norally at this stage we precompute all the colours. // But POV-Ray uses floats for colours, not integers. // Draw a coloured cube for each pixel. #declare drawx = 0; #while (drawx < cols) #declare drawy = 0; #while (drawy < rows) #declare r = c1r * grid[drawx][drawy] + c2r * (1 - grid[drawx][drawy]); #declare g = c1g * grid[drawx][drawy] + c2g * (1 - grid[drawx][drawy]); #declare b = c1b * grid[drawx][drawy] + c2b * (1 - grid[drawx][drawy]); box { texture { pigment { color rgb } } } #declare drawy=drawy+1; #end #declare drawx=drawx+1; #end // Add some text. text { ttf "tahoma.ttf" "http://neil.fraser.name/" 0.1, 0 pigment { color rgb <0.5, 0.5, 0.5> } rotate 90*z scale <10, 10, 1> translate } // Manually back the camera away from larger grids camera { orthographic location <0, 0, -300> look_at <0, 0, 0> } light_source { <0, 0, -100> color rgb <1.0, 1.0, 1.0> parallel point_at <0, 0, 0> }