Last active
August 3, 2021 21:25
-
-
Save stenson/0395a8708f1ae39d125d1236fd40e822 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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