# 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)