#!/usr/bin/env python3 # # Walk through the color spectrum in an orderly fashion and display the colors in Inkscape, # Language: Simple Inkscape Scripting (SIS, apparently) # Details: https://inkscape.org/~pakin/%E2%98%85simple-inkscape-scripting # Github: https://github.com/spakin/SimpInkScr # Developed against Python 3.8.10 # rgbx version 1.0 # # Copyright 2022 Joseph Smith # # Permission to use, copy, modify, and/or distribute this software for any purpose with or without # fee is hereby granted, provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS # SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # OF THIS SOFTWARE. # Changes: # - exceedpageborders -> spillover # - objectstroke -> objectstrokecolor # - dropped support for buggy page dimensions handling (pagewidth, pageheight) # - simplified and improved skip/shuffle/printvalues handling (separate functions) # TODO: maybe: fieldalign (left/center/right), for multiple runs # Bugs/shortcomings # - turnaround only works with plainrgb color set, which suggests it doesn't survive a recharge # - no repetitive output with colorfile # - FIXED: fieldkip plus shufflecolors will shuffle skipped fields as well # - FIXED: fieldskip plus invertcolors: TypeError: unsupported operand type(s) for -: 'int' and 'str' # - FIXED: controlling the script from a config file is cumbersome (added GUI) # -------------------------------------------------------------------------------------------------- # Q and A secion. # # Q: why not group functions neatly into objects? # A: because doing so would bring lots of overhead with little or no real gain. # # Q: why so many global variables? # A: because having them snake through the code in the form of function parameters would result in # a maintenance nightmare (tried and failed). # # Having said that, the initialization section must rank high among the ugliest code blocks ever # written, but no amount of de-globalization or objectification would fix that. # -------------------------------------------------------------------------------------------------- import os, sys import os.path # Config file written by RGBX-GUI CONFIGFILE = 'rgbx-config.txt' # ================================================================================================== # Function definitions # CONFIG # Parse configuration file; sets globals directly def loadconfig(cffile): # For maintainers to maintain: expected types CAN be speciified; without, everything returns as str. # Lists of (quoted) config var names. expectint = ['backgroundcolor', 'colorstep', 'fieldnum', 'fieldheight', 'fieldsep', 'fieldskip', 'skipoffset', 'turnaround', 'huerotation', 'objectsize', 'minobjectsize', 'objectstrokewidth', 'rotatecolor'] expectfloat = ['fieldopacity', 'objectopacity'] # List of config params read from the file. cflist = [] # Read in file contents. with open(cffile) as fp: lines = fp.readlines() for line in lines: if '#' in line: parts = line.split('#') line = parts[0].rstrip() else: line = line.rstrip() if '=' in line: entry = line.split('=') cfname = entry[0].strip() cfval = entry[1].strip() cfval = cfval.replace("'", "") cfval = cfval.replace('"', "") configentry = [cfname, cfval] cflist.append(configentry) fp.close() # Set global variables. for i in range(len(cflist)): name = cflist[i][0] value = cflist[i][1] if (value.startswith("0x")): iv = int(value[2:], 16) globals()[name] = int(iv) elif name in expectint: iv = int('%s' % value, 0) globals()[name] = iv elif name in expectfloat: fv = float('%s' % value) globals()[name] = float(fv) else: globals()[name] = value return() # =============================================================================== # HIGH-LEVEL FUNCTIONS # - all of these are called from the main code # - most of these prepare the final print list # Create a list of fields to be printed or skipped. # This is in fact the MAIN LOOP - the highest-level function in this script. def createprintlist(): global RGBCODE, CHANGEOP, fieldcount for i in range(fieldnum): # Update the print list, keeping field skips into account PRINTLIST.append(RGBCODE) # Change values of color bytes for next rum RGBCODE = updatergb(RGBCODE) # Switch roles of color bytes after every N runs if ((i + 1) % turnaround == 0): # R-B-G to R-G-B output: reverse shift # Works only with an M/R index swap at initialization; see initlmr() if (swapgb == 'yes'): shiftlmr('right') else: shiftlmr('left') if (colorset == 'web'): # No need to recharge bytes # The shift has taken care of the correct index switch (M becomes former L/M) if (CHANGEOP == 'inc'): CHANGEOP = 'dec' elif (CHANGEOP == 'dec'): CHANGEOP = 'inc' elif (colorset != 'plainrgb'): # Recharge bytes (set values high) RGBCODE = rechargergb(RGBCODE) # Reshuffle print list. def handleshuffle(): if (shufflecolors == 'yes'): from random import random, shuffle shuffle(PRINTLIST) # Invert colors. def handleinvert(): if (invertcolors == 'yes'): for i in range(len(PRINTLIST)): PRINTLIST[i] = 0xffffff - PRINTLIST[i] # For handleskips(): decide whether to skip a field. # Assumes (should only be calles with) a fieldskip value > 0. def testskip(fnum): skip = 0 if (fnum < skipoffset): skip = 1 # Complex condition, admittedly rooted in trial, error and heuristics. elif ((fnum - skipoffset) % (fieldskip + 1) != 0): skip = 1 if (invertskip == 'yes'): skip = 1 - skip return(skip) # Handle field skip. def handleskips(): global PRINTLIST, skipcount # No need to traverse the entire print list once more if fieldskip equals 0. if (fieldskip > 0): for i in range(fieldnum): if testskip(i): PRINTLIST[i] = 'skip' # Print color values. def handleprintvalues(): if (printvalues == 'yes'): for i in range(len(PRINTLIST)): if (PRINTLIST[i] != 'skip'): print('%d: %06x' % (i+1, PRINTLIST[i])) # Read 0x-less hex rgb codes from a file to be used as print list. def readcolorfile(rgbf): rgbl = [] with open(rgbf, 'r') as fp: line = fp.readline() while line: if '#' in line: parts = line.split('#') line = parts[0] line = line.strip() if line: rgbl.append(str2hex(line)) line = fp.readline() fp.close() return(rgbl) # =============================================================================== # COLOR SET PROCESSING # Change RGB byte values for next rum (called from main loop) def updatergb(rgbval): if (colorset == 'darkrgb'): # RGB colors: decrement R bytes only rgbval = changecolorbytes(rgbval, rgblist[lindex], 'ignore', 'ignore', -1, 0, 0, colorstep) elif (colorset == 'plainrgb'): # Plainrgb colors: increment L bytes, decrement R bytes; the W bytes remain 00 throughout rgbval = changecolorbytes(rgbval, rgblist[lindex], 'ignore', rgblist[rindex], -1, 0, 1, colorstep) elif (colorset == 'hybrid'): # Hybrid colors: increment L bytes, decrement W and R bytes rgbval = changecolorbytes(rgbval, rgblist[lindex], rgblist[mindex], rgblist[rindex], -1, -1, 1, colorstep) elif (colorset == 'lightrgb'): # Bright colors: decrement W bytes only rgbval = changecolorbytes(rgbval, 'ignore', rgblist[mindex], 'ignore', 0, -1, 0, colorstep) elif (colorset == 'darkmyc'): # MYC colors: decrement W and R bytes rgbval = changecolorbytes(rgbval, rgblist[lindex], 'ignore', rgblist[mindex], -1, 0, -1, colorstep) elif (colorset == 'web'): # 'Web safe' colors: decrement M __OR__ increment R bytes, depending on direction if (CHANGEOP == 'inc'): rgbval = changecolorbytes(rgbval, 'ignore', rgblist[mindex], 'ignore', 0, 1, 0, colorstep) elif (CHANGEOP == 'dec'): rgbval = changecolorbytes(rgbval, 'ignore', rgblist[mindex], 'ignore', 0, -1, 0, colorstep) return(rgbval) # Recharge bytes (set them to 0xff or ceiling value) def rechargergb(rgbval): if (colorset == 'hybrid'): rgbval = chargecolorbytes(rgbval, rgblist[mindex]) elif (colorset == 'darkmyc'): rgbval = chargecolorbytes(rgbval, rgblist[mindex]) rgbval = chargecolorbytes(rgbval, rgblist[lindex]) elif (colorset == 'lightrgb'): rgbval = chargecolorbytes(rgbval, rgblist[lindex]) elif (colorset == 'darkrgb'): ### Could handle turnaround here rgbval = chargecolorbytes(rgbval, rgblist[lindex]) return(rgbval) # ------------------------------------------------------------------------------- # LOW LEVEL RGB MANAGEMENT # Reset color values (vulnerable to overflow) def chargecolorbytes(clr, clname): curvalue = getbyteval(clr, clname) if (clname == 'r'): ceiling = CEILINGR #threshold = THRESHOLDR elif (clname == 'g'): ceiling = CEILINGG #threshold = THRESHOLDG elif (clname == 'b'): ceiling = CEILINGB #threshold = THRESHOLDB else: return(-1) newvalue = ceiling - curvalue newclr = setbyteval(clr, newvalue, clname) return(newclr) # Change the rgb value # The order of names doesn't really matter, as long as signs correspond # If a name is tagged 'ignore', the value of the sign argument is irrelevant def changecolorbytes(clr, lname, wname, rname, lsign, wsign, rsign, inc): newrgbv = clr cnames = (lname, wname, rname) # values: 'r', 'g', 'b', 'ignore' csigns = (lsign, wsign, rsign) # values: -1, 1 for i in range(len(cnames)): if (cnames[i] != 'ignore'): newrgbv = setbyteval(newrgbv, csigns[i] * inc, cnames[i]) return(newrgbv) # Change the value of a byte for specified color (vulnerable to overflow) def setbyteval(clr, clbytes, clname): if (clname == 'r'): newclr = clr + clbytes * 0x10000 elif (clname == 'g'): newclr = clr + clbytes * 0x100 elif (clname == 'b'): newclr = clr + clbytes else: return(-1) return(newclr) # Retrieve the value of a byte for specified color def getbyteval(clr, clname): if (clname == 'r'): # The modulo operation flushes out any overflow clbytes = int(clr / 0x10000) % 0x100 elif (clname == 'g'): clbytes = int(clr / 0x100) % 0x100 elif (clname == 'b'): clbytes = int(clr) % 0x100 else: return(-1) return(clbytes) # ------------------------------------------------------------------------------- # INDEX/SWITCH HANDLING FOR MAIN LOOP # Set initial indices of RGB bytes in rgblist: 0, 1, 2; with red startcolor: R, G, B # This is the only occasion where LMR meets RGB concretely, and only through the L index # R and M values (positions in RGB list) are relative to that of L # The heart of rotational complexity beats here; see the README about LMR vs LRM def initlmr(rgboffs): global rindex, mindex, lindex lindex = rgboffs # L: offset in {r, g, b} list if (swapgb == 'yes'): # This ALSO requires a shift R i.o. a shift L mindex = (lindex + 2) % 3 # M = L + 2 } rindex = (mindex + 2) % 3 # R = L + 1 } => LRM (swapped scheme RBG) else: rindex = (lindex + 2) % 3 # R = L + 2 } mindex = (rindex + 2) % 3 # M = L + 1 } => LMR (plainrgb scheme RGB) # Handle R/L index shifts # Shift values: 'left', 'right'; anything other means no shift is performed def shiftlmr(shift): global rindex, mindex, lindex if (shift == 'left'): # used with LMR (plainrgb scheme RGB) # new X = old X - 1: left shift rindex = (rindex + 2) % 3 mindex = (mindex + 2) % 3 lindex = (lindex + 2) % 3 if (shift == 'right'): # NEEDED with LRM (swapped scheme RBG) # new X = old X + 1: right shift rindex = (rindex + 4) % 3 mindex = (mindex + 4) % 3 lindex = (lindex + 4) % 3 # ------------------------------------------------------------------------------- # FIELD PRINTING # Print items from the rgb list taking field skips into account # This one should be easy to replace with various other routines # E.g. print a bunch of circles (or Super Marios) using the colors from the list def printfields(plist): global ymin for i in range(len(plist)): if (plist[i] != 'skip'): if (type(plist[i]) == int): value = plist[i] else: value = int(plist[i], 0) fld = fieldprep(xmin, ymin, xmin + fieldwidth, ymin + fieldheight, value) l1.add(fld) # The '- 1' should largely do away with annoying 1px-wide stipes ymin = ymin + fieldheight + fieldsep - stripefix # Examine field printing conditions; handle color inversion def fieldprep(x0, y0, x1, y1, fcolor): # Wrap color if its value exceeds the 000000-ffffff range. Best effort / quick fix. # Could be handled per byte instead, with equally little meaning if (wrapcolors == 'yes'): if (fcolor < 0): fcolor = -fcolor while (fcolor > 0xffffff): fcolor = int(fcolor / 0x10) # Print first, adjust indices later fld = writefield(x0, y0, x1, y1, fcolor) return(fld) # Function: display a color field def writefield(x0, y0, x1, y1, clr): fieldcolor = hex2str(clr) f = rect((x0, y0), (x1, y1), stroke=0, stroke_width=0, opacity='%f' % fieldopacity, fill = '#%s' % fieldcolor) return(f) def writebackground(x0, y0, x1, y1, clr): fieldcolor = hex2str(clr) f = rect((x0, y0), (x1, y1), stroke=0, stroke_width=0, fill = '#%s' % fieldcolor) return(f) # ------------------------------------------------------------------------------- # LOW LEVEL # Turn a numeric color value into a 6-digit (padded) hex string def hex2str(value, chars = 6): return('{0:0{1}x}'.format(value, chars)) # Turn a hex color string into a numeric value # Unused, but has won its spurs for debugging def str2hex(string): rv = int('0x%s' % string, 0) return(rv) def restrict(v, minval, maxval): if v < minval: return minval if v > maxval: return maxval return(v) # =============================================================================== # BONUS CODE: print objects i.o printfields # TODO: provide a better hook mechanism for insertable code. # Return a random object position def getobjectpos(pwidth, pheight, size): if (spillover == 'yes'): (x, y) = uniform(0, pwidth), uniform(0, pheight) else: (x, y) = uniform(size, pwidth - size), uniform(size, pheight - size) return(x, y) # Print randomly positioned squares using colors generated by this script def printsquares(plist): for i in range(len(plist)): if (plist[i] != 'skip'): (x, y) = getobjectpos(pagewidth, pageheight, objectsize) if (minobjectsize != 0): xsize = uniform(int(minobjectsize), int(objectsize)) size = (xsize, xsize) else: size = (float(objectsize), float(objectsize)) sqr = rect((0, 0), size, transform='translate(%g, %g)' % (x, y), stroke='%s' % objectstrokecolor, stroke_width='%d' % objectstrokewidth, opacity='%f' % float(objectopacity), fill='#%06x' % plist[i]) l1.add(sqr) # Print randomly positioned rectangles using colors generated by this script # TODO: with an objectsize only things get rather vague (maybe add rectangle ratio to the config) def printrectangles(plist): for i in range(len(plist)): if (plist[i] != 'skip'): (x, y) = getobjectpos(pagewidth, pageheight, objectsize) if (minobjectsize != 0): xsize = uniform(int(minobjectsize), int(objectsize)) ysize = uniform(int(minobjectsize), int(objectsize)) size = (xsize, ysize) else: # This is a bit random xsize = uniform(0, int(objectsize) * 2) ysize = uniform(0, int(objectsize) * 2) size = (xsize, ysize) rct = rect((0, 0), size, transform='translate(%g, %g)' % (x, y), stroke='%s' % objectstrokecolor, stroke_width='%d' % objectstrokewidth, opacity='%f' % float(objectopacity), fill='#%06x' % plist[i]) l1.add(rct) # Print randomly positioned circles using colors generated by this script def printcircles(plist): for i in range(len(plist)): if (plist[i] != 'skip'): (x, y) = getobjectpos(pagewidth, pageheight, objectsize) if (minobjectsize != 0): size = uniform(int(minobjectsize), int(objectsize)) else: size = float(objectsize) # Ovals: ckl = circle((x, y), 50, transform='translate(%g, %g) scale(0.75, 1) rotate(45)' % (x, y), fill='#%06x' % PRINTLIST[i]) ckl = circle((0, 0), size, transform='translate(%g, %g)' % (x, y), stroke='%s' % objectstrokecolor, stroke_width='%d' % objectstrokewidth, opacity='%f' % float(objectopacity), fill='#%06x' % plist[i]) l1.add(ckl) # Print randomly positioned triangles in randomized perspective using colors generated by this script def printtriangles(plist): for i in range(len(plist)): if (plist[i] != 'skip'): (x, y) = getobjectpos(pagewidth, pageheight, objectsize) if (minobjectsize != 0): size = uniform(int(minobjectsize), int(objectsize)) else: size = float(objectsize) trg = regular_polygon(3, (x, y), size, 2, 0.0, 0.7, stroke='%s' % objectstrokecolor, stroke_width='%d' % objectstrokewidth, opacity='%f' % float(objectopacity), fill='#%06x' % plist[i]) l1.add(trg) # =============================================================================== # COLOR SET 'ROTATE' # For color set 'rotate': rotate the hue by a specified amount of degrees. # Code credit: Mark Ransom on Stack Overflow; adapted for specific use # Original code: https://stackoverflow.com/questions/8507885/shift-hue-of-an-rgb-color from math import sqrt,cos,sin,radians rgbmatrix = [[1,0,0],[0,1,0],[0,0,1]] def clamp(v): if v < 0x00: return 0x00 if v > 0xff: return 0xff return int(v + 0.5) def rotatehue(degrees): cosA = cos(radians(degrees)) sinA = sin(radians(degrees)) rgbmatrix[0][0] = cosA + (1.0 - cosA) / 3.0 rgbmatrix[0][1] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA rgbmatrix[0][2] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA rgbmatrix[1][0] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA rgbmatrix[1][1] = cosA + 1./3.*(1.0 - cosA) rgbmatrix[1][2] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA rgbmatrix[2][0] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA rgbmatrix[2][1] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA rgbmatrix[2][2] = cosA + 1./3. * (1.0 - cosA) def getrotatedvalue(rgbval): r = getbyteval(rgbval, 'r') g = getbyteval(rgbval, 'g') b = getbyteval(rgbval, 'b') rx = r * rgbmatrix[0][0] + g * rgbmatrix[0][1] + b * rgbmatrix[0][2] gx = r * rgbmatrix[1][0] + g * rgbmatrix[1][1] + b * rgbmatrix[1][2] bx = r * rgbmatrix[2][0] + g * rgbmatrix[2][1] + b * rgbmatrix[2][2] rx = clamp(rx) gx = clamp(gx) bx = clamp(bx) rv = 0x10000 * rx + 0x100 * gx + bx return(rv) def getnexthue(rgbv, hrot): rotatehue(hrot) newrgb = getrotatedvalue(rgbv) if (printvalues == 'yes'): print('%06x' % RGBCODE) return(newrgb) # Populate the print list for color set 'rotate'. def createrotateprintlist(): global RGBCODE, PRINTLIST testhr = int(huerotation % 360) if (testhr == 0): PRINTLIST = [] else: # Initial RGB code (color to start with) was set during initialization. # If rotatecolor is set, use this color instead. if (rotatecolor != -1): RGBCODE = rotatecolor PRINTLIST = [RGBCODE] if (autofieldnum == 'yes'): rotatesteps = int(360 / huerotation) else: rotatesteps = fieldnum for step in range(rotatesteps - 1): RGBCODE = getnexthue(RGBCODE, huerotation) PRINTLIST.append(RGBCODE) return() #==================================================================================================== # MAIN CODE: INITIALIZATION # Load the contents of the configuration file. loadconfig(CONFIGFILE) # Configuration defaults. if not 'fieldnum' in globals(): fieldnum = 0 if not 'fieldheight' in globals(): fieldheight = 0 if not 'colorstep' in globals(): colorstep = 0x33 if not 'fixstriping' in globals(): fixstriping = 'no' if not 'fieldsep' in globals(): fieldsep = 0 if not 'fieldskip' in globals(): fieldskip = 0 if not 'skipoffset' in globals(): skipoffset = 0 if not 'invertskip' in globals(): invertskip = 'no' if not 'pagemargin' in globals(): pagemargin = 0 if not 'showbackground' in globals(): showbackground = 'no' if not 'backgroundcolor' in globals(): backgroundcolor = 0xffffff if not 'colorset' in globals(): colorset = 'plainrgb' if not 'startcolor' in globals(): startcolor = 'red' if not 'invertcolors' in globals(): invertcolors = 'no' if not 'fieldopacity' in globals(): fieldopacity = 1 if not 'huerotation' in globals(): huerotation = 30 if not 'rotatecolor' in globals(): rotatecolor = -1 if not 'rbgoutput' in globals(): rbgoutput = 'no' if not 'turnaround' in globals(): turnaround = int(0xff / colorstep) if not 'colorfile' in globals(): colorfile = '' if not 'shufflecolors' in globals(): shufflecolors = 'no' if not 'printvalues' in globals(): printvalues = 'no' #---------------------------------------------------------------------------------------------------- # For BONUS CODE: print objects i.o. fields if not 'showmesquares' in globals(): showmesquares = 'no' if not 'showmerectangles' in globals(): showmerectangles = 'no' if not 'showmecircles' in globals(): showmecircles = 'no' if not 'showmetriangles' in globals(): showmetriangles = 'no' if not 'objectsize' in globals(): objectsize = 10 if not 'minobjectsize' in globals(): minobjectsize = 0 if not 'objectstrokecolor' in globals(): objectstrokecolor = 'white' if not 'objectstrokewidth' in globals(): objectstrokewidth = 0 if not 'objectopacity' in globals(): objectopacity = 1 if not 'spillover' in globals(): spillover = 'no' #---------------------------------------------------------------------------------------------------- # Shelved settings (dysfunctional) ###rgbthreshold = 0x333333 # DEFAULT is 0x000000; value at which to declare underflow ###rgbceiling = 0xcccccc # DEFAULT is 0xffffff; value at which to declare SWITCHTIME ###wrapcolors = 'yes' # DEFAULT is 'no'; affects the entire RGB triplet if not 'rgbthreshold' in globals(): rgbthreshold = 0x0 if not 'rgbceiling' in globals(): rgbceiling = 0xffffff if not 'wrapcolors' in globals(): wrapcolors = 'no' # What follows are further initializations based on the configuration settings. While these could be # neatly packed in descriptive functions, since we're dealing with globals that would only complicate # matters. #---------------------------------------------------------------------------------------------------- # (Re)initialze configuration variables depending on others. Set some control variables. # Threshold and ceiling for byte values, normally 0x00 and 0xff respectively. # rgbthreshold and rgbceiling are currently not in the configuration settings. THRESHOLDR = int(rgbthreshold / 0x10000) % 0x100 THRESHOLDG = int(rgbthreshold / 0x100) % 0x100 THRESHOLDB = rgbthreshold % 0x100 CEILINGR = int(rgbceiling / 0x10000) % 0x100 CEILINGG = int(rgbceiling / 0x100) % 0x100 CEILINGB = rgbceiling % 0x100 # These allow fieldnum and fieldheight to be reused. autofieldnum = 'no' autofieldheight = 'no' if (fieldnum == 0): autofieldnum = 'yes' if (fieldheight == 0): autofieldheight = 'yes' # Stripe fixing: shift field start up one pixel. stripefix = 0 if (fixstriping == 'yes'): stripefix = 1 # invertskip has no meaning without fieldskip; may result in no output at all. if (fieldskip == 0): invertskip = 0 # A turnaround value higher than the natural one makes no sense. if (turnaround > int(0xff / colorstep)): turnaround = int(0xff / colorstep) # Get current page dimensions from SIS pagewidth = float(svg_root.get('width').rstrip('px')) pageheight = float(svg_root.get('height').rstrip('px')) # Field width equalss pagewidth, unless a margin is set. pagemargin = float(pagemargin) fieldwidth = pagewidth - 2 * pagemargin # Xmin and Ymin can be easily determined; Xmax/Ymax depend on fieldheight, set later. if (pagemargin == 0): (xmin, ymin) = (0, 0) else: (xmin, ymin) = (pagemargin, pagemargin) # Hex color code of current color to be printed; referred to throughout this script. RGBCODE = None # List of hex values for colors to be displayed. PRINTLIST = [] # For 'web' color set CHANGEOP = 'inc' # Make all color sets print R-G-B by default (only 'web' does that by nature). # This is much easier and safer than tinkering with the near-inscrutable innards of the script. swapgb = 'no' if (rbgoutput == 'yes'): if (colorset == 'web'): swapgb = 'yes' elif (colorset != 'web'): swapgb = 'yes' # Set uo the list of RGB color naes to rotate through. # Sets the byte to be pointed to by the L initial index high. rgblist = ('r', 'g', 'b') RGBCODE = 0x000000 if (startcolor == 'red'): RGBCODE = chargecolorbytes(RGBCODE, 'r') rgboffset = 0 elif (startcolor == 'green'): RGBCODE = chargecolorbytes(RGBCODE, 'g') rgboffset = 1 elif (startcolor == 'blue'): RGBCODE = chargecolorbytes(RGBCODE, 'b') rgboffset = 2 # This hooks the initial L index onto a color name in the list. The other indices (M/R) are set # relative to the L index. Each index will point to a different RGB color name, which is used for # retrieving the corresponding part of the RGB value, processed (changed) over and over again. initlmr(rgboffset) # Initialize (set high) the M bytes (not with 'plainrgb' or 'darkrgb'). if (colorset == 'hybrid' or colorset == 'lightrgb' or colorset == 'darkmyc'): RGBCODE = chargecolorbytes(RGBCODE, rgblist[mindex]) # VAGUE but functional: set the R byte moderately high so that the colors repeat. # Has no menaing for 'darkrgb' (because it wouldn't render primary colors). if (colorset == 'hybrid' or colorset == 'lightrgb' or colorset == 'darkmyc'): # Vulnerable to underflow: depends on turnaround and colorstep values. highrbytes = 0xff - turnaround * colorstep # Failsafe if (highrbytes < 0): highrbytes = 0 RGBCODE = RGBCODE + highrbytes COLORFILELIST = [] # Automatic field number calculation. if (autofieldnum == 'yes'): if (colorfile != ''): COLORFILELIST = readcolorfile(colorfile) fieldnum = len(COLORFILELIST) elif (colorset == 'rotate'): fieldnum = int(360 / huerotation) elif (colorset == 'web'): # Color set 'web' requires twice as many fields as the others to complete a round. fieldnum = int(CEILINGR / int(colorstep)) * 6 else: fieldnum = int(CEILINGR / int(colorstep)) * 3 # Automatic field alignment. if (autofieldheight == 'yes'): emptypagespace = 2 * pagemargin + (fieldnum - 1) * (fieldsep - stripefix) fieldheight = (pageheight - emptypagespace) / fieldnum #==================================================================================================== # MAIN CODE: ACTION # POPULATE PRINT THE LIST # Create the list of fields to be printed. if COLORFILELIST: PRINTLIST = COLORFILELIST elif (colorset == 'rotate'): createrotateprintlist() else: createprintlist() # Hande color shuffle and invert. handleshuffle() handleinvert() # Decide which fields to skip. The RGB code for these fields is simply replaced with the keyword 'skip'. handleskips() # Print color values on request. handleprintvalues() # PRINT STUFF # Add a background layer on request. if (showbackground == 'yes'): l0 = layer('Background') bg = writebackground(0, 0, pagewidth, pageheight, backgroundcolor) l0.add(bg) # Add a layer for field output l1 = layer('Fields') # For BONUS CODE: print onbjects i.o. fields. # Number of objects is determined by fieldnum. If less shown than expected, disable fieldskip. # Cause stuff to appear on the screen. if (showmesquares == 'yes'): printsquares(PRINTLIST) if (showmerectangles == 'yes'): printrectangles(PRINTLIST) if (showmecircles == 'yes'): printcircles(PRINTLIST) if (showmetriangles == 'yes'): printtriangles(PRINTLIST) if (showmesquares == 'no' and showmerectangles == 'no' and showmecircles == 'no' and showmetriangles == 'no'): # Normal output: fields. printfields(PRINTLIST)