sadasdaswre

This commit is contained in:
Nicky Case 2018-03-27 13:39:08 -04:00
parent 5975459f2c
commit ed9c3ff488
24 changed files with 1250 additions and 30 deletions

View File

@ -3,25 +3,33 @@ body{
font-family: "FuturaHandwritten";
font-size: 20px;
cursor: none !important;
}
#slideshow_container{
/* SIMULATION and SLIDESHOW */
#simulations_container, #slideshow_container{
position: absolute;
top:0; left:0;
width: 100%;
height: calc(100% - 60px);
}
#slideshow{
#simulations, #slideshow{
position: absolute;
width: 960px;
height: 540px;
margin: auto;
top:0; left:0; right:0; bottom:0;
background: #eee;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#slideshow div{
#simulations canvas, #slideshow div{
position: absolute;
}
/* NAVIGATION */
#navigation{
position: absolute;
bottom:0;
@ -30,6 +38,15 @@ body{
height:60px;
}
/* PENCIL */
#pencil{
position: absolute;
width:100px;
height:100px;
pointer-events: none;
}
/* THIS THING'S WORDS */
words, bonus, glossary{
display: none;
}
}

View File

@ -1,5 +1,13 @@
<!--
THE WISDOM AND/OR MADNESS OF CROWDS
by Nicky Case | apr 2018
MY "WHY" FOR MAKING THIS:
+ to build a foundation for further curiosity about networks, group dynamics
+ to practice teaching with problem-solving, a pre-req for skills
+ to instill transcendent sense of "fundamentally people", of Humanity's Brain <3
SLIDES:
Keep all words on index.html. This allows people to translate it to SAME repo!
/de.html, etc etc
@ -15,19 +23,47 @@ Cursor is allowed to flow EVERYWHERE though...
<link rel="stylesheet" type="text/css" href="index.css">
</head>
<body>
<div id="slideshow_container">
<div id="slideshow">
</div>
<!-- Simulation(s) in background -->
<div id="simulations_container">
<div id="simulations"></div>
</div>
<!-- Slideshow: words & buttons -->
<div id="slideshow_container">
<div id="slideshow"></div>
</div>
<!-- Navigation: Audio, Contents, Share, Translations -->
<div id="navigation">
</div>
<canvas id="pencil"></canvas>
</body>
</html>
<script src="js/helpers.js"></script>
<script src="js/Slideshow.js"></script>
<script src="chapters/0_Introduction.js"></script>
<!-- THE MAIN EXPLORABLE EXPLANATION -->
<script src="js/lib/helpers.js"></script>
<script src="js/lib/minpubsub.src.js"></script>
<script src="js/lib/Mouse.js"></script>
<script src="js/lib/Sprite.js"></script>
<script src="js/slideshow/Slideshow.js"></script>
<script src="js/slideshow/Pencil.js"></script>
<script src="js/sim/Peep.js"></script>
<script src="js/sim/Connection.js"></script>
<!--script src="js/sim/Drawing.js"></script>
<script src="js/sim/Game.js"></script-->
<script src="js/sim/Simulations.js"></script>
<script src="js/main.js"></script>
<script src="js/chapters/0_Introduction.js"></script>
<!-- - - - - - - - - - - - -->
<!-- THE SLIDESHOW'S WORDS -->
<!-- - - - - - - - - - - - -->
<!-- 0. Introduction -->
<words id="_0a">
@ -36,6 +72,7 @@ in <i>different</i> groups, can be kind, cruel, smart, stupid?
In this explorable explanation,
I'll show how the <i>network</i> of a group itself
can shape the people caught in its web.
<button onclick="slideshow.next()">NEXT</button>
</words>
<words id="_0b">
herp derp herp derp
@ -57,11 +94,17 @@ herp derp herp derp herp derp
<!-- 7. Credits -->
<!-- - - - - - - - - - - - - -->
<!-- BONUS BOXES (footnotes) -->
<!-- - - - - - - - - - - - - -->
<bonus>
</bonus>
<!-- GLOSSARY -->
<!-- - - - - - -->
<!-- GLOSSARY -->
<!-- - - - - - -->
<glossary>
</glossary>

View File

