DrawBot IconΒΆ
The making of (by Andy Clymer)
from ufoLib.glifLib import readGlyphFromString
from fontTools.pens.cocoaPen import CocoaPen
from fontParts.fontshell.glyph import RGlyph
import random
import os
tryAndError = False
drawPencil = True
drawBubbles = True
# Big pencil for the file icon?
# (and then only save the first frame)
bigPencil = False
# Left handed?
leftHand = True
# Reduced color palette for file icon?
reducedPalette = False
folderPath = os.path.split(__file__)[0]
savePath = os.path.join(folderPath, "icon.gif")
singleFileSavePath = os.path.join(folderPath, "icon_%s.png")
#savePath = os.path.join(folderPath, "icon.png")
lightOrange = [1, 0.75, 0, 1]
orange = [1, 0.5, 0, 1]
redOrange = [1, 0.25, 0.1, 1]
darkOrange = [0.3, 0.15, 0, .4]
pinkish = [1, 0.5, 0.6, 1]
magenta = [0.9, 0.1, 0.5, 1]
purple = [0.7, 0.1, 0.8, 1]
darkPurple = [0.5, 0, 0.5]
brightPurple = [1, 0.1, 1, 1]
gray = [0.8, 0.8, 0.8, 1]
white = [1, 1, 1, 1]
# Glif string
iconGlifString = b"""<?xml version="1.0" encoding="UTF-8"?>
<glyph name="A" format="1">
<advance width="0"/>
<outline>
<contour>
<point x="95" y="407" type="line"/>
<point x="328" y="442"/>
<point x="422" y="391"/>
<point x="422" y="258" type="curve" smooth="yes"/>
<point x="422" y="124"/>
<point x="328" y="73"/>
<point x="95" y="108" type="curve"/>
</contour>
<contour>
<point x="218" y="305" type="curve"/>
<point x="218" y="210" type="line"/>
<point x="275" y="206"/>
<point x="300" y="221"/>
<point x="300" y="258" type="curve" smooth="yes"/>
<point x="300" y="295"/>
<point x="275" y="310"/>
</contour>
</outline>
</glyph>
"""
# Read the string into a glyph object
iconGlyph = RGlyph()
pen = iconGlyph.getPointPen()
readGlyphFromString(iconGlifString, glyphObject=iconGlyph, pointPen=pen)
iconGlyph.scaleBy((1.12, 1.2))
# Fetch the path of the glyph as a NSBezierPath
pen = CocoaPen(None)
iconGlyph.draw(pen)
iconPath = pen.path
# ...and then convert it to a DrawBot BezierPath
iconPath = BezierPath(iconPath)
# Remove the inside contour of the glyph, and read another path
iconGlyph.removeContour(1)
# Fetch the path of the glyph as a NSBezierPath
pen = CocoaPen(None)
iconGlyph.draw(pen)
iconOutsidePath = pen.path
# ...and then convert it to a DrawBot BezierPath
iconOutsidePath = BezierPath(iconOutsidePath)
""" Helper functions """
def interpolate(f, a, b):
v = (a + (b - a) * f)
return v
def interpolateColor(f, color0=None, color1=None):
# Default the two colors to pinkish orange and magenta:
if not color0:
if reducedPalette:
color0 = white
else: color0 = lightOrange
if not color1:
if reducedPalette:
color1 = orange
else: color1 = orange
newColor = []
# Interpolate
for i in range(4):
newColor.append(interpolate(f, color0[i], color1[i]))
return tuple(newColor)
def drawBubble(size, phase):
# Shift the phase
if phase > 1:
phase = phase - 1
# Scale the phase, so that it doesn't happen all the time
phase *= 3
# Draw if it's durring the current phase
if phase < 1:
fill(1, 1, 1, 1-phase)
stroke(1, 1, 1, 1)
strokeWidth(10 * (1-phase))
phaseSize = phase*size
oval(-0.5*phaseSize, -0.5*phaseSize, phaseSize, phaseSize)
# Make some random bubble data
bubbles = []
if drawBubbles:
for i in range(100):
bubbles.append(
(random.randint(0, 512), # x
random.randint(0, 512), # y
random.randint(30, 100), # size
random.random()) # phase
)
""" Start drawing """
size(512, 512)
def drawIcon(timeFactor):
# timeFactor is the timeline position, between 0 and 1
# Temporary background color
#fill(0.85)
#rect(0, 0, 512, 512)
translate(256, 256)
scale(1.1)
translate(-256, -256)
translate(-27, -51)
fill(None)
# Transparent shadow under the "D"
with savedState():
#fill(*darkOrange)
stroke(*darkOrange)
strokeWidth(60)
drawPath(iconPath)
# Gradient within the "D"
save()
# Clip
clipPath(iconPath)
# Move to the center of the canvas
translate(256, 256)
circleCount = 30
for i in range(circleCount):
f = i/circleCount
angle = (f * 360) + (360 * timeFactor)
x = 120 * sin(radians(angle+90))
y = 120 * cos(radians(angle+90))
colorFactor = f * f * f # Use an exponential curve for the color factor
stroke(None)
fill(*interpolateColor(colorFactor))
#shadow((0, 0), 50, interpolateColor(colorFactor)) # Extra smoothness?
oval(x-150, y-150, 300, 300)
restore()
# Bubbles
for bubble in bubbles:
save()
clipPath(iconPath)
translate(bubble[0], bubble[1])
drawBubble(bubble[2], timeFactor + bubble[3])
restore()
# Pencil location
angle = (f * 360) + (360 * timeFactor) + 70
x = 120 * sin(radians(angle+90)) + 80
y = 90 * cos(radians(angle+90)) + 10
if not leftHand:
x -= 60
y -= 20
# Shadow inside the "D"
save()
shadowPath = iconPath.copy()
# Add the pencil shadow
shadowX = 256
shadowY = 300
if leftHand:
shadowX -= 40
shadowY -= 10
if drawPencil:
if not bigPencil:
shadowPath.oval(shadowX+x, shadowY+y, 50, 50)
clipPath(iconPath)
translate(-20, -20)
strokeWidth(61)
stroke(0, 0, 0, 0.25)
drawPath(shadowPath)
restore()
# White stroke on top of the "D"
fill(None)
stroke(1)
strokeWidth(30)
drawPath(iconPath)
# Pencil
if drawPencil:
save()
translate(256, 286)
# Rotate the pencil with each step
pencilRotationAngle = 10 * cos(radians(angle))
translate(x, y)
# Pencil
rotate(pencilRotationAngle)
# And an additional amount for the base angle of the pencil
if leftHand:
rotate(50)
else: rotate(-25)
if bigPencil:
scale(1.9, 1.9)
strokeWidth(14)
translate(25, 10)
else: strokeWidth(18)
fill(None)
if reducedPalette:
stroke(1)
fill(*orange)
else:
stroke(*darkPurple)
fill(*brightPurple)
polygon((0, 0), (-40, 40), (-40, 140), (40, 140), (40, 40), close=True)
# Pencil end
oval(-40, 130, 80, 40)
# Pencil tip
if reducedPalette:
fill(1)
else: fill(*darkPurple)
stroke(None)
oval(-20, 0, 40, 40)
restore()
totalFrames = 21
if tryAndError:
totalFrames = 1
for i in range(totalFrames):
f = i / totalFrames
if not i == 0:
newPage()
frameDuration(1/10)
drawIcon(f)
if not tryAndError:
pass#saveImage(singleFileSavePath % i)
if not tryAndError:
saveImage(savePath)