zbeok-Scope
PDF: zbeok-praxinoscope-output
My design is a face winking cheekily. Everything is programmatically generated, including the wink. The squash and stretch effect and sparkles are created by matrix transformations.
GIF:
Code:
// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for Processing 3.3.6 * http://processing.org
// 23 January 2018 * Golan Levin
// See information about Processing PDF export at:
// https://processing.org/reference/libraries/pdf/index.html
// PDF generated by Processing can be opened in Adobe Illustrator.
import processing.pdf.*;
boolean bRecordingPDF = false;
float inch = 72;
float diamArtInner = inch * 1.50;
float diamArtOuter = inch * 4.80;
float diamCutInner = inch * 1.41;
float diamCutOuter = inch * 4.875;
float holeDy = inch * 0.23;
float holeDx = inch * 0.20;
float holeD = inch * 0.1;
final int nFrames = 10;
int myFrameCount = 0;
int exportFrameCount = 0;
boolean bAnimate = true;
boolean bExportFrameImages = false;
//-------------------------------------------------------
void setup() {
size(792, 612); // 11x8.5" at 72DPI
frameRate(15);
smooth();
}
//-------------------------------------------------------
void draw() {
background(240);
if (bRecordingPDF) {
beginRecord(PDF, "praxinoscope-output.pdf");
}
// Do all the drawing.
pushMatrix();
translate(width/2, height/2);
drawCutLines();
drawGuides();
drawAllFrames();
popMatrix();
if (bExportFrameImages) {
// If activated, export .PNG frames
if (exportFrameCount < nFrames) { String filename = "frame_" + nf((exportFrameCount%nFrames), 3) + ".png"; saveFrame("frames/" + filename); println("Saved: " + filename); exportFrameCount++; if (exportFrameCount >= nFrames) {
bExportFrameImages = false;
exportFrameCount = 0;
}
}
}
if (bRecordingPDF) {
endRecord();
bRecordingPDF = false;
}
}
//-------------------------------------------------------
void keyPressed() {
switch (key) {
case ' ':
// Press spacebar to pause/unpause the animation.
bAnimate = !bAnimate;
break;
case 'p':
case 'P':
// Press 'p' to export a PDF for the Praxinoscope.
bRecordingPDF = true;
break;
case 'f':
case 'F':
// Press 'f' to export .png Frames (to make an animated .GIF)
myFrameCount = 0;
exportFrameCount = 0;
bExportFrameImages = true;
bAnimate = true;
break;
}
}
//-------------------------------------------------------
void drawCutLines() {
fill(0);
textAlign(CENTER, BOTTOM);
text("Praxinoscope Template", 0, 0-diamCutOuter/2-6);
stroke(0);
strokeWeight(1.0);
noFill();
if (!bRecordingPDF) {
fill(255);
}
ellipse(0, 0, diamCutOuter, diamCutOuter);
noFill();
if (!bRecordingPDF) {
fill(240);
}
ellipse(0, 0, diamCutInner, diamCutInner);
noFill();
ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD);
line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
//-------------------------------------------------------
void drawGuides() {
// This function draws the guidelines.
// Don't draw these when we're exporting the PDF.
if (!bRecordingPDF) {
noFill();
stroke(128);
strokeWeight(0.2);
ellipse(0, 0, diamArtInner, diamArtInner);
ellipse(0, 0, diamArtOuter, diamArtOuter);
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float pxi = diamArtInner/2 * cos(angle);
float pyi = diamArtInner/2 * sin(angle);
float pxo = diamArtOuter/2 * cos(angle);
float pyo = diamArtOuter/2 * sin(angle);
stroke(128);
strokeWeight(0.2);
line (pxi, pyi, pxo, pyo);
}
// Draw the red wedge outline, highlighting the main view.
int redWedge = 7; // assuming nFrames = 10
for (int i=redWedge; i<=(redWedge+1); i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float pxi = diamArtInner/2 * cos(angle);
float pyi = diamArtInner/2 * sin(angle);
float pxo = diamArtOuter/2 * cos(angle);
float pyo = diamArtOuter/2 * sin(angle);
stroke(255, 0, 0);
strokeWeight(2.0);
line (pxi, pyi, pxo, pyo);
}
noFill();
stroke(255, 0, 0);
strokeWeight(2.0);
float startAngle = redWedge*TWO_PI/nFrames;
float endAngle = (redWedge+1)*TWO_PI/nFrames;
arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle);
arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle);
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
pushMatrix();
rotate(angle);
float originY = ((diamArtOuter + diamArtInner)/2)/2;
translate(0, 0-originY);
noFill();
stroke(128);
strokeWeight(0.2);
line (-inch/2, 0, inch/2, 0);
line (0, -inch/2, 0, inch/2);
popMatrix();
}
}
}
//-------------------------------------------------------
void drawAllFrames() {
for (int i=0; i<nFrames; i++) {
float angle = map(i, 0, nFrames, 0, TWO_PI);
float originY = ((diamArtOuter + diamArtInner)/2)/2;
pushMatrix();
rotate(angle);
translate(0, 0-originY);
scale(0.8, 0.8); // feel free to ditch this
int whichFrame = i;
if (bAnimate) {
whichFrame = (i+myFrameCount)%nFrames;
}
drawArtFrame (whichFrame);
popMatrix();
}
myFrameCount++;
}
//-------------------------------------------------------
void drawArtFrame (int whichFrame) {
// Draw the artwork for a generic frame of the Praxinoscope,
// given the framenumber (whichFrame) out of nFrames.
// NOTE #1: The "origin" for the frame is in the center of the wedge.
// NOTE #2: Remember that everything will appear upside-down!
strokeWeight(2);
pushMatrix();
scale(-1,-1);
shiny(whichFrame);
translate(0,cos(whichFrame*2/(TWO_PI)+PI/2)*10);
scale(1.0,1-.4*cos(whichFrame*2/TWO_PI-PI/2));
fill(200);
noStroke();
ellipse(0, 0, 50,50);
fill(0);
stroke(0);
ellipse(-10, 0, 5,10);
ellipse(10, 0, 5,cos(whichFrame*2/TWO_PI)*10);
arc(0, -10, 10, 10*2, PI*5/6, TWO_PI*14/12, CHORD);
popMatrix();
}
void shiny(int num) {
pushMatrix();
scale(cos(num*2/TWO_PI-PI/2));
line(20,15,20,35);
line(15,25,25,25);
scale(cos(num*2/TWO_PI+PI/2));
line(30,20,30,35);
line(25,27.5,35,27.5);
popMatrix();
}