@ -2,8 +2,17 @@
SLIDES.push(
{
chapter: "1",
boxes: [
boxes:[
{words:"_0a", x:20, y:70, w:300, h:200}
],
sims:[
{
network: {
contagion: 0.25,
peeps: [ [100,100],[200,200] ],
connections: [ [0,1] ]
}
}
]
},
{

View File

@ -1,7 +0,0 @@
//////////////////
// HELPERS ///////
//////////////////
function $(query){
return document.querySelector(query);
}

64
slides/js/lib/Mouse.js Normal file
View File

@ -0,0 +1,64 @@
/////////////////////////////
// MOUSE ////////////////////
/////////////////////////////
var Mouse = {
x:0, y:0,
pressed:false
};
Mouse.ondown = function(){}; // add your own callback
Mouse.onmove = function(){}; // add your own callback
Mouse.onup = function(){}; // add your own callback
Mouse._ondown = function(event){
Mouse.pressed = true;
Mouse._onmove(event);
Mouse.ondown();
};
Mouse._onmove = function(event){
Mouse.x = event.clientX;
Mouse.y = event.clientY;
Mouse.onmove();
};
Mouse._onup = function(event){
Mouse.pressed = false;
Mouse.onup();
};
Mouse.update = function(){
// Just pressed, or just released (one frame ago)
Mouse.justPressed = (!Mouse.lastPressed && Mouse.pressed);
Mouse.justReleased = (Mouse.lastPressed && !Mouse.pressed);
// The last frame's stuff
Mouse.lastX = Mouse.x;
Mouse.lastY = Mouse.y;
Mouse.lastPressed = Mouse.pressed;
};
// TOUCH.
function _touchWrapper(callback){
return function(event){
var _event = {};
_event.clientX = event.changedTouches[0].clientX;
_event.clientY = event.changedTouches[0].clientY;
event.preventDefault();
callback(_event);
};
}
// INIT
Mouse.init = function(target){
// Regular mouse
target.addEventListener("mousedown", Mouse._ondown);
target.addEventListener("mousemove", Mouse._onmove);
window.addEventListener("mouseup", Mouse._onup);
// Touch events
target.addEventListener("touchstart", _touchWrapper(Mouse._ondown), false);
target.addEventListener("touchmove", _touchWrapper(Mouse._onmove), false);
document.body.addEventListener("touchend", function(){
Mouse._onup();
}, false);
};

46
slides/js/lib/Sprite.js Normal file
View File

@ -0,0 +1,46 @@
function Sprite(config){
var self = this;
self.config = config;
// Properties...
self.x = 0;
self.y = 0;
self.pivotX = 0;
self.pivotY = 0;
self.scale = 1;
self.rotation = 0; // radians
// The image!
self.image = new Image();
self.image.src = config.src;
// Frames
self.currentFrame = 0;
self.totalFrames = config.frames;
self.nextFrame = function(){
self.currentFrame = (self.currentFrame+1)%self.totalFrames;
};
self.gotoFrame = function(frame){
self.currentFrame = frame;
};
// Draw
self.draw = function(ctx){
var sw = config.sw;
var sh = config.sh;
var sx = self.currentFrame*sw;
var sy = 0;
ctx.save();
ctx.translate(self.x, self.y);
ctx.scale(self.scale, self.scale);
ctx.rotate(self.rotation);
ctx.translate(-self.pivotX, -self.pivotY);
ctx.drawImage(self.image, sx, sy, sw, sh, 0, 0, sw, sh);
ctx.restore();
};
}

38
slides/js/lib/helpers.js Normal file
View File

@ -0,0 +1,38 @@
//////////////////
// HELPERS ///////
//////////////////
Math.TAU = 6.2831853072;
// The poor man's jQuery
function $(query){
return document.querySelector(query);
}
function $all(query){
return document.querySelectorAll(query);
}
// Wide Sigmoid
function sigmoid(x){
return x / (1 + Math.abs(x));
}
// Create retina canvas
function createCanvas(canvas, width, height){
// The "canvas" arg not provided? make a new one!
if(arguments.length==2){
width = arguments[0];
height = arguments[1];
canvas = document.createElement("canvas");
}
// Set difference in width & height
canvas.width = width*2;
canvas.height = height*2;
canvas.style.width = width;
canvas.style.height = height;
return canvas;
}

View File

@ -0,0 +1,95 @@
/*!
* MinPubSub
* Copyright(c) 2011 Daniel Lamb <daniellmb.com>
* MIT Licensed
*/
(function (context) {
var MinPubSub = {};
// the topic/subscription hash
var cache = context.c_ || {}; //check for 'c_' cache for unit testing
MinPubSub.publish = function ( /* String */ topic, /* Array? */ args) {
// summary:
// Publish some data on a named topic.
// topic: String
// The channel to publish on
// args: Array?
// The data to publish. Each array item is converted into an ordered
// arguments on the subscribed functions.
//
// example:
// Publish stuff on '/some/topic'. Anything subscribed will be called
// with a function signature like: function(a,b,c){ ... }
//
// publish('/some/topic', ['a','b','c']);
var subs = cache[topic],
len = subs ? subs.length : 0;
//can change loop or reverse array if the order matters
while (len--) {
subs[len].apply(context, args || []);
}
};
MinPubSub.subscribe = function ( /* String */ topic, /* Function */ callback) {
// summary:
// Register a callback on a named topic.
// topic: String
// The channel to subscribe to
// callback: Function
// The handler event. Anytime something is publish'ed on a
// subscribed channel, the callback will be called with the
// published array as ordered arguments.
//
// returns: Array
// A handle which can be used to unsubscribe this particular subscription.
//
// example:
// subscribe('/some/topic', function(a, b, c){ /* handle data */ });
if (!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback]; // Array
};
MinPubSub.unsubscribe = function ( /* Array */ handle, /* Function? */ callback) {
// summary:
// Disconnect a subscribed function for a topic.
// handle: Array
// The return value from a subscribe call.
// example:
// var handle = subscribe('/some/topic', function(){});
// unsubscribe(handle);
var subs = cache[callback ? handle : handle[0]],
callback = callback || handle[1],
len = subs ? subs.length : 0;
while (len--) {
if (subs[len] === callback) {
subs.splice(len, 1);
}
}
};
// UMD definition to allow for CommonJS, AMD and legacy window
if (typeof module === 'object' && module.exports) {
// CommonJS, just export
module.exports = exports = MinPubSub;
} else if (typeof define === 'function' && define.amd) {
// AMD support
define(function () {
return MinPubSub;
});
} else if (typeof context === 'object') {
// If no AMD and we are in the browser, attach to window
context.publish = MinPubSub.publish;
context.subscribe = MinPubSub.subscribe;
context.unsubscribe = MinPubSub.unsubscribe;
}
})(this.window);

23
slides/js/main.js Normal file
View File

@ -0,0 +1,23 @@
window.onload = function(){
// Setting up the main stuff
window.simulations = new Simulations();
window.slideshow = new Slideshow();
window.pencil = new Pencil();
// Initializing the Mouse
Mouse.init(document.body);
// Animation loop IS update loop, whatever
function update(){
simulations.update();
slideshow.update();
pencil.update();
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
// First slide!
slideshow.goto(0);
}

View File

@ -0,0 +1,75 @@
function Connection(config){
var self = this;
// Properties
self.from = config.from;
self.to = config.to;
self.uncuttable = config.uncuttable || false;
self.sim = config.sim;
// Update
self.update = function(){};
// Draw
self.draw = function(ctx){
ctx.strokeStyle = "#444";
ctx.lineWidth = self.uncuttable ? 6 : 2; // thick=uncuttable
ctx.beginPath();
ctx.moveTo(self.from.x, self.from.y);
ctx.lineTo(self.to.x, self.to.y);
ctx.stroke();
};
// Hit Test with a LINE SEGMENT
// code adapted from https://gist.github.com/Joncom/e8e8d18ebe7fe55c3894
self.hitTest = function(line){
var p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y;
p0_x = line[0];
p0_y = line[1];
p1_x = line[2];
p1_y = line[3];
p2_x = self.from.x;
p2_y = self.from.y;
p3_x = self.to.x;
p3_y = self.to.y;
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x;
s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x;
s2_y = p3_y - p2_y;
var s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
};
}
/*
Connection.getConnected = function(peep){
var results = [];
for(var i=0; i<connections.length; i++){ // in either direction
var c = connections[i];
if(c.from==peep) results.push(c.to);
if(c.to==peep) results.push(c.from);
}
return results;
}
function removeAllConnectedTo(peep){
for(var i=connections.length-1; i>=0; i--){ // backwards index coz we're deleting
var c = connections[i];
if(c.from==peep || c.to==peep){ // in either direction
connections.splice(i,1); // remove!
}
}
}
function _makeUncuttable(arrayOfConnections){
for(var i=0; i<arrayOfConnections.length; i++){
arrayOfConnections[i].push(true);
}
}
*/

75
slides/js/sim/Drawing.js Normal file
View File

@ -0,0 +1,75 @@
function Drawing(){
var self = this;
// Update!
self.update = function(){
// Connection
if(self.connectFrom){
// Over any peeps? Connect to THAT! Else, connect to Mouse
var peepHovered = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
if(peepHovered==self.connectFrom) peepHovered=null; // if same, nah
self.connectTo = peepHovered ? peepHovered : Mouse;
}
// Erase
if(self.isErasing){
self.eraseTrail.unshift([Mouse.x,Mouse.y]); // add to start
if(self.eraseTrail.length>10){
self.eraseTrail.pop(); // remove from end
}
}else{
self.eraseTrail.pop(); // remove from end
}
};
// Connection!
self.connectFrom = null;
self.connectTo = null;
self.startConnect = function(from){
self.connectFrom = from;
};
self.endConnect = function(){
self.connectFrom = null;
};
// Erase!
self.isErasing = false;
self.eraseTrail = [];
self.startErase = function(){
self.isErasing = true;
};
self.endErase = function(){
self.isErasing = false;
};
// Draw
self.draw = function(ctx){
// Connecting...
if(self.connectFrom){
ctx.strokeStyle = "#ccc";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(self.connectFrom.x, self.connectFrom.y);
ctx.lineTo(self.connectTo.x, self.connectTo.y);
ctx.stroke();
}
// Erase
if(self.eraseTrail.length>0){
ctx.strokeStyle = "#dd4040";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(self.eraseTrail[0][0], self.eraseTrail[0][1]);
for(var i=1; i<self.eraseTrail.length; i++){
ctx.lineTo(self.eraseTrail[i][0], self.eraseTrail[i][1]);
}
ctx.stroke();
}
};
}

287
slides/js/sim/Game.js Normal file
View File

@ -0,0 +1,287 @@
Math.TAU = Math.PI*2;
var canvas = document.getElementById("canvas");// || document.createElement("canvas");
canvas.style.cursor = "none";
var ctx = canvas.getContext('2d');
var peeps = [];
var connections = [];
var drawing = new Drawing();
var cursor = new Cursor();
var winnerImage = new Image();
winnerImage.src = "img/winner.png";
var CONTAGION_THRESHOLD = 0;
var CONTAGION_THRESHOLD_2 = 0;
var clearNetwork = function(){
peeps = [];
connections = [];
};
var loadNetwork = function(data){
// Clear!
clearNetwork();
// Peeps
data.peeps.forEach(function(p){
addPeep(p[0], p[1], p[2]);
});
// Connections
data.connections.forEach(function(c){
var from = peeps[c[0]];
var to = peeps[c[1]];
var uncuttable = c[2];
addConnection(from, to, uncuttable);
});
// Contagion threshold?
if(data.contagion !== undefined){
CONTAGION_THRESHOLD = data.contagion;
}else{
CONTAGION_THRESHOLD = 0;
}
if(data.contagion2 !== undefined){
CONTAGION_THRESHOLD_2 = data.contagion2;
}else{
CONTAGION_THRESHOLD_2 = 0;
}
}
var saveNetwork = function(){
var data = {
peeps: [],
connections: [],
contagion: CONTAGION_THRESHOLD,
contagion2: CONTAGION_THRESHOLD_2,
};
peeps.forEach(function(peep){
data.peeps.push([peep.x, peep.y, peep.state]);
});
connections.forEach(function(c){
var fromIndex = peeps.indexOf(c.from);
var toIndex = peeps.indexOf(c.to);
data.connections.push([fromIndex, toIndex, c.uncuttable]);
});
return data;
}
var DRAW_STATE = 0; // 0-nothing | 1-connecting | 2-erasing
var DRAW_CONNECT_FROM = null;
var CONNECT_FROM_BUFFER = 15;//25;
var CONNECT_TO_BUFFER = 25;
var YOU_ARE_WINNER = false;
function update(){
// Mouse logic...
if(SIM_IS_RUNNING){
DRAW_STATE = 0; // back to normal
Mouse.update();
}else{
if(Mouse.justPressed && DRAW_STATE===0){
// Clicked on a peep?
var peepClicked = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
if(peepClicked){
DRAW_CONNECT_FROM = peepClicked;
DRAW_STATE = 1; // START CONNECTING
drawing.startConnect(peepClicked); // Drawing logic
}else{
DRAW_STATE = 2; // START ERASING
}
}
if(DRAW_STATE==2){ // ERASE
// Intersect with any CUTTABLE connections?
var line = [Mouse.lastX, Mouse.lastY, Mouse.x, Mouse.y];
for(var i=connections.length-1; i>=0; i--){ // going BACKWARDS coz killing
var c = connections[i];
if(c.uncuttable) continue; // don't touch the UNCUTTABLES
if(c.hitTest(line)) connections.splice(i,1);
}
drawing.startErase(); // Drawing logic
}
if(Mouse.justReleased && DRAW_STATE!==0){
// Connecting peeps, and released on a peep?
if(DRAW_STATE==1){
var peepReleased = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
if(peepReleased){ // connect 'em!
addConnection(DRAW_CONNECT_FROM, peepReleased);
DRAW_CONNECT_FROM = null;
}
drawing.endConnect(); // Drawing logic
}else if(DRAW_STATE==2){
drawing.endErase(); // Drawing logic
}
DRAW_STATE = 0; // back to normal
}
Mouse.update();
// Cursor Logic
if(DRAW_STATE==0){
var peepHovered = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
if(peepHovered){
cursor.setMode(Cursor.CONNECT);
}else{
cursor.setMode(Cursor.NORMAL);
}
}
if(DRAW_STATE==1){
cursor.setMode(Cursor.CONNECT);
}
if(DRAW_STATE==2){
cursor.setMode(Cursor.ERASE);
}
}
// Update Logic
connections.forEach(function(connection){
connection.update(ctx);
});
drawing.update();
peeps.forEach(function(peep){
peep.update();
});
cursor.update();
// Draw Logic
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = SIM_IS_RUNNING ? "#eee" : "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.scale(2,2);
_preUpdate();
//ctx.translate(0,100);
connections.forEach(function(connection){
connection.draw(ctx);
});
drawing.draw(ctx);
peeps.forEach(function(peep){
peep.draw(ctx);
});
cursor.draw(ctx);
_onUpdate();
if(YOU_ARE_WINNER){
ctx.drawImage(winnerImage, 0, 0, 500, 500);
}
ctx.restore();
// RAF
requestAnimationFrame(update);
}
function _preUpdate(){
// TO IMPLEMENT
}
function _onUpdate(){
// TO IMPLEMENT
}
///////////////////////////////////////
// CONTAGION UI, WHY NOT HMMMM ////////
///////////////////////////////////////
function $(query){
return document.querySelector(query);
}
function showContagionUI(){
// Just display the div
$("#sim_ui").style.display = "block";
_updateSimRunningUI();
}
var SIM_IS_RUNNING = false;
var SIM_STEP = 0;
var _updateSimRunningUI = function(){
if(SIM_IS_RUNNING){
$("#sim_is_not_running").style.display = "none";
$("#sim_is_running").style.display = "inline";
//document.body.style.background = "#777";
$("#sim_step").innerHTML = SIM_STEP;
}else{
$("#sim_is_not_running").style.display = "inline";
$("#sim_is_running").style.display = "none";
//document.body.style.background = "";
}
};
var _networkBeforeSimulationStarted = null;
function _startSim(){
SIM_STEP = 0;
SIM_IS_RUNNING = true;
_networkBeforeSimulationStarted = saveNetwork();
_updateSimRunningUI();
_startSimulation();
};
$("#sim_start").onclick = _startSim;
function _stopSim(){
SIM_IS_RUNNING = false;
_resetToBeforeSimStarted();
_updateSimRunningUI();
_stopSimulation();
};
$("#sim_stop").onclick = _stopSim;
function _simNext(){
SIM_STEP++;
_updateSimRunningUI();
_stepSimulation();
};
$("#sim_next").onclick = _simNext;
function _resetToBeforeSimStarted(){
loadNetwork(_networkBeforeSimulationStarted);
}
function _startSimulation(){
// To Implement
}
function _stopSimulation(){
// To Implement
}
function _stepSimulation(){
_infectPeople();
}
function _infectPeople(){
// Consider all peeps, and their friends
var toInfect = [];
peeps.forEach(function(peep){
// How many infected friends?
if(peep.numFriends==0) return; // No friends? NVM.
var ratioOfInfectedFriends = peep.numInfectedFriends/peep.numFriends;
// Passed threshold?
if(CONTAGION_THRESHOLD==0){ // simple contagion, just ANY friend
if(peep.numInfectedFriends>0) toInfect.push(peep);
}else{
// greater OR EQUALS (fuzz coz floating point)
if(ratioOfInfectedFriends>=CONTAGION_THRESHOLD-0.0001){
toInfect.push(peep);
}
}
});
// "Infect" the peeps who need to get infected
toInfect.forEach(function(peep){
peep.infect();
});
}

272
slides/js/sim/Peep.js Normal file
View File

@ -0,0 +1,272 @@
var PEEP_STATE_COLORS = {
1: "#ccc",
2: "#dd4040"
};
var _hack_SHOW_BOTH_STATES = false;
var _hack_HIDE_BARS = false;
var _hack_REINTEGRATION_PUZZLE = false;
var testingImage = new Image();
testingImage.src = "img/testing.png";
function Peep(config){
var self = this;
// Properties
self.x = config.x;
self.y = config.y;
self.state = config.state;
// Update:
self.numFriends = 0;
self.numInfectedFriends = 0;
self.faceX = 0;
self.faceY = 0;
self.faceBlink = 0;
self.isMajority = false;
var _faceFollow = 0.75+(Math.random()*0.1);
self.update = function(){
// Face position!
var faceVector = {
x: (Mouse.x-self.x)/5,
y: (Mouse.y-self.y)/5
};
faceVector.mag = Math.sqrt(faceVector.x*faceVector.x + faceVector.y*faceVector.y);
var max_distance = 5;
if(faceVector.mag>max_distance){
faceVector.x = faceVector.x * (max_distance/faceVector.mag);
faceVector.y = faceVector.y * (max_distance/faceVector.mag);
}
self.faceX = self.faceX*_faceFollow + faceVector.x*(1-_faceFollow);
self.faceY = self.faceY*_faceFollow + faceVector.y*(1-_faceFollow);
// Blink?
if(!self.faceBlink){
if(Math.random()<0.002) self.faceBlink=true;
}else{
if(Math.random()<0.09) self.faceBlink=false;
}
// Friends connected... or infected
var friends = getConnected(self);
self.numFriends = friends.length;
self.numInfectedFriends = 0;
friends.forEach(function(friend){
if(friend.state==2) self.numInfectedFriends++;
});
};
// Draw
var radius = 20;
var barWidth = 30;
var barHeight = 10;
self.draw = function(ctx){
ctx.save();
ctx.translate(self.x, self.y);
// Circle
//ctx.fillStyle = (self.state==1) ? "#ccc" : "#dd4040"; //"#ffdf00";
var myColor = PEEP_STATE_COLORS[self.state];
ctx.fillStyle = myColor;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.TAU, false);
ctx.fill();
// INFECT ON NEXT TURN?
/*var infectOnNextTurn = (self.numFriends>0 && self.numInfectedFriends/self.numFriends>=CONTAGION_THRESHOLD);
if(infectOnNextTurn){
ctx.strokeStyle = PEEP_STATE_COLORS[2];
ctx.lineWidth = 2;
ctx.stroke();
}*/
// Face
ctx.save();
ctx.translate(self.faceX, self.faceY);
ctx.fillStyle = "rgba(0,0,0,0.5)";
if(self.faceBlink){
ctx.beginPath();
ctx.rect(-14, -1, 8, 2);
ctx.fill();
ctx.beginPath();
ctx.rect(6, -1, 8, 2);
ctx.fill();
}else{
ctx.beginPath();
ctx.arc(-10, -1, 3, 0, Math.TAU, false);
ctx.fill();
ctx.beginPath();
ctx.arc(10, -1, 3, 0, Math.TAU, false);
ctx.fill();
}
ctx.beginPath();
ctx.rect(-7, 4, 14, 2);
ctx.fill();
ctx.restore();
//////////////////////////////////////////////////////////
// LABEL FOR INFECTED/FRIENDS, BAR, AND CONTAGION LEVEL //
//////////////////////////////////////////////////////////
if(!_hack_HIDE_BARS && !self._hack_TESTED){
ctx.save();
// Say: Infected/Friends
ctx.translate(0,-42);
var labelNum = self.numInfectedFriends+"/"+self.numFriends;
var labelPercent = "";
if(self.numFriends>0){
labelPercent = Math.round(100*(self.numInfectedFriends/self.numFriends)) + "%";
}
ctx.font = '12px sans-serif';
ctx.fillStyle = myColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fontWeight = "bold";
ctx.fillText(labelNum, 0, 0);
// A nice bar
ctx.translate(0,12);
ctx.lineWidth = 1;
// the white fill
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight);
ctx.fill();
// The color fills
if(self.numFriends>0){
if(!_hack_SHOW_BOTH_STATES){
ctx.fillStyle = PEEP_STATE_COLORS[2]; // state = 2 infected
ctx.beginPath();
ctx.rect(-barWidth/2, -barHeight/2, barWidth*(self.numInfectedFriends/self.numFriends), barHeight);
ctx.fill();
}else{
var ratio = self.numInfectedFriends/self.numFriends;
if((!_hack_REINTEGRATION_PUZZLE && self.state==1)
|| (_hack_REINTEGRATION_PUZZLE && self.state==2)){
var w = barWidth*(1-ratio);
ctx.fillStyle = PEEP_STATE_COLORS[1];
ctx.beginPath();
ctx.rect(-barWidth/2, -barHeight/2, w, barHeight);
ctx.fill();
ctx.fillStyle = PEEP_STATE_COLORS[2];
ctx.beginPath();
ctx.rect(-barWidth/2+w, -barHeight/2, barWidth*(ratio), barHeight);
ctx.fill();
}else{
var w = barWidth*(ratio);
ctx.fillStyle = PEEP_STATE_COLORS[2];
ctx.beginPath();
ctx.rect(-barWidth/2, -barHeight/2, w, barHeight);
ctx.fill();
ctx.fillStyle = PEEP_STATE_COLORS[1];
ctx.beginPath();
ctx.rect(-barWidth/2+w, -barHeight/2, barWidth*(1-ratio), barHeight);
ctx.fill();
}
}
}
// The outline
ctx.strokeStyle = myColor;
ctx.beginPath();
if(self.numFriends>0){
ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight);
}else{
ctx.rect(-barWidth/2, 0, barWidth, 0);
}
ctx.stroke();
// a pointer for contagion level
ctx.translate(0, barHeight/2+2);
if(!_hack_REINTEGRATION_PUZZLE){
if(CONTAGION_THRESHOLD && CONTAGION_THRESHOLD>0){
self._drawThreshold(ctx, CONTAGION_THRESHOLD);
}
if(CONTAGION_THRESHOLD_2 && CONTAGION_THRESHOLD_2>0){
self._drawThreshold(ctx, CONTAGION_THRESHOLD_2);
}
}else{
if(self.state==1){
self._drawThreshold(ctx, 1/3);
}else{
self._drawThreshold(ctx, 2/3);
}
}
// Percent
ctx.font = '8px sans-serif';
ctx.fillStyle = "rgba(0,0,0,0.8)";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fontWeight = "bold";
ctx.fillText(labelPercent, 0, -6);
ctx.restore();
}
// AM I BEING TESTED
if(self._hack_TESTED){
ctx.drawImage(testingImage, -30, -85, 60, 60);
}
ctx.restore();
};
self._drawThreshold = function(ctx, threshold){
ctx.save();
ctx.translate(barWidth*threshold - barWidth/2, 0);
ctx.strokeStyle = "#000"; //PEEP_STATE_COLORS[2];
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(0,-14);
ctx.stroke();
ctx.restore();
}
// Hit Test
self.hitTest = function(x,y,buffer){
if(buffer===undefined) buffer=0;
var dx = self.x-x;
var dy = self.y-y;
var dist2 = dx*dx+dy*dy;
var r = radius+buffer;
return (dist2<r*r);
};
// Infect
self.infect = function(){
self.state = 2;
}
}
function _mouseOverPeep(buffer){
var result;
peeps.forEach(function(peep){
if(peep.hitTest(Mouse.x, Mouse.y, buffer)) result=peep;
});
return result;
}
function addPeep(x,y, state){
var peep = new Peep({
x:x, y:y,
state: state ? state : 1
});
peeps.push(peep);
}
function removePeep(peep){
removeAllConnectedTo(peep); // remove connections first
peeps.splice(peeps.indexOf(peep),1); // BYE peep
}

View File

@ -0,0 +1,104 @@
/******************************
An interactive game in the BACKGROUND of the Slideshow...
(if fullscreen, origin is top-left of slideshow)
(if not, allow MULTIPLE canvasses & games.)
******************************/
function Simulations(){
var self = this;
self.dom = $("#simulations");
self.sims = [];
self.clear = function(){
self.sims.forEach(function(sim){
self.dom.removeChild(sim.canvas);
sim.kill();
});
};
self.showSims = function(simConfigs){
self.clear();
simConfigs.forEach(function(simConfig){
var sim = new Sim(simConfig);
self.dom.appendChild(sim.canvas);
self.sims.push(sim);
});
};
}
function Sim(config){
var self = this;
self.config = config;
self.networkConfig = config.network;
// Canvas
self.canvas = createCanvas(500, 500);
self.ctx = self.canvas.getContext('2d');
// Networks... clear/init
self.clear = function(){
self.peeps = [];
self.connections = [];
self.contagion = 0;
};
self.init = function(){
// Clear!
self.clear();
// Peeps
self.networkConfig.peeps.forEach(function(p){
var x = p[0],
y = p[1],
infected = p[2];
self.addPeep(x, y, infected);
});
// Connections
self.networkConfig.connections.forEach(function(c){
var from = peeps[c[0]],
to = peeps[c[1]],
uncuttable = c[2];
self.addConnection(from, to, uncuttable);
});
};
// Add Peeps/Connections
self.addPeep = function(x, y, infected){
var peep = new Peep({ x:x, y:y, infected:infected, sim:self });
self.peeps.push(peep);
return peep;
};
self.addConnection = function(from, to, uncuttable){
// Don't allow connecting to self...
if(from==to) return;
// ...or if already exists, in either direction
for(var i=0; i<self.connections.length; i++){
var c = self.connections[i];
if(c.from==from && c.to==to) return;
if(c.from==to && c.to==from) return;
}
// Otherwise, go ahead and add it!
var connection = new Connection({ from:from, to:to, uncuttable:uncuttable, sim:self });
self.connections.push(connection);
return connection;
};
// Update
self.update = function(){
};
// INIT NOW
self.init();
}

View File

@ -0,0 +1,77 @@
/******************************************
THE PENCIL
it's purely visual. replace the cursor
draw / erase / click
******************************************/
function Pencil(){
var self = this;
self.canvas = createCanvas( $("#pencil"), 100, 100 );
self.ctx = self.canvas.getContext('2d');
// Sprite
self.sprite = new Sprite({
src: "sprites/pencil.png",
frames:3, sw:200, sh:200,
});
self.sprite.pivotX = 0;
self.sprite.pivotY = 200;
self.sprite.scale = 0.75;
var _size = 100;
var _margin = 10;
var _offset = 10;
self.colors = [
"#ccc",
"#000",
"#ff5555"
];
// Update
self.update = function(){
// Pencil's rotation
if(isNaN(self.x)) self.x=0;
if(isNaN(self.y)) self.y=0;
var xy_velocity = ((Mouse.x-self.x) + (Mouse.y-self.y))/10; // in down-right direction
var gotoRotation = -sigmoid(xy_velocity) * Math.TAU/8;
self.sprite.rotation = self.sprite.rotation*0.8 + gotoRotation*0.2;
// Pencil's offset
var gotoOffset = Mouse.pressed ? -8 : 10;
_offset = _offset*0.5 + gotoOffset*0.5;
// Update position
self.x = Mouse.x;
self.y = Mouse.y;
// Move DOM there
self.canvas.style.left = self.x-_margin;
self.canvas.style.top = self.y-_size+_margin;
// Reset canvas
var ctx = self.ctx;
ctx.clearRect(0,0,self.canvas.width,self.canvas.height);
ctx.save();
ctx.translate(_margin*2, 200-_margin*2);
// Draw pencil's dot
if(!Mouse.pressed){
ctx.fillStyle = self.colors[self.sprite.currentFrame];
ctx.beginPath();
ctx.arc(0, 0, 5, 0, Math.TAU);
ctx.fill();
}
// Draw pencil
self.sprite.x = _offset;
self.sprite.y = -_offset;
self.sprite.draw(ctx);
ctx.restore();
};
}

View File

@ -1,9 +1,12 @@
//////////////////////////
// THE SLIDESHOW, YO /////
//////////////////////////
/******************************************
THE SLIDESHOW
- background: fullscreen iframe (so can draw everywhere)
- foreground: words & pictures
******************************************/
var SLIDES = [];
var slideshow = new Slideshow();
function Slideshow(){
@ -22,6 +25,10 @@ function Slideshow(){
// Clear DOM
self.clear();
// Show simulations
slide.sims = slide.sims || [];
simulations.showSims(slide.sims);
// Add boxes
slide.boxes = slide.boxes || [];
slide.boxes.forEach(function(box){
@ -52,8 +59,3 @@ function Slideshow(){
};
}
// On load, set slideshow to first slide
window.addEventListener("load", function(){
slideshow.goto(0);
});

BIN
slides/sprites/pencil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB