Skip to content

Instantly share code, notes, and snippets.

@LettError
Last active March 14, 2018 16:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LettError/374c427f68108d36f561ac218b9c1606 to your computer and use it in GitHub Desktop.
Save LettError/374c427f68108d36f561ac218b9c1606 to your computer and use it in GitHub Desktop.
Toy script for RF3: draw animated pixels in a tiny window, save to gif.
# coding: utf-8
import os
import vanilla
import mojo.canvas
from mojo.UI import PutFile
from random import random
from mojo.drawingTools import *
import drawBot
from AppKit import NSTimer
# http://robofont.com/documentation/building-tools/api/mojo/mojo-canvas/?highlight=canvas
"""
A small toy that shows the RoboFont3 CanvasGroup.
Requires: the DrawBot extension.
erik@letterror.com
space: start / stop animation so you can draw on individual frames
up / down arrows: step through the frames if the animation is stopped.
"""
class TW(object):
_small = "".join(chr(m) for m in [9999, 65039, 9643, 65039])
_big = "".join(chr(m) for m in [9999, 65039, 9723, 65039])
_save = "".join(chr(m) for m in [128250, 128230])
_erase = "".join(chr(m) for m in [10006, 65039])
def __init__(self):
#print("_small", [ord(b) for b in self._small])
#print("_big", [ord(b) for b in self._big])
#print("_save", [ord(b) for b in self._save])
#print("_erase", [ord(b) for b in self._erase])
self.points = {}
self.w = vanilla.Window((200, 230), title="BlinkenDraw")
self.w.c = mojo.canvas.CanvasGroup((0,30,200,200), delegate=self)
self.w.b = vanilla.Button((5, 5, 50, 20), self._save, callback=self.callbackExport)
self.w.s = vanilla.Button((60, 5, 45, 20), self._small, callback=self.callbackPenSize)
self.w.e = vanilla.Button((110, 5, 30, 20), self._erase, callback=self.callbackErase)
self.w.t = vanilla.TextBox((-45, 7, 40, 20), "0", sizeStyle="small", alignment="right")
self.w.bind("close", self.callbackClose)
self.maxFrames = 10
self.dotSize = 5
self.currentFrame = 0
self.timerInterval = .0025
self.timer = None
self.running = True
self.w.open()
def callbackErase(self, sender):
# if we're running: clear the whole thing.
# if we're not running, clear the current frame
if self.running:
self.points = {}
else:
self.points[self.currentFrame] = []
self.w.c.update()
def callbackPenSize(self, sender):
if self.dotSize == 5:
self.dotSize = 20
self.w.s.setTitle(self._small)
else:
self.dotSize = 5
self.w.s.setTitle(self._big)
def callbackExport(self, sender):
# use drawbot to export the thing
if self.timer is not None:
self.timer.invalidate()
self.timer = None
state = self.running
self.running = False
a, b, _w, _h = self.w.c.getPosSize()
path = PutFile("Save this gif as", "blinkenDraw.gif")
if path is None:
return
drawBot.newDrawing()
for i in range(len(self.points)):
drawBot.newPage(width=_w, height=_h)
drawBot.frameDuration(0.025)
drawBot.fill(0)
drawBot.rect(0,0,_w, _h)
frame = self.points.get(i)
if not frame:
continue
drawBot.fill(1)
for p in frame:
ds = self.dotSize
pos = p[0]-.5*ds, p[1]-.5*ds
drawBot.rect(pos[0], pos[1],ds,ds)
drawBot.saveImage(path)
drawBot.endDrawing()
self.running = state
def callbackClose(self, sender):
self.timer = None
def draw(self):
a, b, w, h = self.w.getPosSize()
fill(0)
rect(0,0,w,h)
frame = self.points.get(self.currentFrame)
if not frame:
self.scheduleTimer()
return
fill(1)
self.w.t.set("{frameCount}/{maxFrames}".format(**dict(frameCount=self.currentFrame, maxFrames=self.maxFrames)))
for p in frame:
ds = self.dotSize + .1*random() -0.2
pos = p[0]-.5*ds, p[1]-.5*ds
rect(pos[0], pos[1],ds,ds)
if self.running:
self.nextFrame()
self.scheduleTimer()
def scheduleTimer(self):
if self.timer is not None:
self.timer.invalidate()
self.timer = None
if self.running:
self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(self.timerInterval, self, "animateCallback:", None, False)
def animateCallback_(self, timer):
self.w.c.update()
self.scheduleTimer()
def nextFrame(self):
self.currentFrame += 1
if self.currentFrame > self.maxFrames:
self.currentFrame = 0
def previousFrame(self):
self.currentFrame -= 1
if self.currentFrame < 0:
self.currentFrame = self.maxFrames-1
def addPixel(self, command, pos):
a, b, w, h = self.w.getPosSize()
f = (int(pos.x), int(pos.y))
f = f[0]-f[0]%self.dotSize+.5*self.dotSize, f[1]-f[1]%self.dotSize+.5*self.dotSize
if not self.currentFrame in self.points:
self.points[self.currentFrame] = []
if command == 'd' and f in self.points[self.currentFrame]:
self.points[self.currentFrame].remove(f)
return
if f not in self.points[self.currentFrame]:
self.points[self.currentFrame].append(f)
def keyDown(self, event):
if event.keyCode() == 49:
self.running = not self.running
if event.keyCode() == 126:
# arrow up
self.previousFrame()
self.w.c.update()
elif event.keyCode() == 125:
# arrow down
self.nextFrame()
self.w.c.update()
if self.running:
self.scheduleTimer()
def mouseDown(self, event):
self.addPixel("d", event.locationInWindow())
self.w.c.update()
def mouseDragged(self, event):
self.addPixel("l", event.locationInWindow())
self.w.c.update()
OpenWindow(TW)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment