Skip to content

Instantly share code, notes, and snippets.

@stenson
Last active August 3, 2021 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stenson/0395a8708f1ae39d125d1236fd40e822 to your computer and use it in GitHub Desktop.
Save stenson/0395a8708f1ae39d125d1236fd40e822 to your computer and use it in GitHub Desktop.
# Andy Clymer’s DrawBot icon,
# slightly modified to work in Coldtype
# original source: https://www.drawbot.com/content/drawBotIcon.html
# modifications: moved timing and output to
# coldtype’s @drawbot_animation decorator
# to get drawbot in your virtualenv:
# pip install git+https://github.com/typemytype/drawbot
from coldtype import *
from coldtype.drawbot import *
from math import *
from fontTools.ufoLib.glifLib import readGlyphFromString
from fontTools.pens.cocoaPen import CocoaPen
from fontParts.fontshell.glyph import RGlyph
import random
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 = 0
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]
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 = db.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 = db.BezierPath(iconOutsidePath)
""" Helper functions """
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(norm(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:
db.fill(1, 1, 1, 1-phase)
db.stroke(1, 1, 1, 1)
db.strokeWidth(10 * (1-phase))
phaseSize = phase*size
db.oval(-0.5*phaseSize, -0.5*phaseSize, phaseSize, phaseSize)
# Make some random bubble data
random.seed(0)
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 """
def drawIcon(timeFactor):
db.translate(256, 256)
db.scale(1.1)
db.translate(-256, -256)
db.translate(-27, -51)
db.fill(None)
# Transparent shadow under the "D"
with db.savedState():
#fill(*darkOrange)
db.stroke(*darkOrange)
db.strokeWidth(60)
db.drawPath(iconPath)
# Gradient within the "D"
db.save()
# Clip
db.clipPath(iconPath)
# Move to the center of the canvas
db.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
db.stroke(None)
db.fill(*interpolateColor(colorFactor))
#shadow((0, 0), 50, interpolateColor(colorFactor)) # Extra smoothness?
db.oval(x-150, y-150, 300, 300)
db.restore()
# Bubbles
for bubble in bubbles:
db.save()
db.clipPath(iconPath)
db.translate(bubble[0], bubble[1])
drawBubble(bubble[2], timeFactor + bubble[3])
db.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"
db.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)
db.clipPath(iconPath)
db.translate(-20, -20)
db.strokeWidth(61)
db.stroke(0, 0, 0, 0.25)
db.drawPath(shadowPath)
db.restore()
# White stroke on top of the "D"
db.fill(None)
db.stroke(1)
db.strokeWidth(30)
db.drawPath(iconPath)
# Pencil
if drawPencil:
db.save()
db.translate(256, 286)
# Rotate the pencil with each step
pencilRotationAngle = 10 * cos(radians(angle))
db.translate(x, y)
# Pencil
db.rotate(pencilRotationAngle)
# And an additional amount for the base angle of the pencil
if leftHand:
db.rotate(50)
else: db.rotate(-25)
if bigPencil:
db.scale(1.9, 1.9)
db.strokeWidth(14)
db.translate(25, 10)
else: db.strokeWidth(18)
db.fill(None)
if reducedPalette:
db.stroke(1)
db.fill(*orange)
else:
db.stroke(*darkPurple)
db.fill(*brightPurple)
db.polygon((0, 0), (-40, 40), (-40, 140), (40, 140), (40, 40), close=True)
# Pencil end
db.oval(-40, 130, 80, 40)
# Pencil tip
if reducedPalette:
db.fill(1)
else:
db.fill(*darkPurple)
db.stroke(None)
db.oval(-20, 0, 40, 40)
db.restore()
@drawbot_animation((512, 512), timeline=Timeline(21, fps=12))
def dbicon(f):
drawIcon(f.e("linear"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment