Logo
  • Home
  • Equipment
  • Tutorials
  • Calendar
  • TPZ Staff Page
  • (Beta) Tech Ai Support
The Possible Zone
Fab Lab Website
Fab Lab Website
/
Tutorials
/
Tutorials Database
/Use AI and Python to make *.svg graphics
Use AI and Python to make *.svg graphics
Use AI and Python to make *.svg graphics
Use AI and Python to make *.svg graphics

Use AI and Python to make *.svg graphics

Project

Date Created
January 11, 2024
Author
U
Untitled
Tools & Skills
illustratorpythonai
[CoF] Careers of the Future
Launch Lab Connection
Competency
  • Introduction
  • Prerequisites
  • drawsvg setup
  • Wrangling Python
  • Setting up the library
  • First shapes
  • Programs
  • Generating code with AI chatbots
  • Vector Graphics from GPT-4 Turbo
an AI-generated *.svg image
Fig. 0: an AI-generated SVG image.

Introduction

AI image generation using tools like DALL-E is cool. But what about using AI text tools to generate code which produces graphics? The difference is subtle but useful for those of us in the digital fabrication realm; using these methods, we can directly produce vector graphics! No more image tracing! As an added bonus, we can also modify the code used to generate the graphics. Very nice.

Prerequisites

A basic familiarity with Python is helpful but not required for this tutorial. You can pick it up as you go by examining code snippets, just know that it may be more time-efficient to run through an introductory Python tutorial first. You will need access to a computer with sufficient permission to install software; note that once this part is completed you can continue as a normal user, so perhaps a proximate IT professional can assist with the initial set-up.

drawsvg setup

‣

Wrangling Python

Mac

Download the latest Python release from the official language site. We want to do it this way rather than via command line in order to also install IDLE, an acronym for Integrated Development and Learning Environment. Press the big yellow “Download Python 3.x.x” button (for me, this was 3.12.2) to get the *.pkg file. Open it and follow the installation instructions with default settings, accepting terms as required.

Now, open a Terminal window and type:

python3 --version

The result should match the version you just downloaded. If not, un-collapse the following collapse-o:

‣

oh no

If you followed the previous tutorial, you may have a different version of Python installed via conda. When you start your Terminal, the system reads a startup script and figures out what versions of programs to use; the conda installation is messing with that process. To fix it, open a Finder window, navigate to your home directory (it is likely named after you), and press Command + Shift + . (period). This will show hidden files. Locate the file called “.zshrc” and double-click it to open in a text editor. Look for a block of conda stuff near the top, like is highlighted here:

A screenshot of a text editor showing a highlighted section of a file called .zshrc
Fig. 1: booting conda things from the Terminal startup script.

Delete the entire highlighted block, save the file, close any open Terminal windows, open a fresh Terminal window, and check the Python version again. It should be correct!

Windows

TODO

‣

Setting up the library

Open a Terminal (Mac) or Powershell (Windows) window. Install drawsvg:

pip3 install drawsvg

Now, open IDLE by opening Launchpad (Mac) or the Windows menu, searching for IDLE, and clicking the icon. An IDLE shell will appear, list off the currently running Python kernel, and await a command with a “>>>” prompt. Import drawsvg:

import drawsvg as draw

A blank line with a “>>>” prompt is a good sign — no errors! You’ll need to run this command each time you re-open IDLE; this tutorial assumes you have done that in the current session, but if you get the following error:

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    d = draw.Drawing(200,200,origin='center')
NameError: name 'draw' is not defined

… simply run “import drawsvg as draw” again.

‣

First shapes

Type the following lines into the IDLE “>>>” prompt, pressing Enter after each:

d = draw.Drawing(200,200,origin='center')
d.append(draw.Line(-50,-50,50,50,stroke='black'))
d.append(draw.Line(-50,50,50,-50,stroke='black'))
d.save_svg('test.svg')

Now, navigate to the default IDLE save directory, which is likely “Documents” (you can check by pulling down the File menu and selecting “Open”). Open the “test.svg” file using a fresh web browser window. You should see a pair of crossed diagonal lines! Orient the web browser window next to the IDLE window:

