xxxxxxxxxx
/*
For the Cards 🎴 #WCCChallenge
+-+-+-+-+-+-+-+-+-+
|H|o|l|l|e|r|i|t|h|
+-+-+-+-+-+-+-+-+-+
| |P|u|n|c|h|e|d| |
+-+-+-+-+-+-+-+-+-+
|C|a|r|d| |C|o|d|e|
+-+-+-+-+ +-+-+-+-+
This standard was approved as a Federal Information Processing Standard by the
Office of Management and Budget on June 16, 1971.
Details concerning the use of this standard within the Federal Government are
contained in FIPS PUB 14, HOLLERITH PUNCHED CARD CODE. For a complete
list of the publications available in the FEDERAL INFORMATION PROCESSING
STANDARDS Series, write to the Office of Technical Information and Publications,
National Bureau of Standards, Washington, D.C. 20234.
https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub14.pdf
IBM Type 29 punch card font:
https://scruss.com/blog/2017/03/21/keypunch029-for-all-your-punched-card-font-needs/
Sounds from https://opengameart.org/
Instructions:
=============
Type in your message and hit enter when the line is done. Sorry, punched cards didn't
backspace and neither does this!
The card image is automatically generated and downloaded, then a groovy op-art image based
on the holes will be generated. Right click to (optionally) save.
Pressing the Any-Key(tm) or clicking the left mouse button will load in a blank new card
to allow your immense joy continue.
**** Please don't fold, spindle or mutilate. ****
Another punched-card rabbit-hole:
https://repository.library.brown.edu/studio/item/bdr:788264/PDF/
*/
// For the State Machine
//-----------------------------------
const State = {
Start: 'Start',
Load: 'Load',
Key: 'Key',
Eject: 'Eject',
Groove: 'Groove',
};
// Global scope variables
//-----------------------------------
let codes = []; // ASCII to binary conversion array. Loaded by the codes file
let myChads = []; // Fun little bits of falling confetti
let myCard; // the card object
let UC; //font
let KP; //font
let eject; //sound
let click; //sound
let space; //sound
let roller; //sound
let timer;
let myState = State.Start;
let bg; //colour
// Preload Fonts and Sounds
//-----------------------------------
function preload() {
UC = loadFont("UniversCondensed.ttf");
KP = loadFont("Keypunch029.otf");
eject = loadSound('eject.wav');
space = loadSound('space.wav');
click = loadSound('click.wav');
roller = loadSound('roller.wav');
}
// Setup
//-----------------------------------
function setup() {
pixelDensity(1);
frameRate(30);
// aspect ratio 26x59 - the standard punched card ratio.
createCanvas(min(windowWidth, windowHeight * 59 / 26), min(windowHeight, windowWidth * 26 / 59));
bg = color(150);
//initialize the ASCII code lookup
for (let i = 0; i < 128; i++) {
codes[i] = -1;
}
fillCodes();
myCard = new Card(width - 200);
eject.playMode('sustain');
space.playMode('sustain');
click.playMode('sustain');
roller.playMode('restart');
background(bg);
}
// Main draw loop is a state machine
//-----------------------------------
function draw() {
let card_y = (height - 26 / 59 * (width - 200)) / 2; //based on the original Hollerith card 7 3/8 inches wide by 3 1/4 inches high
switch (myState) {
case State.Start: // State machine start
background(bg);
timer = 0;
myState = State.Load;
roller.play();
roller.setVolume(1, 0.5);
break;
case State.Load: // Card loading animation
if (timer < 100) {
background(bg);
myCard.display(easeOutCubic(timer, width, 100 - width, 100), card_y); // Easy-Peasy
timer++;
} else {
myState = State.Key;
roller.stop();
space.rate(3);
space.play();
timer = 0;
}
break;
case State.Key: // Allow keying in charactered. Handled by the keyReleased() function down below...
background(bg);
myCard.display(100, card_y);
showChads();
break;
case State.Eject: // Card eject animation
if (timer == 0) { //first time download the card image
background(bg);
myCard.display(100, card_y);
let datetime = nf([month(), day(), hour(), minute(), second()], 2);
save(myCard.pg, 'punchcard-' + year() + datetime.join("") + '.png');
eject.play(); //start sounds
roller.play();
timer++;
} else if (timer < 60) {
background(bg);
myCard.display(easeInCubic(timer, 100, -100 - width, 60), card_y);
showChads();
timer++;
} else {
myChads = []; //empty out the chad tray...
myState = State.Groove;
timer = 0;
roller.setVolume(0, 3);
}
break;
case State.Groove: // Bonus Art!
if (timer <= 100) {
myCard.groove(100, card_y);
timer++;
}
break;
}
}
// No Hanging Chads Here.....
//-----------------------------------
function showChads() {
for (let i = myChads.length - 1; i >= 0; i--) {
myChads[i].display();
myChads[i].move();
if (myChads[i].y > height + 10) {
myChads.splice(i, 1); // Chad had to leave.
}
}
}
// Handle all the keyboard stuff
//-----------------------------------
function keyReleased() {
if (myState == State.Key) {
let myKey = key.toUpperCase();
if (myKey.charCodeAt(0) > 128) { // Only filled in 128 character mappings
return false;
}
if (keyCode === ENTER) { // Handle the Enter key (Leave State.Key)
myState = State.Eject;
}
if ((myKey.length == 1) && codes[myKey.charCodeAt(0)] >= 0) { //No special keys allowed && must be in the keycodes map
myCard.type(myKey);
}
}
if (myState == State.Groove) { //Get back to the beginning.
myCard.reset();
myState = State.Start;
}
return false;
}
// cubic easing functions for animations
//---------------------------------------
function easeInCubic(t, b, c, d) {
return c * (t /= d) * t * t + b;
}
function easeOutCubic(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
// Handle the mouse stuff
//-----------------------------------
let lapse = 0; // mouse timer global to debounce
function mousePressed() {
if (millis() - lapse < 400) {
return (false);
}
lapse = millis();
if (mouseButton === RIGHT && (myState == State.Groove)) { // Allow saving only in groove mode!!
let datetime = nf([month(), day(), hour(), minute(), second()], 2);
save("img_" + year() + datetime.join("") + ".jpg");
} else if (mouseButton === CENTER) {
// nothing here
} else { // Left click
if (myState == State.Groove) { //Get back to the beginning.
myCard.reset();
myState = State.Start;
}
}
return (false)
}