# Creates an array of page-wide fields with quasi-random colors
# Breaks all RGB logic by simply adding values to the whole

# Language: Simple Inkscape Scripting (SIS, apparently)
# Details: https://inkscape.org/~pakin/%E2%98%85simple-inkscape-scripting
# Github: https://github.com/spakin/SimpInkScr

# colorcript version 0.3
#
# 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.

# Bugs/shortcomings
# - does not necessarily align the output with the bottom of the page
# - has very unpredictable output (which also makes it interesting)

# N.B. tries to reach ffffff, which isn't always achieved due to rounding


# Settings

# Page dimensions. Best practice: create a page of the intended dimensions and set everything to px.
# (Document Properties -> Display units; Export PNG -> Units). In that case, these can be left unset.
# If set, make sure that at least Display units are set to px.
# If either of the folowing is not set, page dimensions of the document are used. Values may differ.
#pagewidth = 1000
#pageheight = 1000

# Entries below with a DEFAULT value can be commented out
fieldnum = 30				# MUST BE SET.
#printvalues = 'yes'			# DEFAULTS TO 'no'
#invertcolors = 'yes'			# DEFAULTS TO 'no'


# Exotic settings: may or may not yield anything of use or interest

# colorshift is the value by which the value (starting from 0 == 000000) is incremented
# Normally it is auto-determined from the number of fields to be printed.
#colorshift = 24			# DEFAULTS TO 0 for auto
#includeextremes = 'yes'		# DEFAULTS TO 'no'; print fields for 000000 and ffffff as well
#colorsalt = 1				# DEFAULTS TO 0; positive or negative integer

# MAINTENANCE GUIDE
# fieldnum
# - determines eveything: no of fields AND increment
# colorshift
# - increment: value by which to increment RGB code in steps
# - calculated according to number of fields set with fieldnum
# colorsalt
# - is just a plaything (no relevance to the main loop)
# - determines whether to divide 0xffffff or 0x1000000
# fieldfactor
# - determines whether to divide by 0xf or 0x10
# coloroffset
# - determines which color to start from (000000 or the first incremented value)
# To include or exclude extremes (000000 black and ffffff white):
# 1. Include
# fieldfactor determines first color: 000000, aaaaaa, bbbbbb, .., ffffff
# E.g. 5 fields (cs = colorshift):
# 000000, aaaaaa, bbbbbb, cccccc, ffffff
# 0cs      cs     2cs     3cs     4cs
# ffffff = 4cs ==> cs == ffffff / 4 == ffffff / (fieldnum - 1)
# fieldnum - 1: fieldnum + coloroffset with coloroffset = -1
# 2. Exclude
# fieldfactor determines first color: aaaaaa, bbbbbb, cccccc, .., ffffff - cs
# E.g. 5 fields (cs = colorshift):
# 000000*, aaaaaa, bbbbbb, cccccc, dddddd, eeeeee, ffffff* (* = not shown)
# 0cs      cs      2cs     3cs     4cs     5cs     6cs
# ffffff = 6cs ==> cs == ffffff / 6 == ffffff / (fieldnum + 1)
# fieldnum + 1: fieldnum + coloroffset with coloroffset = 1
# Therefore: without includeextremes (no extremes shown):
# - fieldfactor can go (fixed value 1)
# - coloroffset can go (fixed value 1)
# This would probably be acceptable behavior


# Code section

# Return a hex string from a hex int value
def hex2str(value, chars = 6):
  return('{0:0{1}x}'.format(value, chars))

if not 'printvalues' in globals(): printvalues = 'no'
if not 'includeextremes' in globals(): includeextremes = 'no'
if not 'invertcolors' in globals(): invertcolors = 'no'
if not 'colorshift' in globals(): colorshift = 0
if not 'colorsalt' in globals(): colorsalt = 0

# Page stuff.
# Set page dimensions if not set already. Pixel-oriented!
if ('pageheight' in globals()) and ('pagewidth' in globals()):
  svg_root.set('width', '%spx' % pagewidth)
  svg_root.set('height', '%spx' % pageheight)
  width, height = svg_root.width, svg_root.height
  svg_root.set('viewBox', '0 0 %.0f %.0f' % (width, height))
else:
  pagewidth = float(svg_root.get('width').rstrip('px'))
  pageheight = float(svg_root.get('height').rstrip('px'))

# Field stuff.
l1 = layer('Fields')
(xmin, ymin) = (0, 0)
fieldheight = int(pageheight / fieldnum)
(xmax, ymax) = (pagewidth, fieldheight)

# Weird stuff.
if (includeextremes == 'yes'):
  fieldfactor = -1
  coloroffset = 0
else:
  fieldfactor = 1
  coloroffset = 1

# Auto-determine color shift is not set
if (colorshift == 0):
  colorshift = int((0xffffff + colorsalt) / (fieldnum + fieldfactor))

if (printvalues == 'yes'):
  print('printing %d fields at color shift 0x%s (%d)' % (fieldnum, hex2str(colorshift), colorshift))

for i in range(fieldnum):

  thiscolor = (i + coloroffset) * colorshift

  if (invertcolors == 'yes'):
    thiscolor = 0xffffff - thiscolor
  fieldcolor = hex2str(thiscolor)
  if (printvalues == 'yes'): print('%s' % fieldcolor)

  field = rect((xmin, ymin), (xmin + xmax, ymin + ymax), stroke=0, stroke_width=0, fill = '#%s' % fieldcolor)
  l1.add(field)
  ymin = ymin + fieldheight