A screenshot of a Python IDLE window and a web browser with a rendered *.svg image.
Fig. 2: a reasonable drawsvg rapid test environment: IDLE shell on the left, web browser pointed to the generated *.svg file on the right.

Now, when you update the test file (”test.svg”, in this case), you can just click over to the browser window and refresh the page to see changes. You can also open the file in a vector graphics program like Adobe Illustrator, and then send the shape to your choice of Trotecs, Zünds, Nomads, Cricuts, or any other vector-graphics-craving digital fabrication machine.

‣

Programs

Re-typing every line of code when you make a change isn’t terribly efficient. As you produce more sophisticated *.svg generation programs, you’ll want to save them as Python scripts that can be easily edited, revision-controlled, shared, and run. In the IDLE Shell, drop down the File menu and select New File. You will be greeted with a fresh window called “Untitled”. For now, paste in a modified version of the test code, including the drawsvg import:

import drawsvg as draw
d = draw.Drawing(200,200,origin='center')
d.append(draw.Line(-50,-50,50,50,stroke='purple'))
d.append(draw.Line(-50,50,50,-50,stroke='orange'))
d.save_svg('test.svg')

File > Save the document; IDLE will automatically append *.py to identify the program as a Python script. To run the program, press F5 or drop down the Run menu and click “Run Module”. In the future if you haven’t saved, IDLE will prompt you to do so first. Click over to the web browser and refresh to see the new colors:

A screenshot of a Python IDLE Shell, a saved Python script, and a web browser window showing a render of a generated *.svg image.
Fig. 3: generating an *.svg image from a saved script.

Generating code with AI chatbots

From here, you could manually write routines which call the drawsvg library. This is a terrific exercise; head over to the documentation and run through a few examples, and then start building. But using AI tools is also fun! For this we use Playlab, which gives us easy access to state-of-the-art language models like GPT-4 Turbo.

‣

Vector Graphics from GPT-4 Turbo

In the TPZ Playlab instance, open the project called ‘svg-o-matic’. This project loads the drawsvg documentation as a reference and includes a bit of text instruction to shape the output (which isn’t perfect, but helps minimize errors):

your job is to create Python scripts that generate svg files based on inputs provided by the user in chat. The script should use the drawsvg library along with other common libraries that may be relevant, such as math or random. make sure the script only uses valid attributes for the libraries and calls them with correct capitalization and punctuation. for example, saveSvg is not a valid attribute, but save_svg is correct. Color is not valid, but color is valid. setPixelScale is not valid, but set_pixel_scale is valid. make sure the attributes used do not create errors. do not use polygon as an attribute as it does not exist.

Start the chatbot by pressing Launch, then enter a prompt. The example at the top of this page came from this text:

write a python script that uses the drawsvg library to create a 1000x1000 page filled with a horizontal gradient that fades from white to light green. the page should be filled with randomly generated irregular shapes with rounded corners that overlap. 10% of the background should be visible. the shapes should be filled with random gradients and outlined in black with a stroke width of 1.

GPT-4 Turbo returned some conversational text, and then a block of code:

It’s not perfect. If we paste the code into Jupyter and execute as-is, we get a few errors:

These errors are just incorrect variable name formatting; GPT-4 Turbo seems to like camelCase, while drawsvg uses hyphenated lowercase variables. Updating the last two lines as follows fixed the problem; ‘d.save_svg’ saves the file as gpt-shapes.svg, and ‘d’ displays the graphic in the Jupyter window:

d.save_svg('gpt-shapes.svg') # save *.svg file
d

Here is another example produced by the same code, which randomizes each time:

A generated *.svg image with random polygons and colors.
Fig. 4: a generated *.svg image with random polygons and colors.

Here is a different prompt which produces a simple scene:

write a python script that produces a 1000x1000 image using drawsvg and is filled with a gradient which looks like a sunset. add other shapes that look like a hillside with houses and trees. the sky should be on top and the ground should be on the bottom.

