Generated Shapes
Generated Shapes
A shape made with a simple stroke is not, in itself, particularly interesting.
But vary the dimensions, and birth a population beyond counting, and something changes.
Viewed from afar, the mind cannot comprehend something so complex, yet so basic.
An experiment in Python scripting. The script only hints at what it produces.
The artworks are very dense and intended to be printed at size A0 or above. It may be helpful to open an image in a new browser tab, in order to fully view.
#generate_shapes(shape='circle', output_file='output/circles-mono.svg', count=12500, scale=25, fill=False)
#generate_shapes(shape='rectangle', output_file='output/rectangles-mono.svg', count=25000, scale=25, aspect_ratio=2.0, fill=False)
#generate_shapes(shape='square', output_file='output/squares-mono.svg', count=25000, scale=50, fill=False)
#generate_shapes(shape='triangle', output_file='output/triangles-mono.svg', count=25000, scale=50, fill=False)
import drawsvg as draw
import random
import math
import os
PALETTE_WARM = {
1: '#bbe0f0', # Columbia Blue
2: '#f49f0a', # Gamboge
3: '#00a6a6', # Light Sea Green
4: '#efca08', # Jonquil
5: '#f08700' # Tangerine
}
PALETTE_COOL = {
1: '#a8dadc', # Powder Blue
2: '#457b9d', # Celadon Blue
3: '#1d3557', # Prussian Blue
4: '#48cae4', # Sky Blue
5: '#023e8a' # Royal Blue
}
PALETTE_EARTH = {
1: '#d4a373', # Fawn
2: '#8a5a44', # Coffee
3: '#606c38', # Dark Olive
4: '#dda15e', # Earth Yellow
5: '#bc6c25' # Copper
}
PALETTE_BERRY = {
1: '#e0aaff', # Mauve
2: '#c77dff', # Heliotrope
3: '#9d4edd', # Purple
4: '#7b2cbf', # French Violet
5: '#5a189a' # Persian Indigo
}
def generate_shapes(
shape='circle',
output_file='output/shapes.svg',
width=3508,
height=4960,
count=100000,
tiers=5,
scale=10,
aspect_ratio=2.0,
palette=None,
fill=True,
fill_color=None,
stroke_color='#000000',
background=None,
opacity=1.0,
rotate=False,
seed=None,
dpi=300,
):
"""Generate an SVG filled with random shapes.
Args:
shape: 'circle', 'rectangle', 'square', or 'triangle'
output_file: Output SVG path
width: Canvas width in pixels
height: Canvas height in pixels
count: Number of shapes to generate
tiers: Number of size tiers (each shape is randomly assigned one)
scale: Base size multiplier (size = tier * scale)
aspect_ratio: Width/height ratio for rectangles
palette: Dict mapping tier -> color, or None for single-colour output
fill: Whether shapes are filled
fill_color: Explicit fill color (overrides palette when set)
stroke_color: Stroke colour
background: Canvas background colour (None = transparent)
opacity: Shape opacity (0.0 - 1.0)
rotate: Randomly rotate rectangles and squares
seed: Random seed for reproducible output
dpi: Output resolution (default 300)
"""
if seed is not None:
random.seed(seed)
output_dir = os.path.dirname(output_file)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
width_in = width / dpi
height_in = height / dpi
d = draw.Drawing(
f'{width_in}in', f'{height_in}in',
viewBox=f'0 0 {width} {height}',
displayInline=False,
)
if background:
d.append(draw.Rectangle(0, 0, width, height, fill=background))
group = draw.Group()
for _ in range(count):
x = random.uniform(0, width)
y = random.uniform(0, height)
tier = random.randint(1, tiers)
size = tier * scale
stroke_width = tier
if fill:
if fill_color:
fc = fill_color
elif palette:
fc = palette.get(tier, stroke_color)
else:
fc = stroke_color
else:
fc = 'none'
sc = stroke_color
if not fill and palette:
sc = palette.get(tier, stroke_color)
kwargs = dict(
fill=fc,
stroke=sc,
stroke_width=stroke_width,
opacity=opacity,
)
if shape == 'circle':
elem = draw.Circle(x, y, size, **kwargs)
elif shape == 'rectangle':
w = size * aspect_ratio
h = size
if rotate:
angle = random.uniform(0, 360)
kwargs['transform'] = f'rotate({angle} {x + w / 2} {y + h / 2})'
elem = draw.Rectangle(x, y, w, h, **kwargs)
elif shape == 'square':
if rotate:
angle = random.uniform(0, 360)
kwargs['transform'] = f'rotate({angle} {x + size / 2} {y + size / 2})'
elem = draw.Rectangle(x, y, size, size, **kwargs)
elif shape == 'triangle':
h = size * math.sqrt(3) / 2
x1, y1 = x, y - h * 2 / 3
x2, y2 = x - size / 2, y + h / 3
x3, y3 = x + size / 2, y + h / 3
if rotate:
angle = random.uniform(0, 360)
kwargs['transform'] = f'rotate({angle} {x} {y})'
elem = draw.Lines(x1, y1, x2, y2, x3, y3, close=True, **kwargs)
else:
raise ValueError(f"Unknown shape: {shape}")
group.append(elem)
d.append(group)
d.save_svg(output_file)
Script is licensed CC BY-NC 4.0 https://creativecommons.org/licenses/by-nc/4.0/