# Flexible code for colorful bubbles with randomized colors

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

# bubblescript version 0.1
#
# 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.


# ************************************************************************************************************

# User section

# Entries below with a DEFAULT value can be commented out

# If either of thse is not set, page dimensions of the document are used. Values may differ.
#pagewidth = 1000
#pageheight = 1000

minbubblesize= 60				# DEFAULTS TO 40.
maxbubblesize = 240				# DEFAULTS TO 200.
bubblenum = 21					# DEFAULTS TO 10.
#bubbleopacity = 0.3				# DEFAULTS TO 0.6.
#bubblestrokewidth = 1				# DEFAULTS TO 0.
#bubblestrokecolor = '0c0c0c'			# DEFAULTS TO 'ffffff'.
pageboundaries = 'yes'				# DEFAULTS TO 'no'.
pagemargin = 10				# DEFAULTS TO 10.
#backgroundcolor = 000000			# DEFAULTS TO 'ffffff'.
#bubbleshape = 'square'			# DEFAULTS TO 'circle'.
palette = 'yes'				# DEFAULTS TO 'no'. load palette? requires a palette file.
palettefile = '/home/nobody/palette.txt'	# MUST BE SET with palette.

# ************************************************************************************************************

# Code section

if not 'minbubblesize' in globals(): minbubblesize = 40
if not 'maxbubblesize' in globals(): maxbubblesize = 200
if not 'bubblenum' in globals(): bubblenum = 10
if not 'bubbleopacity' in globals(): bubbleopacity = 0.6
if not 'bubblestrokewidth' in globals(): bubblestrokewidth = 0
if not 'bubblestrokecolor' in globals(): bubblestrokecolor = 'ffffff'
if not 'pageboundaries' in globals(): pageboundaries = 'no'
if not 'pagemargin' in globals(): pagemargin = 10
if not 'backgroundcolor' in globals(): backgroundcolor = 'ffffff'
if not 'bubbleshape' in globals(): bubbleshape = 'circle'
if not 'palette' in globals(): palette = 'no'

# Set page dimensions if not set already. Doc: "SVG works in nominal "pixel" units."
# TODO: FIX VIEWBOX/VIEWPORT ISSUES
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'))


# ************************************************************************************************************

# Function definitions

def getrandposition():
  if (pageboundaries == 'yes'):
    xmin = maxbubblesize / 2 + pagemargin
    ymin = maxbubblesize / 2 + pagemargin
    xmax = pagewidth - (maxbubblesize / 2) - pagemargin
    ymax = pageheight - (maxbubblesize / 2) - pagemargin
  else:
    xmin = 0
    ymin = 0
    xmax = pagewidth
    ymax = pageheight
  xpos = uniform(xmin, xmax)
  ypos = uniform(ymin, ymax)
  return (xpos, ypos)

# function for creating a shape; default is square
def makeshape(kind, xpos, ypos, size, sfill, swidth, objfill):
  if kind == 'bg':
    obj = rect((0,0 ), (size, size), stroke=sfill, stroke_width=swidth, fill=objfill)
  elif kind == 'square':
    obj = rect((xpos, ypos), (xpos + size, ypos + size), stroke=sfill, stroke_width=swidth, fill=objfill)
  else:
    radius = size / 2
    obj = circle((xpos, ypos), radius, stroke=sfill, stroke_width=swidth, fill=objfill)
  return obj

# Reads a palette file abd returns a list of RGB values and its length
# File format:
# <six-digit hex rgb><artbitrary text>
# Example palette file contents:
#
# 04d9ff          Neon Blue
# ff9933          Neon Carrot
# fe4164          Neon Fuchsia
#
# Function will TRY to read the first six characters of each line
def getpalette():
  rgbl = []
  rgbm = 0
  with open(palettefile, 'r') as fp:
    line = fp.readline()
    while line:
      line = line.rstrip()
      # parts = line.split()
      # line = parts[0]
      line = line[0:6]
      rgbl.append(line)
      line = fp.readline()
  fp.close()
  rgbm = len(rgbl) - 1
  return(rgbl, rgbm)

# Different ways for fetching a color, depending on config
# With a matrix, cell colors are accessed by index (flat list)
def getcolor():
  if palette == 'yes':
    rgbindex = randrange(rgbmax)
    clr = '#%s' % rgblist[rgbindex]
  else:
    clr = '#%02x%02x%02x' % (randrange(256), randrange(256), randrange(256))
  return clr

# ************************************************************************************************************

# main code

# Two layers: background), bubbles
l1 = layer('Background')
l2 = layer('Bubbles')

# TODO: pageheight
background = makeshape('bg', 0, 0, pagewidth, 0, 0, '#%s' % backgroundcolor)
l1.add(background)

if (palette == 'yes'):
  (rgblist, rgbmax) = getpalette()

# Every single element is created and positioned individually, then added to its layer
for i in range(bubblenum):

  # Create randomly positioned bubble
  (y, x) = getrandposition()
  bubblesize = uniform(minbubblesize, maxbubblesize)
  bubblecolor = getcolor()
  grad = linear_gradient((0, 0), (0, 0))
  grad.add_stop(0, bubblecolor, bubbleopacity)
  bubble = makeshape(bubbleshape, x, y, bubblesize, bubblestrokecolor, bubblestrokewidth, grad)
  # ADDING TWO OPACITY BYTES DOESN'T SEEM TO WORK
  #bubble = makeshape(bubbleshape, x, y, bubblesize, bubblestrokecolor, bubblestrokewidth, '%scc' % bubblecolor)
  l2.add(bubble)