which produces this code (after minimal corrections):

Despite specific prompting about sky being on top, the result is inverted but otherwise recognizable:

An *.svg image generated by a Python script written by AI, showing a rough render of a few houses and trees on a hillside with a sunset in the background. The image is upside-down for unknown reasons, despite specific prompting to avoid inversion.
Fig. 5: an *.svg image generated from a Python drawsvg script written by AI.
import drawsvg as dw
import random

# Function to create a random gradient
def random_gradient():
    gradient = dw.LinearGradient(
        random.uniform(-500, 1500), 0,
        random.uniform(-500, 1500), 0
    )
    gradient.add_stop(0, 'rgb({},{},{})'.format(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
    gradient.add_stop(1, 'rgb({},{},{})'.format(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
    return gradient

# Create the main drawing
d = dw.Drawing(1000, 1000, origin='center')

# Create a horizontal gradient for the background
bg_gradient = dw.LinearGradient(0, 0, 1000, 0)
bg_gradient.add_stop(0, 'white')
bg_gradient.add_stop(1, 'lightgreen')
background = dw.Rectangle(-500, -500, 1000, 1000, fill=bg_gradient)
d.append(background)

# Function to generate a random path with rounded corners
def random_path():
    path = dw.Path(fill=random_gradient(), stroke='black', stroke_width=1)
    start_x, start_y = random.uniform(-400, 400), random.uniform(-400, 400)
    path.M(start_x, start_y)
    for _ in range(random.randint(3, 8)):  # Random number of points
        x, y = random.uniform(-400, 400), random.uniform(-400, 400)
        path.L(x, y)
    path.Z()
    return path

# Keep track of the area covered by shapes
covered_area = 0
total_area = 1000 * 1000

# Generate shapes until at least 10% of the background is visible
while covered_area / total_area < 0.9:
    shape = random_path()
    d.append(shape)
    # Estimate covered area by assuming each shape is approximately 100x100 in size
    covered_area += 100 * 100

# Save the SVG file
d.setPixelScale(1)  # Set scale to 1:1
d.saveSvg('random_shapes_with_gradient.svg')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[32], line 47
     44     covered_area += 100 * 100
     46 # Save the SVG file
---> 47 d.setPixelScale(1)  # Set scale to 1:1
     48 d.saveSvg('random_shapes_with_gradient.svg')

AttributeError: 'Drawing' object has no attribute 'setPixelScale'
import drawsvg as draw
import math

# Create a new drawing
d = draw.Drawing(1000, 1000, origin='center', displayInline=False)

# Create a gradient for the sunset sky
gradient = draw.LinearGradient(0, 500, 0, -500)
gradient.add_stop(0, 'darkred')
gradient.add_stop(0.1, 'red')
gradient.add_stop(0.3, 'orange')
gradient.add_stop(0.6, 'yellow')
gradient.add_stop(1, 'lightblue')

# Draw the sky rectangle with the gradient
sky = draw.Rectangle(-500, 0, 1000, 500, fill=gradient)
d.append(sky)

# Create a hill using a path
hill = draw.Path(fill='darkgreen', stroke_width=0)
hill.M(-500, -350)
hill.q(250, 100, 500, 0)
hill.q(250, -100, 500, 0)
hill.L(-500, -500)
hill.Z()
d.append(hill)

# Add trees to the hill
for i in range(-450, 451, 100):
    tree_trunk = draw.Rectangle(i+10, -350, 10, 50, fill='saddlebrown')
    tree_crown = draw.Circle(i+15, -280, 30, fill='darkgreen')
    d.append(tree_trunk)
    d.append(tree_crown)

# Add houses to the hill
for i in range(-400, 401, 200):
    # House body
    house_body = draw.Rectangle(i, -350, 80, 60, fill='burlywood')
    d.append(house_body)
    # House roof
    house_roof = draw.Lines(i, -290, i+40, -250, i+80, -290, close=True, fill='maroon')
    d.append(house_roof)

# Save the SVG file
d.save_svg('gpt-shapes.svg') # save *.svg file
d