Last active
March 14, 2018 16:38
-
-
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.
This file contains hidden or 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
# 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. | |
[email protected] | |
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